Page MenuHomePhorge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/dnssd/avahi-publicservice.cpp b/dnssd/avahi-publicservice.cpp
index 1e012424b3..788ed89d5b 100644
--- a/dnssd/avahi-publicservice.cpp
+++ b/dnssd/avahi-publicservice.cpp
@@ -1,263 +1,263 @@
/* This file is part of the KDE project
*
* Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl>
*
* 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 "avahi-publicservice_p.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStringList>
#include "publicservice.h"
#include <config-dnssd.h>
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include "servicebrowser.h"
#include "settings.h"
#include "avahi_server_interface.h"
#include "avahi_entrygroup_interface.h"
Q_DECLARE_METATYPE(QList<QByteArray>)
namespace DNSSD
{
PublicService::PublicService(const QString& name, const QString& type, unsigned int port,
const QString& domain, const QStringList& subtypes)
: QObject(), ServiceBase(new PublicServicePrivate(this, name, type, domain, port))
{
K_D;
if (domain.isNull()) d->m_domain="local.";
d->m_subtypes=subtypes;
}
PublicService::~PublicService()
{
stop();
}
void PublicServicePrivate::tryApply()
{
if (fillEntryGroup()) commit();
else {
m_parent->stop();
emit m_parent->published(false);
}
}
void PublicService::setServiceName(const QString& serviceName)
{
K_D;
d->m_serviceName = serviceName;
if (d->m_running) {
d->m_group->Reset();
d->tryApply();
}
}
void PublicService::setDomain(const QString& domain)
{
K_D;
d->m_domain = domain;
if (d->m_running) {
d->m_group->Reset();
d->tryApply();
}
}
void PublicService::setType(const QString& type)
{
K_D;
d->m_type = type;
if (d->m_running) {
d->m_group->Reset();
d->tryApply();
}
}
void PublicService::setSubTypes(const QStringList& subtypes)
{
K_D;
d->m_subtypes = subtypes;
if (d->m_running) {
d->m_group->Reset();
d->tryApply();
}
}
QStringList PublicService::subtypes() const
{
K_D;
return d->m_subtypes;
}
void PublicService::setPort(unsigned short port)
{
K_D;
d->m_port = port;
if (d->m_running) {
d->m_group->Reset();
d->tryApply();
}
}
void PublicService::setTextData(const QMap<QString,QByteArray>& textData)
{
K_D;
d->m_textData = textData;
if (d->m_running) {
d->m_group->Reset();
d->tryApply();
}
}
bool PublicService::isPublished() const
{
K_D;
return d->m_published;
}
bool PublicService::publish()
{
K_D;
publishAsync();
while (d->m_running && !d->m_published) QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return d->m_published;
}
void PublicService::stop()
{
K_D;
if (d->m_group) d->m_group->Reset();
d->m_running = false;
d->m_published = false;
}
bool PublicServicePrivate::fillEntryGroup()
{
registerTypes();
if (!m_group) {
QDBusReply<QDBusObjectPath> rep=m_server->EntryGroupNew();
if (!rep.isValid()) return false;
m_group=new org::freedesktop::Avahi::EntryGroup("org.freedesktop.Avahi",rep.value().path(), QDBusConnection::systemBus());
connect(m_group,SIGNAL(StateChanged(int,QString)), this, SLOT(groupStateChanged(int,QString)));
}
if (m_serviceName.isNull()) {
QDBusReply<QString> rep=m_server->GetHostName();
if (!rep.isValid()) return false;
m_serviceName=rep.value();
}
QList<QByteArray> txt;
QMap<QString,QByteArray>::ConstIterator itEnd = m_textData.constEnd();
for (QMap<QString,QByteArray>::ConstIterator it = m_textData.constBegin(); it!=itEnd ; ++it)
- if (it.value().isNull()) txt.append(it.key().toAscii());
- else txt.append(it.key().toAscii()+'='+it.value());
+ if (it.value().isNull()) txt.append(it.key().toLatin1());
+ else txt.append(it.key().toLatin1()+'='+it.value());
for (;;) {
QDBusReply<void> ret = m_group->AddService(-1,-1, 0, m_serviceName, m_type , domainToDNS(m_domain) ,
m_hostName, m_port,txt);
if (ret.isValid()) break;
// serious error, bail out
if (ret.error().name()!=QLatin1String("org.freedesktop.Avahi.CollisionError")) return false;
// name collision, try another
QDBusReply<QString> rep=m_server->GetAlternativeServiceName(m_serviceName);
if (rep.isValid()) m_serviceName = rep.value();
else return false;
}
Q_FOREACH(const QString &subtype, m_subtypes)
m_group->AddServiceSubtype(-1,-1, 0, m_serviceName, m_type, domainToDNS(m_domain) , subtype);
return true;
}
void PublicServicePrivate::serverStateChanged(int s,const QString&)
{
if (!m_running) return;
switch (s) {
case AVAHI_SERVER_INVALID:
m_parent->stop();
emit m_parent->published(false);
break;
case AVAHI_SERVER_REGISTERING:
case AVAHI_SERVER_COLLISION:
if (m_group) m_group->Reset();
m_collision=true;
break;
case AVAHI_SERVER_RUNNING:
if (m_collision) {
m_collision=false;
tryApply();
}
}
}
void PublicService::publishAsync()
{
K_D;
if (d->m_running) stop();
if (!d->m_server) {
d->m_server = new org::freedesktop::Avahi::Server("org.freedesktop.Avahi","/",QDBusConnection::systemBus());
connect(d->m_server,SIGNAL(StateChanged(int,QString)),d,SLOT(serverStateChanged(int,QString)));
}
int state=AVAHI_SERVER_INVALID;
QDBusReply<int> rep=d->m_server->GetState();
if (rep.isValid()) state=rep.value();
d->m_running=true;
d->m_collision=true; // make it look like server is getting out of collision to force registering
d->serverStateChanged(state, QString());
}
void PublicServicePrivate::groupStateChanged(int s, const QString& reason)
{
switch (s) {
case AVAHI_ENTRY_GROUP_COLLISION: {
QDBusReply<QString> rep=m_server->GetAlternativeServiceName(m_serviceName);
if (rep.isValid()) m_parent->setServiceName(rep.value());
else serverStateChanged(AVAHI_SERVER_INVALID, reason);
break;
}
case AVAHI_ENTRY_GROUP_ESTABLISHED:
m_published=true;
emit m_parent->published(true);
break;
case AVAHI_ENTRY_GROUP_FAILURE:
serverStateChanged(AVAHI_SERVER_INVALID, reason);
default:
break;
}
}
void PublicService::virtual_hook(int, void*)
{
}
}
#include "moc_publicservice.cpp"
#include "moc_avahi-publicservice_p.cpp"
diff --git a/dnssd/avahi_server_interface.cpp b/dnssd/avahi_server_interface.cpp
index 094fde4d99..d24c414673 100644
--- a/dnssd/avahi_server_interface.cpp
+++ b/dnssd/avahi_server_interface.cpp
@@ -1,54 +1,54 @@
/*
* This file was generated by dbusxml2cpp version 0.6
* Command line was: dbusxml2cpp -m -p avahi_server_interface /home/qba/src/kdelibs/dnssd/org.freedesktop.Avahi.Server.xml
*
* dbusxml2cpp is Copyright (C) 2006 Trolltech ASA. All rights reserved.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#include "avahi_server_interface.h"
#include "servicebase.h"
#include <QtCore/QUrl>
Q_DECLARE_METATYPE(QList<QByteArray>)
/*
* Implementation of interface class OrgFreedesktopAvahiServerInterface
*/
OrgFreedesktopAvahiServerInterface::OrgFreedesktopAvahiServerInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
{
}
OrgFreedesktopAvahiServerInterface::~OrgFreedesktopAvahiServerInterface()
{
}
namespace DNSSD {
void registerTypes()
{
static bool registered=false;
if (!registered) {
qDBusRegisterMetaType<QList<QByteArray> >();
registered=true;
}
}
QString domainToDNS(const QString &domain)
{
if (domainIsLocal(domain)) return domain;
else return QUrl::toAce(domain);
}
QString DNSToDomain(const QString& domain)
{
if (domainIsLocal(domain)) return domain;
- else return QUrl::fromAce(domain.toAscii());
+ else return QUrl::fromAce(domain.toLatin1());
}
}
#include "moc_avahi_server_interface.cpp"
diff --git a/dnssd/mdnsd-publicservice.cpp b/dnssd/mdnsd-publicservice.cpp
index 14fdc47965..2a775fab6f 100644
--- a/dnssd/mdnsd-publicservice.cpp
+++ b/dnssd/mdnsd-publicservice.cpp
@@ -1,207 +1,207 @@
/* This file is part of the KDE project
*
* Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl>
*
* 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 <QtCore/QCoreApplication>
#include <QtCore/QStringList>
#include <netinet/in.h>
#include "publicservice.h"
#include "servicebase_p.h"
#include "mdnsd-sdevent.h"
#include "mdnsd-responder.h"
#include "settings.h"
#define K_D PublicServicePrivate* d=static_cast<PublicServicePrivate*>(this->d)
namespace DNSSD
{
void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name,
const char*, const char*, void *context);
class PublicServicePrivate : public Responder, public ServiceBasePrivate
{
public:
PublicServicePrivate(PublicService* parent, const QString& name, const QString& type, unsigned int port,
const QString& domain) : Responder(), ServiceBasePrivate(name, type, domain, QString(), port),
m_published(false), m_parent(parent)
{}
bool m_published;
PublicService* m_parent;
QStringList m_subtypes;
virtual void customEvent(QEvent* event);
};
PublicService::PublicService(const QString& name, const QString& type, unsigned int port,
const QString& domain, const QStringList& subtypes)
: QObject(), ServiceBase(new PublicServicePrivate(this, name, type, port, domain))
{
K_D;
if (domain.isNull()) d->m_domain="local.";
d->m_subtypes=subtypes;
}
PublicService::~PublicService()
{
stop();
}
void PublicService::setServiceName(const QString& serviceName)
{
K_D;
d->m_serviceName = serviceName;
if (d->isRunning()) {
stop();
publishAsync();
}
}
void PublicService::setDomain(const QString& domain)
{
K_D;
d->m_domain = domain;
if (d->isRunning()) {
stop();
publishAsync();
}
}
QStringList PublicService::subtypes() const
{
K_D;
return d->m_subtypes;
}
void PublicService::setType(const QString& type)
{
K_D;
d->m_type = type;
if (d->isRunning()) {
stop();
publishAsync();
}
}
void PublicService::setSubTypes(const QStringList& subtypes)
{
K_D;
d->m_subtypes = subtypes;
if (d->isRunning()) {
stop();
publishAsync();
}
}
void PublicService::setPort(unsigned short port)
{
K_D;
d->m_port = port;
if (d->isRunning()) {
stop();
publishAsync();
}
}
bool PublicService::isPublished() const
{
K_D;
return d->m_published;
}
void PublicService::setTextData(const QMap<QString,QByteArray>& textData)
{
K_D;
d->m_textData = textData;
if (d->isRunning()) {
stop();
publishAsync();
}
}
bool PublicService::publish()
{
K_D;
publishAsync();
while (d->isRunning() && !d->m_published) d->process();
return d->m_published;
}
void PublicService::stop()
{
K_D;
d->stop();
d->m_published = false;
}
void PublicService::publishAsync()
{
K_D;
if (d->isRunning()) stop();
TXTRecordRef txt;
TXTRecordCreate(&txt,0,0);
QMap<QString,QByteArray>::ConstIterator itEnd = d->m_textData.end();
for (QMap<QString,QByteArray>::ConstIterator it = d->m_textData.begin(); it!=itEnd ; ++it) {
if (TXTRecordSetValue(&txt,it.key().toUtf8(),it.value().length(),it.value())!=kDNSServiceErr_NoError) {
TXTRecordDeallocate(&txt);
emit published(false);
return;
}
}
DNSServiceRef ref;
QString fullType=d->m_type;
Q_FOREACH(const QString &subtype, d->m_subtypes) fullType+=','+subtype;
- if (DNSServiceRegister(&ref,0,0,d->m_serviceName.toUtf8(),fullType.toAscii().constData(),domainToDNS(d->m_domain),NULL,
+ if (DNSServiceRegister(&ref,0,0,d->m_serviceName.toUtf8(),fullType.toLatin1().constData(),domainToDNS(d->m_domain),NULL,
htons(d->m_port),TXTRecordGetLength(&txt),TXTRecordGetBytesPtr(&txt),publish_callback,
reinterpret_cast<void*>(d)) == kDNSServiceErr_NoError) d->setRef(ref);
TXTRecordDeallocate(&txt);
if (!d->isRunning()) emit published(false);
}
void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name,
const char*, const char*, void *context)
{
QObject *obj = reinterpret_cast<QObject*>(context);
if (errorCode != kDNSServiceErr_NoError) {
ErrorEvent err;
QCoreApplication::sendEvent(obj, &err);
} else {
PublishEvent pev(QString::fromUtf8(name));
QCoreApplication::sendEvent(obj, &pev);
}
}
void PublicServicePrivate::customEvent(QEvent* event)
{
if (event->type()==QEvent::User+SD_ERROR) {
m_parent->stop();
emit m_parent->published(false);
}
if (event->type()==QEvent::User+SD_PUBLISH) {
m_published=true;
emit m_parent->published(true);
m_serviceName = static_cast<PublishEvent*>(event)->m_name;
}
}
void PublicService::virtual_hook(int, void*)
{
}
}
#include "moc_publicservice.cpp"
diff --git a/dnssd/mdnsd-remoteservice.cpp b/dnssd/mdnsd-remoteservice.cpp
index 3ffd91c67b..cf3366ea71 100644
--- a/dnssd/mdnsd-remoteservice.cpp
+++ b/dnssd/mdnsd-remoteservice.cpp
@@ -1,154 +1,154 @@
/* This file is part of the KDE project
*
* Copyright (C) 2004, 2005 Jakub Stachowski <qbast@go2.pl>
*
* 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 <netinet/in.h>
#include <QtCore/QEventLoop>
#include <QtCore/QCoreApplication>
#include <kdebug.h>
#include "remoteservice.h"
#include "servicebase_p.h"
#include "mdnsd-responder.h"
#include "mdnsd-sdevent.h"
namespace DNSSD
{
void resolve_callback ( DNSServiceRef,
DNSServiceFlags,
uint32_t,
DNSServiceErrorType errorCode,
const char*,
const char *hosttarget,
uint16_t port,
uint16_t txtLen,
const unsigned char *txtRecord,
void *context
);
#define K_D RemoteServicePrivate* d=static_cast<RemoteServicePrivate*>(this->d)
class RemoteServicePrivate : public Responder, public ServiceBasePrivate
{
public:
RemoteServicePrivate(RemoteService* parent, const QString& name,const QString& type,const QString& domain) :
Responder(), ServiceBasePrivate(name, type, domain, QString(), 0), m_resolved(false), m_parent(parent)
{}
bool m_resolved;
RemoteService* m_parent;
virtual void customEvent(QEvent* event);
};
RemoteService::RemoteService(const QString& name,const QString& type,const QString& domain)
: ServiceBase(new RemoteServicePrivate(this, name, type, domain))
{
}
RemoteService::~RemoteService()
{}
bool RemoteService::resolve()
{
K_D;
resolveAsync();
while (d->isRunning() && !d->m_resolved) d->process();
d->stop();
return d->m_resolved;
}
void RemoteService::resolveAsync()
{
K_D;
if (d->isRunning()) return;
d->m_resolved = false;
kDebug() << this << ":Starting resolve of : " << d->m_serviceName << " " << d->m_type << " " << d->m_domain << "\n";
DNSServiceRef ref;
- if (DNSServiceResolve(&ref,0,0,d->m_serviceName.toUtf8(), d->m_type.toAscii().constData(),
+ if (DNSServiceResolve(&ref,0,0,d->m_serviceName.toUtf8(), d->m_type.toLatin1().constData(),
domainToDNS(d->m_domain),(DNSServiceResolveReply)resolve_callback,reinterpret_cast<void*>(d))
== kDNSServiceErr_NoError) d->setRef(ref);
if (!d->isRunning()) emit resolved(false);
}
bool RemoteService::isResolved() const
{
K_D;
return d->m_resolved;
}
void RemoteServicePrivate::customEvent(QEvent* event)
{
if (event->type() == QEvent::User+SD_ERROR) {
stop();
m_resolved=false;
emit m_parent->resolved(false);
}
if (event->type() == QEvent::User+SD_RESOLVE) {
ResolveEvent* rev = static_cast<ResolveEvent*>(event);
m_hostName = rev->m_hostname;
m_port = rev->m_port;
m_textData = rev->m_txtdata;
m_resolved = true;
emit m_parent->resolved(true);
}
}
void RemoteService::virtual_hook(int, void*)
{
// BASE::virtual_hook(int, void*);
}
void resolve_callback ( DNSServiceRef,
DNSServiceFlags,
uint32_t,
DNSServiceErrorType errorCode,
const char*,
const char *hosttarget,
uint16_t port,
uint16_t txtLen,
const unsigned char *txtRecord,
void *context
)
{
QObject *obj = reinterpret_cast<QObject*>(context);
if (errorCode != kDNSServiceErr_NoError) {
ErrorEvent err;
QCoreApplication::sendEvent(obj, &err);
return;
}
char key[256];
int index=0;
unsigned char valueLen;
kDebug() << "Resolve callback\n";
QMap<QString,QByteArray> map;
const void *voidValue = 0;
while (TXTRecordGetItemAtIndex(txtLen,txtRecord,index++,256,key,&valueLen,
&voidValue) == kDNSServiceErr_NoError)
{
if (voidValue) map[QString::fromUtf8(key)]=QByteArray((const char*)voidValue,valueLen);
else map[QString::fromUtf8(key)].clear();
}
ResolveEvent rev(DNSToDomain(hosttarget),ntohs(port),map);
QCoreApplication::sendEvent(obj, &rev);
}
}
#include "moc_remoteservice.cpp"
diff --git a/dnssd/mdnsd-servicebrowser.cpp b/dnssd/mdnsd-servicebrowser.cpp
index 00caabc106..00918c305f 100644
--- a/dnssd/mdnsd-servicebrowser.cpp
+++ b/dnssd/mdnsd-servicebrowser.cpp
@@ -1,201 +1,201 @@
/* This file is part of the KDE project
*
* Copyright (C) 2004 Jakub Stachowski <qbast@go2.pl>
*
* 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 "mdnsd-servicebrowser_p.h"
#include "domainbrowser.h"
#include "servicebrowser.h"
#include "mdnsd-responder.h"
#include "remoteservice.h"
#include "mdnsd-sdevent.h"
#include <dns_sd.h>
#include <QtCore/QStringList>
#include <QtCore/QHash>
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <QtNetwork/QHostInfo>
#define TIMEOUT_WAN 2000
#define TIMEOUT_LAN 200
namespace DNSSD
{
void query_callback (DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode,
const char *serviceName, const char *regtype, const char *replyDomain, void *context);
ServiceBrowser::ServiceBrowser(const QString& type,bool autoResolve,const QString& domain, const QString& subtype)
:d(new ServiceBrowserPrivate(this))
{
d->m_type=type;
d->m_autoResolve=autoResolve;
d->m_domain=domain;
d->m_subtype=subtype;
d->timeout.setSingleShot(true);
connect(&d->timeout,SIGNAL(timeout()),d,SLOT(onTimeout()));
}
ServiceBrowser::State ServiceBrowser::isAvailable()
{
// DNSServiceRef ref;
// bool ok (DNSServiceCreateConnection(&ref)==kDNSServiceErr_NoError);
// if (ok) DNSServiceRefDeallocate(ref);
// return (ok) ? Working : Stopped;
return Working;
}
ServiceBrowser::~ ServiceBrowser()
{
delete d;
}
bool ServiceBrowser::isAutoResolving() const
{
return d->m_autoResolve;
}
void ServiceBrowserPrivate::serviceResolved(bool success)
{
QObject* sender_obj = const_cast<QObject*>(sender());
RemoteService* svr = static_cast<RemoteService*>(sender_obj);
disconnect(svr,SIGNAL(resolved(bool)),this,SLOT(serviceResolved(bool)));
QList<RemoteService::Ptr>::Iterator it = m_duringResolve.begin();
QList<RemoteService::Ptr>::Iterator itEnd = m_duringResolve.end();
while ( it!= itEnd && svr!= (*it).data()) ++it;
if (it != itEnd) {
if (success) {
m_services+=(*it);
emit m_parent->serviceAdded(RemoteService::Ptr(svr));
}
m_duringResolve.erase(it);
queryFinished();
}
}
void ServiceBrowser::startBrowse()
{
if (d->isRunning()) return;
d->m_finished = false;
DNSServiceRef ref;
QString fullType=d->m_type;
if (!d->m_subtype.isEmpty()) fullType=d->m_subtype+"._sub."+d->m_type;
- if (DNSServiceBrowse(&ref,0,0, fullType.toAscii().constData(),
+ if (DNSServiceBrowse(&ref,0,0, fullType.toLatin1().constData(),
domainToDNS(d->m_domain),query_callback,reinterpret_cast<void*>(d))
== kDNSServiceErr_NoError) d->setRef(ref);
if (!d->isRunning()) emit finished();
else d->timeout.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAN : TIMEOUT_WAN);
}
void ServiceBrowserPrivate::queryFinished()
{
if (!m_duringResolve.count() && m_finished) emit m_parent->finished();
}
QList<RemoteService::Ptr> ServiceBrowser::services() const
{
return d->m_services;
}
void ServiceBrowser::virtual_hook(int, void*)
{}
RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList<RemoteService::Ptr>& where) const
{
Q_FOREACH (const RemoteService::Ptr& i, where) if (*s==*i) return i;
return RemoteService::Ptr();
}
void ServiceBrowserPrivate::customEvent(QEvent* event)
{
if (event->type()==QEvent::User+SD_ERROR) {
stop();
m_finished=false;
queryFinished();
}
if (event->type()==QEvent::User+SD_ADDREMOVE) {
AddRemoveEvent *aev = static_cast<AddRemoveEvent*>(event);
// m_type has useless trailing dot
RemoteService::Ptr svr(new RemoteService(aev->m_name,aev->m_type.left(aev->m_type.length()-1),aev->m_domain));
if (aev->m_op==AddRemoveEvent::Add) {
if (m_autoResolve) {
connect(svr.data(),SIGNAL(resolved(bool)),this,SLOT(serviceResolved(bool)));
m_duringResolve+=svr;
svr->resolveAsync();
} else {
m_services+=svr;
emit m_parent->serviceAdded(svr);
}
}
else {
RemoteService::Ptr found=find(svr, m_duringResolve);
if (!found.isNull()) m_duringResolve.removeAll(found);
else {
found=find(svr, m_services);
if (!found.isNull()) {
emit m_parent->serviceRemoved(found);
m_services.removeAll(found);
}
}
}
m_finished = aev->m_last;
if (m_finished) queryFinished();
}
}
void ServiceBrowserPrivate::onTimeout()
{
m_finished=true;
queryFinished();
}
void query_callback (DNSServiceRef, DNSServiceFlags flags, uint32_t, DNSServiceErrorType errorCode,
const char *serviceName, const char *regtype, const char *replyDomain,
void *context)
{
QObject *obj = reinterpret_cast<QObject*>(context);
if (errorCode != kDNSServiceErr_NoError) {
ErrorEvent err;
QCoreApplication::sendEvent(obj, &err);
} else {
AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add :
AddRemoveEvent::Remove, QString::fromUtf8(serviceName), regtype,
DNSToDomain(replyDomain), !(flags & kDNSServiceFlagsMoreComing));
QCoreApplication::sendEvent(obj, &arev);
}
}
// TODO: Please Implement Me - Using a KResolver (if not natively)
QHostAddress ServiceBrowser::resolveHostName(const QString &hostname)
{
return QHostAddress();
}
QString ServiceBrowser::getLocalHostName()
{
return QHostInfo::localHostName();
}
}
#include "moc_servicebrowser.cpp"
#include "mdnsd-servicebrowser_p.moc"
diff --git a/interfaces/kimproxy/library/kimproxy.cpp b/interfaces/kimproxy/library/kimproxy.cpp
index b4ae6647b6..f96bc4abb5 100644
--- a/interfaces/kimproxy/library/kimproxy.cpp
+++ b/interfaces/kimproxy/library/kimproxy.cpp
@@ -1,660 +1,660 @@
/*
kimproxy.cpp
IM service library for KDE
Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk>
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 "kimproxy.h"
#include <QPixmapCache>
#include <kapplication.h>
#include <kdbusservicestarter.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kiconloader.h>
#include <kservice.h>
#include <kservicetypetrader.h>
#include "kimiface.h"
#include <kservicetype.h>
struct AppPresenceCurrent
{
QString appId;
int presence;
};
static int debugArea() {
static int s_area = KDebug::registerArea("kimproxy (kdelibs)");
return s_area;
}
class ContactPresenceListCurrent : public QList<AppPresenceCurrent>
{
public:
// return value indicates if the supplied parameter was better than any existing presence
bool update( const AppPresenceCurrent );
AppPresenceCurrent best();
};
class KIMProxy::Private
{
public:
// list of the strings in use by KIMIface
QStringList presence_strings;
// list of the icon names in use by KIMIface
QStringList presence_icons;
// map of presences
PresenceStringMap presence_map;
};
bool ContactPresenceListCurrent::update( AppPresenceCurrent ap )
{
if ( isEmpty() )
{
append( ap );
return true;
}
bool bestChanged = false;
AppPresenceCurrent best;
best.presence = -1;
ContactPresenceListCurrent::iterator it = begin();
const ContactPresenceListCurrent::iterator itEnd = end();
ContactPresenceListCurrent::iterator existing = itEnd;
while ( it != itEnd )
{
if ( (*it).presence > best.presence )
best = (*it);
if ( (*it).appId == ap.appId )
existing = it;
++it;
}
if ( ap.presence > best.presence ||
best.appId == ap.appId )
bestChanged = true;
if ( existing != itEnd )
{
erase( existing );
append( ap );
}
return bestChanged;
}
AppPresenceCurrent ContactPresenceListCurrent::best()
{
AppPresenceCurrent best;
best.presence = -1;
ContactPresenceListCurrent::iterator it = begin();
const ContactPresenceListCurrent::iterator itEnd = end();
while ( it != itEnd )
{
if ( (*it).presence > best.presence )
best = (*it);
++it;
}
// if it's still -1 here, we have no presence data, so we return Unknown
if ( best.presence == -1 )
best.presence = 0;
return best;
}
// int bestPresence( AppPresence* ap )
// {
// Q_ASSERT( ap );
// AppPresence::const_iterator it;
// it = ap->begin();
// int best = 0; // unknown
// if ( it != ap->end() )
// {
// best = it.data();
// ++it;
// for ( ; it != ap->end(); ++it )
// {
// if ( it.data() > best )
// best = it.data();
// }
// }
// return best;
// }
//
// QCString bestAppId( AppPresence* ap )
// {
// Q_ASSERT( ap );
// AppPresence::const_iterator it;
// QCString bestAppId;
// it = ap->begin();
// if ( it != ap->end() )
// {
// int best = it.data();
// bestAppId = it.key();
// ++it;
// for ( ; it != ap->end(); ++it )
// {
// if ( it.data() > best )
// {
// best = it.data();
// bestAppId = it.key();
// }
// }
// }
// return bestAppId;
// }
OrgKdeKIMInterface * findInterface( const QString & app )
{
return new OrgKdeKIMInterface( app, "/KIMIface", QDBusConnection::sessionBus() );
}
class KIMProxySingleton
{
public:
KIMProxy instance;
};
Q_GLOBAL_STATIC(KIMProxySingleton, s_instance)
KIMProxy * KIMProxy::instance()
{
return &s_instance()->instance;
}
KIMProxy::KIMProxy() : QObject(), d( new Private )
{
//QDBus::sessionBus().registerObject( "/KIMProxy", this);
m_initialized = false;
connect( QDBusConnection::sessionBus().interface(),
SIGNAL(serviceOwnerChanged(QString,QString,QString)),
SLOT(nameOwnerChanged(QString,QString,QString)) );
d->presence_strings.append( "Unknown" );
d->presence_strings.append( "Offline" );
d->presence_strings.append( "Connecting" );
d->presence_strings.append( "Away" );
d->presence_strings.append( "Online" );
d->presence_icons.append( "presence_unknown" );
d->presence_icons.append( "presence_offline" );
d->presence_icons.append( "presence_connecting" );
d->presence_icons.append( "presence_away" );
d->presence_icons.append( "presence_online" );
//QCString senderApp = "Kopete";
//QCString senderObjectId = "KIMIface";
//DCOPCString method = "contactPresenceChanged( QString, QCString, int )";
//QCString receiverObjectId = "KIMProxyIface";
QDBusConnection::sessionBus().connect( QString(), "/KIMIface", "org.kde.KIM", "contactPresenceChanged",
this, SLOT(contactPresenceChanged(QString,QString,int)) );
}
KIMProxy::~KIMProxy( )
{
qDeleteAll(m_im_client_stubs);
}
bool KIMProxy::initialize()
{
if ( !m_initialized )
{
m_initialized = true; // we should only do this once, as registeredToDCOP() will catch any new starts
// So there is no error from a failed query when using kdelibs 3.2, which don't have this servicetype
if ( KServiceType::serviceType( IM_SERVICE_TYPE ) )
{
// see what apps implementing our service type are out there
const KService::List offers = KServiceTypeTrader::self()->query( IM_SERVICE_TYPE );
KService::List::const_iterator offer;
QStringList registeredApps = QDBusConnection::sessionBus().interface()->registeredServiceNames();
foreach (const QString &app, registeredApps)
{
//kDebug( debugArea() ) << " considering: " << *app;
//for each offer
for ( offer = offers.begin(); offer != offers.end(); ++offer )
{
QString dbusService = (*offer)->property("X-DBUS-ServiceName").toString();
if ( !dbusService.isEmpty() )
{
//kDebug( debugArea() ) << " is it: " << dbusService << "?";
// if the application implements the dcop service, add it
if ( app.startsWith( dbusService ) )
{
m_apps_available = true;
//kDebug( debugArea() ) << " app name: " << (*offer)->name() << ", has instance " << *app << ", dbusService: " << dbusService;
if ( !m_im_client_stubs.contains( dbusService ) )
{
kDebug( debugArea() ) << "App " << app << ", found, using it for presence info.";
m_im_client_stubs.insert( app, findInterface( app ) );
pollApp( app );
}
}
}
}
}
}
}
return !m_im_client_stubs.isEmpty();
}
void KIMProxy::nameOwnerChanged( const QString & appId, const QString &, const QString & newOwner )
{
// unregister...
if ( m_im_client_stubs.contains( appId ) )
{
kDebug( debugArea() ) << appId << " quit, removing its presence info.";
PresenceStringMap::Iterator it = d->presence_map.begin();
const PresenceStringMap::Iterator end = d->presence_map.end();
for ( ; it != end; ++it )
{
ContactPresenceListCurrent list = it.value();
ContactPresenceListCurrent::iterator cpIt = list.begin();
while( cpIt != list.end() )
{
ContactPresenceListCurrent::iterator gone = cpIt++;
if ( (*gone).appId == appId )
{
list.erase( gone );
}
}
}
delete m_im_client_stubs.take( appId );
emit sigPresenceInfoExpired();
}
// reregister...
if ( !newOwner.isEmpty() ) { // application registered
bool newApp = false;
// get an up to date list of offers in case a new app was installed
// and check each of the offers that implement the service type we're looking for,
// to see if any of them are the app that just registered
const KService::List offers = KServiceTypeTrader::self()->query( IM_SERVICE_TYPE );
KService::List::const_iterator it;
for ( it = offers.begin(); it != offers.end(); ++it )
{
QString dbusService = (*it)->property("X-DBUS-ServiceName").toString();
if ( appId.startsWith( dbusService ) )
{
// if it's not already known, insert it
if ( !m_im_client_stubs.contains( appId ) )
{
newApp = true;
kDebug( debugArea() ) << "App: " << appId << ", dbusService: " << dbusService << " started, using it for presence info.";
m_im_client_stubs.insert( appId, findInterface( appId ) );
}
}
//else
// kDebug( debugArea() ) << "App doesn't implement our ServiceType";
}
//if ( newApp )
// emit sigPresenceInfoExpired();
}
}
void KIMProxy::contactPresenceChanged( const QString& uid, const QString& appId, int presence )
{
// update the presence map
//kDebug( debugArea() ) << "uid: " << uid << " appId: " << appId << " presence " << presence;
ContactPresenceListCurrent current;
current = d->presence_map[ uid ];
//kDebug( debugArea() ) << "current best presence from : " << current.best().appId << " is: " << current.best().presence;
AppPresenceCurrent newPresence;
newPresence.appId = appId;
newPresence.presence = presence;
if ( current.update( newPresence ) )
{
d->presence_map.insert( uid, current );
emit sigContactPresenceChanged( uid );
}
}
int KIMProxy::presenceNumeric( const QString& uid )
{
AppPresenceCurrent ap;
ap.presence = 0;
if ( initialize() )
{
ContactPresenceListCurrent presence = d->presence_map[ uid ];
ap = presence.best();
}
return ap.presence;
}
QString KIMProxy::presenceString( const QString& uid )
{
AppPresenceCurrent ap;
ap.presence = 0;
if ( initialize() )
{
ContactPresenceListCurrent presence = d->presence_map[ uid ];
ap = presence.best();
}
if ( ap.appId.isEmpty() )
return QString();
else
return d->presence_strings[ ap.presence ];
}
QPixmap KIMProxy::presenceIcon( const QString& uid )
{
AppPresenceCurrent ap;
ap.presence = 0;
if ( initialize() )
{
ContactPresenceListCurrent presence = d->presence_map[ uid ];
ap = presence.best();
}
if ( ap.appId.isEmpty() )
{
//kDebug( debugArea() ) << "returning a null QPixmap because we were asked for an icon for a uid we know nothing about";
return QPixmap();
}
else
{
//kDebug( debugArea() ) << "returning this: " << d->presence_icons[ ap.presence ];
return SmallIcon( d->presence_icons[ ap.presence ]);
}
}
QStringList KIMProxy::allContacts()
{
QStringList value = d->presence_map.keys();
return value;
}
QStringList KIMProxy::reachableContacts()
{
QStringList value;
if ( initialize() )
{
QHashIterator<QString, OrgKdeKIMInterface*> it( m_im_client_stubs );
while (it.hasNext())
{
it.next();
value += it.value()->reachableContacts( );
}
}
return value;
}
QStringList KIMProxy::onlineContacts()
{
QStringList value;
PresenceStringMap::iterator it = d->presence_map.begin();
const PresenceStringMap::iterator end= d->presence_map.end();
for ( ; it != end; ++it )
if ( it.value().best().presence > 2 /*Better than Connecting, ie Away or Online*/ )
value.append( it.key() );
return value;
}
QStringList KIMProxy::fileTransferContacts()
{
QStringList value;
if ( initialize() )
{
QHashIterator<QString, OrgKdeKIMInterface*> it( m_im_client_stubs );
while (it.hasNext())
{
it.next();
value += it.value()->fileTransferContacts( );
}
}
return value;
}
bool KIMProxy::isPresent( const QString& uid )
{
return ( !d->presence_map[ uid ].isEmpty() );
}
QString KIMProxy::displayName( const QString& uid )
{
QString name;
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForUid( uid ) )
name = s->displayName( uid );
}
//kDebug( debugArea() ) << name;
return name;
}
bool KIMProxy::canReceiveFiles( const QString & uid )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForUid( uid ) )
return s->canReceiveFiles( uid );
}
return false;
}
bool KIMProxy::canRespond( const QString & uid )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForUid( uid ) )
return s->canRespond( uid );
}
return false;
}
QString KIMProxy::context( const QString & uid )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForUid( uid ) )
return s->context( uid );
}
return QString();
}
void KIMProxy::chatWithContact( const QString& uid )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForUid( uid ) )
{
kapp->updateRemoteUserTimestamp( s->service() );
s->chatWithContact( uid );
}
}
return;
}
void KIMProxy::messageContact( const QString& uid, const QString& message )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForUid( uid ) )
{
kapp->updateRemoteUserTimestamp( s->service() );
s->messageContact( uid, message );
}
}
return;
}
void KIMProxy::sendFile(const QString &uid, const QString &sourceURL, const QString &altFileName, uint fileSize )
{
if ( initialize() )
{
QHashIterator<QString,OrgKdeKIMInterface*> it( m_im_client_stubs );
while ( it.hasNext() )
{
it.next();
if ( it.value()->canReceiveFiles( uid ) )
{
kapp->updateRemoteUserTimestamp( it.value()->service() );
it.value()->sendFile( uid, sourceURL, altFileName, fileSize );
break;
}
}
}
return;
}
bool KIMProxy::addContact( const QString &contactId, const QString &protocol )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForProtocol( protocol ) )
return s->addContact( contactId, protocol );
}
return false;
}
QString KIMProxy::locate( const QString & contactId, const QString & protocol )
{
if ( initialize() )
{
if ( OrgKdeKIMInterface* s = stubForProtocol( protocol ) )
return s->locate( contactId, protocol );
}
return QString();
}
bool KIMProxy::imAppsAvailable()
{
return ( !m_im_client_stubs.isEmpty() );
}
bool KIMProxy::startPreferredApp()
{
#ifdef __GNUC__
# warning "unused variable: preferences"
#endif
QString preferences = QString("[X-DBUS-ServiceName] = '%1'").arg( preferredApp() );
// start/find an instance of DBUS/InstantMessenger
QString error;
QString dbusService;
// Get a preferred IM client.
// The app will notify itself to us using nameOwnerChanged, so we don't need to record a stub for it here
// FIXME: error in preferences, see debug output
preferences.clear();
int result = KDBusServiceStarter::self()->findServiceFor( IM_SERVICE_TYPE, QString("Application"), &error, &dbusService );
kDebug( debugArea() ) << "error was: " << error << ", dbusService: " << dbusService;
return ( result == 0 );
}
void KIMProxy::pollAll( const QString &uid )
{
Q_UNUSED(uid);
/* // We only need to call this function if we don't have any data at all
// otherwise, the data will be kept fresh by received presence change
// DCOP signals
if ( !d->presence_map.contains( uid ) )
{
AppPresence *presence = new AppPresence();
// record current presence from known clients
QDictIterator<OrgKdeKIMInterface> it( m_im_client_stubs );
for ( ; it.current(); ++it )
{
- presence->insert( it.currentKey().toAscii().constData(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys...
+ presence->insert( it.currentKey().toLatin1().constData(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys...
}
d->presence_map.insert( uid, presence );
}*/
}
void KIMProxy::pollApp( const QString & appId )
{
//kDebug( debugArea() ) ;
OrgKdeKIMInterface * appStub = m_im_client_stubs.value( appId );
QStringList contacts = m_im_client_stubs.value( appId )->allContacts();
QStringList::iterator it = contacts.begin();
QStringList::iterator end = contacts.end();
for ( ; it != end; ++it )
{
ContactPresenceListCurrent current = d->presence_map[ *it ];
AppPresenceCurrent ap;
ap.appId = appId;
#ifdef __GNUC__
# warning "KIMProxy::pollApp( const QString & appId ).presenceStatus() function doesn't exist Need to fix it"
#endif
//ap.presence = appStub->presenceStatus( *it );
current.append( ap );
d->presence_map.insert( *it, current );
if ( current.update( ap ) )
emit sigContactPresenceChanged( *it );
//kDebug( debugArea() ) << " uid: " << *it << " presence: " << ap.presence;
}
}
OrgKdeKIMInterface * KIMProxy::stubForUid( const QString &uid )
{
// get best appPresence
AppPresenceCurrent ap = d->presence_map[ uid ].best();
// look up the presence string from that app
return m_im_client_stubs.value( ap.appId );
}
OrgKdeKIMInterface * KIMProxy::stubForProtocol( const QString &protocol)
{
Q_UNUSED(protocol)
#ifdef __GNUC__
# warning "KIMProxy::stubForProtocol( const QString &protocol) code disabled: protocols() function doesn't exist. Need to fix it"
#endif
#if 0
OrgKdeKIMInterface * app;
// see if the preferred client supports this protocol
QString preferred = preferredApp();
if ( ( app = m_im_client_stubs.value( preferred ) ) )
{
if ( app->protocols().value().filter( protocol ).count() > 0 )
return app;
}
// preferred doesn't do this protocol, try the first of the others that says it does
QHashIterator<QString, OrgKdeKIMInterface*> it( m_im_client_stubs );
while ( it.hasNext() )
{
it.next();
if ( it.value()->protocols().value().filter( protocol ).count() > 0 )
return it.value();
}
#endif
return 0L;
}
QString KIMProxy::preferredApp()
{
KConfig cfg( IM_CLIENT_PREFERENCES_FILE, KConfig::SimpleConfig );
KConfigGroup cg(&cfg, IM_CLIENT_PREFERENCES_SECTION );
QString preferredApp = cg.readEntry( IM_CLIENT_PREFERENCES_ENTRY );
//kDebug( debugArea() ) << "found preferred app: " << preferredApp;
return preferredApp;
}
diff --git a/kconf_update/kconfigutils.cpp b/kconf_update/kconfigutils.cpp
index 22591b0cfa..f2663e1316 100644
--- a/kconf_update/kconfigutils.cpp
+++ b/kconf_update/kconfigutils.cpp
@@ -1,127 +1,127 @@
/* This file is part of the KDE libraries
Copyright 2010 Canonical Ltd
Author: Aurélien Gâteau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) 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 "kconfigutils.h"
// KDE
#include <kconfig.h>
#include <kconfiggroup.h>
namespace KConfigUtils
{
bool hasGroup(KConfig *config, const QStringList &lst)
{
KConfigGroup group = openGroup(config, lst);
return group.exists();
}
KConfigGroup openGroup(KConfig *config, const QStringList &_lst)
{
if (_lst.isEmpty()) {
return KConfigGroup(config, QString());
}
QStringList lst = _lst;
KConfigGroup cg;
for (cg = KConfigGroup(config, lst.takeFirst()); !lst.isEmpty(); cg = KConfigGroup(&cg, lst.takeFirst())) {}
return cg;
}
QStringList parseGroupString(const QString &_str, bool *ok, QString *error)
{
QString str = unescapeString(_str.trimmed(), ok, error);
if (!ok) {
return QStringList();
}
*ok = true;
if (str[0] != '[') {
// Simplified notation, no '['
return QStringList() << str;
}
if (!str.endsWith(']')) {
*ok = false;
*error = QString("Missing closing ']' in %1").arg(_str);
return QStringList();
}
// trim outer brackets
str.chop(1);
str.remove(0, 1);
return str.split("][");
}
QString unescapeString(const QString &src, bool *ok, QString *error)
{
QString dst;
int length = src.length();
for (int pos = 0; pos < length; ++pos) {
QChar ch = src.at(pos);
if (ch != '\\') {
dst += ch;
} else {
++pos;
if (pos == length) {
*ok = false;
*error = QString("Unfinished escape sequence in %1").arg(src);
return QString();
}
ch = src.at(pos);
if (ch == 's') {
dst += ' ';
} else if (ch == 't') {
dst += '\t';
} else if (ch == 'n') {
dst += '\n';
} else if (ch == 'r') {
dst += '\r';
} else if (ch == '\\') {
dst += '\\';
} else if (ch == 'x') {
if (pos + 2 < length) {
char value = src.mid(pos + 1, 2).toInt(ok, 16);
if (*ok) {
- dst += QChar::fromAscii(value);
+ dst += QChar::fromLatin1(value);
pos += 2;
} else {
*error = QString("Invalid hex escape sequence at column %1 in %2").arg(pos).arg(src);
return QString();
}
} else {
*ok = false;
*error = QString("Unfinished hex escape sequence at column %1 in %2").arg(pos).arg(src);
return QString();
}
} else {
*ok = false;
*error = QString("Invalid escape sequence at column %1 in %2").arg(pos).arg(src);
return QString();
}
}
}
*ok = true;
return dst;
}
} // namespace
diff --git a/kde3support/kdecore/k3rfcdate.cpp b/kde3support/kdecore/k3rfcdate.cpp
index 1613a17d83..5cde179438 100644
--- a/kde3support/kdecore/k3rfcdate.cpp
+++ b/kde3support/kdecore/k3rfcdate.cpp
@@ -1,495 +1,495 @@
/*
* This file is part of the KDE libraries
* Copyright (c) 2000-2002 Waldo Bastian <bastian@kde.org>
* 2002 Rik Hemsley <rik@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 version 2 as published by the Free Software Foundation.
*
* 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 "k3rfcdate.h"
#include <sys/param.h>
#include <ctype.h>
#include <stdlib.h>
#include <QtCore/QMutableStringListIterator>
#include <QtCore/QCharRef>
#include <QtCore/QByteArray>
static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
{
if (sizeof(time_t) == 4)
{
if ((time_t)-1 < 0)
{
if (year >= 2038)
{
year = 2038;
mon = 0;
day = 1;
hour = 0;
minute = 0;
second = 0;
}
}
else
{
if (year >= 2115)
{
year = 2115;
mon = 0;
day = 1;
hour = 0;
minute = 0;
second = 0;
}
}
}
unsigned int ret = (day - 32075) /* days */
+ 1461L * (year + 4800L + (mon - 14) / 12) / 4
+ 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
- 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
- 2440588;
ret = 24*ret + hour; /* hours */
ret = 60*ret + minute; /* minutes */
ret = 60*ret + second; /* seconds */
return ret;
}
static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
// we follow the recommendation of rfc2822 to consider all
// obsolete time zones not listed here equivalent to "-0000"
static const struct {
const char tzName[4];
int tzOffset;
} known_zones[] = {
{ "UT", 0 },
{ "GMT", 0 },
{ "EST", -300 },
{ "EDT", -240 },
{ "CST", -360 },
{ "CDT", -300 },
{ "MST", -420 },
{ "MDT", -360 },
{ "PST", -480 },
{ "PDT", -420 },
{ { 0,0,0,0 }, 0 }
};
time_t
K3RFCDate::parseDate(const QString &_date)
{
if (_date.isEmpty())
return 0;
// This parse a date in the form:
// Wednesday, 09-Nov-99 23:12:40 GMT
// or
// Sat, 01-Jan-2000 08:00:00 GMT
// or
// Sat, 01 Jan 2000 08:00:00 GMT
// or
// 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
//
// We ignore the weekday
//
time_t result = 0;
int offset = 0;
char *newPosStr;
const QByteArray dateArray = _date.toLatin1();
const char *dateString = dateArray.data();
int day = 0;
char monthStr[4];
int month = -1;
int year = 0;
int hour = 0;
int minute = 0;
int second = 0;
// Strip leading space
while(*dateString && isspace(*dateString))
dateString++;
// Strip weekday
while(*dateString && !isdigit(*dateString) && !isspace(*dateString))
dateString++;
// Strip trailing space
while(*dateString && isspace(*dateString))
dateString++;
if (!*dateString)
return result; // Invalid date
if (isalpha(*dateString))
{
// ' Nov 5 1994 18:15:30 GMT'
// Strip leading space
while(*dateString && isspace(*dateString))
dateString++;
for(int i=0; i < 3;i++)
{
if (!*dateString || (*dateString == '-') || isspace(*dateString))
return result; // Invalid date
monthStr[i] = tolower(*dateString++);
}
monthStr[3] = '\0';
newPosStr = (char*)strstr(haystack, monthStr);
if (!newPosStr)
return result; // Invalid date
month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
if ((month < 0) || (month > 11))
return result; // Invalid date
while (*dateString && isalpha(*dateString))
dateString++; // Skip rest of month-name
}
// ' 09-Nov-99 23:12:40 GMT'
// ' 5 1994 18:15:30 GMT'
day = strtol(dateString, &newPosStr, 10);
dateString = newPosStr;
if ((day < 1) || (day > 31))
return result; // Invalid date;
if (!*dateString)
return result; // Invalid date
while(*dateString && (isspace(*dateString) || (*dateString == '-')))
dateString++;
if (month == -1)
{
for(int i=0; i < 3;i++)
{
if (!*dateString || (*dateString == '-') || isspace(*dateString))
return result; // Invalid date
monthStr[i] = tolower(*dateString++);
}
monthStr[3] = '\0';
newPosStr = (char*)strstr(haystack, monthStr);
if (!newPosStr)
return result; // Invalid date
month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
if ((month < 0) || (month > 11))
return result; // Invalid date
while (*dateString && isalpha(*dateString))
dateString++; // Skip rest of month-name
}
// '-99 23:12:40 GMT'
while(*dateString && (isspace(*dateString) || (*dateString == '-')))
dateString++;
if (!*dateString || !isdigit(*dateString))
return result; // Invalid date
// '99 23:12:40 GMT'
year = strtol(dateString, &newPosStr, 10);
dateString = newPosStr;
// Y2K: Solve 2 digit years
if ((year >= 0) && (year < 50))
year += 2000;
if ((year >= 50) && (year < 100))
year += 1900; // Y2K
if ((year < 1900) || (year > 2500))
return result; // Invalid date
// Don't fail if the time is missing.
if (*dateString)
{
// ' 23:12:40 GMT'
if (!isspace(*dateString++))
return result; // Invalid date
hour = strtol(dateString, &newPosStr, 10);
dateString = newPosStr;
if ((hour < 0) || (hour > 23))
return result; // Invalid date
if (!*dateString)
return result; // Invalid date
// ':12:40 GMT'
if (*dateString++ != ':')
return result; // Invalid date
minute = strtol(dateString, &newPosStr, 10);
dateString = newPosStr;
if ((minute < 0) || (minute > 59))
return result; // Invalid date
if (!*dateString)
return result; // Invalid date
// ':40 GMT'
if (*dateString != ':' && !isspace(*dateString))
return result; // Invalid date
// seconds are optional in rfc822 + rfc2822
if (*dateString ==':') {
dateString++;
second = strtol(dateString, &newPosStr, 10);
dateString = newPosStr;
if ((second < 0) || (second > 59))
return result; // Invalid date
} else {
dateString++;
}
while(*dateString && isspace(*dateString))
dateString++;
}
// don't fail if the time zone is missing, some
// broken mail-/news-clients omit the time zone
if (*dateString) {
if ((strncasecmp(dateString, "gmt", 3) == 0) ||
(strncasecmp(dateString, "utc", 3) == 0))
{
dateString += 3;
while(*dateString && isspace(*dateString))
dateString++;
}
if ((*dateString == '+') || (*dateString == '-')) {
offset = strtol(dateString, &newPosStr, 10);
if (abs(offset) < 30)
{
dateString = newPosStr;
offset = offset * 100;
if (*dateString && *(dateString+1))
{
dateString++;
int minutes = strtol(dateString, &newPosStr, 10);
if (offset > 0)
offset += minutes;
else
offset -= minutes;
}
}
if ((offset < -9959) || (offset > 9959))
return result; // Invalid date
int sgn = (offset < 0)? -1:1;
offset = abs(offset);
offset = ((offset / 100)*60 + (offset % 100))*sgn;
} else {
for (int i=0; known_zones[i].tzName != 0; i++) {
if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
offset = known_zones[i].tzOffset;
break;
}
}
}
}
result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
// avoid negative time values
if ((offset > 0) && (offset > result))
offset = 0;
result -= offset*60;
// If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
// This is so that parse error and valid epoch 0 return values won't
// be the same for sensitive applications...
if (result < 1) result = 1;
return result;
}
time_t
K3RFCDate::parseDateISO8601( const QString& input_ )
{
if (input_.isEmpty())
return 0;
// These dates look like this:
// YYYY-MM-DDTHH:MM:SS
// But they may also have 0, 1 or 2 suffixes.
// Suffix 1: .secfrac (fraction of second)
// Suffix 2: Either 'Z' or +zone or -zone, where zone is HHMM
unsigned int year = 0;
unsigned int month = 0;
unsigned int mday = 0;
unsigned int hour = 0;
unsigned int min = 0;
unsigned int sec = 0;
int offset = 0;
QString input = input_;
// First find the 'T' separator, if any.
int tPos = input.indexOf(QLatin1Char('T'));
// If there is no time, no month or no day specified, fill those missing
// fields so that 'input' matches YYYY-MM-DDTHH:MM:SS
if (-1 == tPos) {
const int dashes = input.count('-');
if (0 == dashes) {
input += "-01-01";
} else if (1 == dashes) {
input += "-01";
}
tPos = input.length();
input += "T12:00:00";
}
// Now parse the date part.
QString dateString = input.left(tPos).trimmed();
QString timeString = input.mid(tPos + 1).trimmed();
QStringList l = dateString.split( '-');
if (l.size() < 3)
return 0;
year = l[0].toUInt();
month = l[1].toUInt();
mday = l[2].toUInt();
// Z suffix means UTC.
if ('Z' == timeString.at(timeString.length() - 1)) {
timeString.remove(timeString.length() - 1, 1);
}
// +zone or -zone suffix (offset from UTC).
int plusPos = timeString.lastIndexOf('+');
if (-1 != plusPos) {
QString offsetString = timeString.mid(plusPos + 1);
offset = offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt();
timeString = timeString.left(plusPos);
} else {
int minusPos = timeString.lastIndexOf('-');
if (-1 != minusPos) {
QString offsetString = timeString.mid(minusPos + 1);
offset = - int(offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt());
timeString = timeString.left(minusPos);
}
}
// secfrac suffix.
int dotPos = timeString.lastIndexOf('.');
if (-1 != dotPos) {
timeString = timeString.left(dotPos);
}
// Now parse the time part.
l = timeString.split( ':');
if (l.size() < 3)
return 0;
hour = l[0].toUInt();
min = l[1].toUInt();
sec = l[2].toUInt();
time_t result = ymdhms_to_seconds(year, month, mday, hour, min, sec);
// avoid negative time values
if ((offset > 0) && (offset > result))
offset = 0;
result -= offset*60;
// If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
// This is so that parse error and valid epoch 0 return values won't
// be the same for sensitive applications...
if (result < 1) result = 1;
return result;
}
int K3RFCDate::localUTCOffset()
{
time_t timeNow = time((time_t*) 0);
tm *tM = gmtime(&timeNow);
unsigned int timeUTC = ymdhms_to_seconds(tM->tm_year+1900, tM->tm_mon+1, tM->tm_mday,
tM->tm_hour, tM->tm_min, tM->tm_sec);
tM = localtime(&timeNow);
unsigned int timeLocal = ymdhms_to_seconds(tM->tm_year+1900, tM->tm_mon+1, tM->tm_mday,
tM->tm_hour, tM->tm_min, tM->tm_sec);
return ((int)(timeLocal-timeUTC))/60;
}
static const char day_names[][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static const char month_names[][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
QByteArray K3RFCDate::rfc2822DateString(time_t utcTime, int utcOffset)
{
utcTime += utcOffset * 60;
tm *tM = gmtime(&utcTime);
char sgn = (utcOffset < 0) ? '-' : '+';
int z = (utcOffset < 0) ? -utcOffset : utcOffset;
QByteArray dateStr;
dateStr = QString().sprintf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d",
day_names[tM->tm_wday], tM->tm_mday,
month_names[tM->tm_mon], tM->tm_year+1900,
tM->tm_hour, tM->tm_min, tM->tm_sec,
- sgn, z/60%24, z%60).toAscii();
+ sgn, z/60%24, z%60).toLatin1();
return dateStr;
}
diff --git a/kde3support/kdeui/k3dockwidget.cpp b/kde3support/kdeui/k3dockwidget.cpp
index 66aff747c4..919e5d9401 100644
--- a/kde3support/kdeui/k3dockwidget.cpp
+++ b/kde3support/kdeui/k3dockwidget.cpp
@@ -1,3472 +1,3472 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Max Judin <novaprint@mtu-net.ru>
Copyright (C) 2002,2003 Joseph Wenninger <jowenn@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 version 2 as published by the Free Software Foundation.
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 "k3dockwidget.h"
#include "k3dockwidget_p.h"
#include "k3dockwidget_private.h"
#include <QApplication>
#include <QLayout>
#include <QPainter>
#include <Qt3Support/Q3StrIList>
#include <QCursor>
#include <QWidget>
#include <QTabWidget>
#include <QStyle>
#include <QMouseEvent>
#include <QObject>
#include <QStylePainter>
#include <QStyleOption>
#ifndef NO_KDE2
#include <kconfiggroup.h>
#include <klocalizedstring.h>
#include <ktoolbar.h>
#include <k3popupmenu.h>
#include <kwindowsystem.h>
#include <kdebug.h>
#include <kglobalsettings.h>
#if HAVE_X11
#include <X11/X.h>
#include <X11/Xlib.h>
#include <qx11info_x11.h>
#endif
#else
#include <q3toolbar.h>
#include <q3popupmenu.h>
#endif
#include <stdlib.h>
#undef BORDERLESS_WINDOWS
#define DOCK_CONFIG_VERSION "0.0.5"
static const char* const dockback_xpm[]={
"6 6 2 1",
"# c black",
". c None",
"......",
".#....",
"..#..#",
"...#.#",
"....##",
"..####"};
static const char* const todesktop_xpm[]={
"5 5 2 1",
"# c black",
". c None",
"####.",
"##...",
"#.#..",
"#..#.",
"....#"};
static const char* const not_close_xpm[]={
"5 5 2 1",
"# c black",
". c None",
"#####",
"#...#",
"#...#",
"#...#",
"#####"};
/**
* A special kind of KMainWindow that is able to have dockwidget child widgets.
*
* The main widget should be a dockwidget where other dockwidgets can be docked to
* the left, right, top, bottom or to the middle.
* Furthermore, the K3DockMainWindow has got the KDocManager and some data about the dock states.
*
* @author Max Judin.
*/
-K3DockMainWindow::K3DockMainWindow( QWidget* parent, const char *name, Qt::WFlags f)
+K3DockMainWindow::K3DockMainWindow( QWidget* parent, const char *name, Qt::WindowFlags f)
: KXmlGuiWindow( parent, f )
{
setObjectName( name );
QString new_name = QString(name) + QString("_DockManager");
dockManager = new K3DockManager( this, new_name.toLatin1().constData() );
mainDockWidget = 0L;
setAttribute( Qt::WA_DeleteOnClose );
}
K3DockMainWindow::~K3DockMainWindow()
{
delete dockManager;
}
K3DockManager* K3DockMainWindow::manager() const
{
return dockManager;
}
void K3DockMainWindow::setMainDockWidget( K3DockWidget* mdw )
{
if ( mainDockWidget == mdw ) return;
mainDockWidget = mdw;
dockManager->setMainDockWidget2(mdw);
}
K3DockWidget* K3DockMainWindow::getMainDockWidget() const
{
return mainDockWidget;
}
void K3DockMainWindow::setView( QWidget *view )
{
if ( view->isA("K3DockWidget") ){
if ( view->parent() != this ) ((K3DockWidget*)view)->applyToWidget( this );
}
#ifndef NO_KDE2
KXmlGuiWindow::setCentralWidget(view);
#else
Q3MainWindow::setCentralWidget(view);
#endif
}
K3DockWidget* K3DockMainWindow::createDockWidget( const QString& name, const QPixmap &pixmap, QWidget* parent, const QString& strCaption, const QString& strTabPageLabel)
{
return new K3DockWidget( dockManager, name.toLatin1().constData(), pixmap, parent, strCaption, strTabPageLabel );
}
void K3DockMainWindow::activateDock()
{
dockManager->activate();
}
Q3PopupMenu* K3DockMainWindow::dockHideShowMenu() const
{
return dockManager->dockHideShowMenu();
}
void K3DockMainWindow::makeDockVisible( K3DockWidget* dock )
{
if ( dock )
dock->makeDockVisible();
}
void K3DockMainWindow::makeDockInvisible( K3DockWidget* dock )
{
if ( dock )
dock->undock();
}
void K3DockMainWindow::makeWidgetDockVisible( QWidget* widget )
{
makeDockVisible( dockManager->findWidgetParentDock(widget) );
}
void K3DockMainWindow::writeDockConfig(QDomElement &base)
{
dockManager->writeConfig(base);
}
void K3DockMainWindow::readDockConfig(QDomElement &base)
{
dockManager->readConfig(base);
}
#ifndef NO_KDE2
void K3DockMainWindow::writeDockConfig( KConfig* c, const QString &group )
{
dockManager->writeConfig( c, group );
}
void K3DockMainWindow::readDockConfig( KConfig* c, const QString &group )
{
dockManager->readConfig( c, group );
}
#endif
void K3DockMainWindow::slotDockWidgetUndocked()
{
QObject* pSender = (QObject*) sender();
if (!pSender->inherits("K3DockWidget")) return;
K3DockWidget* pDW = (K3DockWidget*) pSender;
emit dockWidgetHasUndocked( pDW);
}
/*************************************************************************/
K3DockWidgetAbstractHeaderDrag::K3DockWidgetAbstractHeaderDrag( K3DockWidgetAbstractHeader* parent, K3DockWidget* dock, const char* name )
:QFrame( parent, name )
{
dw = dock;
installEventFilter( dock->dockManager() );
}
K3DockWidgetAbstractHeaderDrag::~K3DockWidgetAbstractHeaderDrag()
{
}
K3DockWidget* K3DockWidgetAbstractHeaderDrag::dockWidget() const
{
return dw;
}
/*************************************************************************/
K3DockWidgetHeaderDrag::K3DockWidgetHeaderDrag( K3DockWidgetAbstractHeader* parent, K3DockWidget* dock, const char* name )
:K3DockWidgetAbstractHeaderDrag( parent, dock, name )
{
}
K3DockWidgetHeaderDrag::~K3DockWidgetHeaderDrag()
{
}
void K3DockWidgetHeaderDrag::paintEvent( QPaintEvent* )
{
QStylePainter paint;
paint.begin( this );
QStyleOption qso;
qso.initFrom( this );
paint.drawPrimitive( QStyle::PE_IndicatorToolBarHandle, qso );
paint.end();
}
/*************************************************************************/
K3DockWidgetAbstractHeader::K3DockWidgetAbstractHeader( K3DockWidget* parent, const char* name )
:QFrame( parent, name )
{
}
/*************************************************************************/
K3DockWidgetHeader::K3DockWidgetHeader( K3DockWidget* parent, const char* name )
:K3DockWidgetAbstractHeader( parent, name )
{
#ifdef BORDERLESS_WINDOWS
setCursor(QCursor(Qt::ArrowCursor));
#endif
d = new K3DockWidgetHeaderPrivate( this );
layout = new QHBoxLayout( this );
layout->setSizeConstraint( QLayout::Minimum );
drag = new K3DockWidgetHeaderDrag( this, parent );
// Auxillary pixmap (to create the dock buttons)
QPixmap auxPix;
closeButton = new K3DockButton_Private( this, "DockCloseButton" );
closeButton->setToolTip( i18n("Close") );
auxPix = style()->standardPixmap( QStyle::SP_DockWidgetCloseButton );
closeButton->setIcon( QIcon( auxPix ) );
closeButton->setFixedSize( auxPix.size() );
connect( closeButton, SIGNAL(clicked()), parent, SIGNAL(headerCloseButtonClicked()));
connect( closeButton, SIGNAL(clicked()), parent, SLOT(undock()));
stayButton = new K3DockButton_Private( this, "DockStayButton" );
stayButton->setToolTip( i18nc("Freeze the window geometry", "Freeze") );
stayButton->setCheckable( true );
auxPix = QPixmap( not_close_xpm );
stayButton->setIcon( QIcon( auxPix ) );
stayButton->setFixedSize( auxPix.size() );
connect( stayButton, SIGNAL(clicked()), this, SLOT(slotStayClicked()));
dockbackButton = new K3DockButton_Private( this, "DockbackButton" );
dockbackButton->setToolTip( i18nc("Dock this window", "Dock") );
auxPix = QPixmap( dockback_xpm );
dockbackButton->setIcon( QIcon( auxPix ) );
dockbackButton->setFixedSize( auxPix.size() );
connect( dockbackButton, SIGNAL(clicked()), parent, SIGNAL(headerDockbackButtonClicked()));
connect( dockbackButton, SIGNAL(clicked()), parent, SLOT(dockBack()));
d->toDesktopButton = new K3DockButton_Private( this, "ToDesktopButton" );
d->toDesktopButton->setToolTip( i18n("Detach") );
auxPix = QPixmap( todesktop_xpm );
d->toDesktopButton->setIcon( QIcon( auxPix ) );
d->toDesktopButton->setFixedSize( auxPix.size() );
connect( d->toDesktopButton, SIGNAL(clicked()), parent, SLOT(toDesktop()));
stayButton->hide();
d->dummy = new QWidget( this );
d->dummy->setFixedSize( 1, closeButton->height() );
layout->addWidget( drag );
layout->addWidget( dockbackButton );
layout->addWidget( d->toDesktopButton );
layout->addWidget( d->dummy);
layout->addWidget( stayButton );
layout->addWidget( closeButton );
layout->activate();
d->dummy->hide();
#if 1
drag->setFixedHeight( closeButton->height() );
#else
drag->setFixedHeight( layout->minimumSize().height() );
#endif
}
K3DockWidgetHeader::~K3DockWidgetHeader()
{
}
void K3DockWidgetHeader::setTopLevel( bool isTopLevel )
{
d->topLevel = isTopLevel;
if ( isTopLevel ){
K3DockWidget* par = (K3DockWidget*)parent();
if( par && par->isDockBackPossible() )
dockbackButton->show();
else
dockbackButton->hide();
stayButton->hide();
closeButton->hide();
d->toDesktopButton->hide();
drag->setEnabled( true );
} else {
dockbackButton->hide();
stayButton->hide();
if (!d->forceCloseButtonHidden) closeButton->show();
if( d->showToDesktopButton )
d->toDesktopButton->show();
}
layout->activate();
bool dontShowDummy=drag->isVisibleTo(this) || dockbackButton->isVisibleTo(this) ||
d->toDesktopButton->isVisibleTo(this) || stayButton->isVisibleTo(this) ||
closeButton->isVisibleTo(this);
for (Q3PtrListIterator<K3DockButton_Private> it( d->btns );it.current();++it) {
dontShowDummy=dontShowDummy || (it.current()->isVisibleTo(this));
}
if (dontShowDummy) d->dummy->hide(); else d->dummy->show();
updateGeometry();
}
void K3DockWidgetHeader::forceCloseButtonHidden(bool hidden) {
d->forceCloseButtonHidden=hidden;
if (hidden) closeButton->hide();
else closeButton->show();
}
K3DockWidgetHeaderDrag *K3DockWidgetHeader::dragPanel() {
return drag;
}
void K3DockWidgetHeader::setDragPanel( K3DockWidgetHeaderDrag* nd )
{
if ( !nd ) return;
delete layout;
layout = new QHBoxLayout( this );
layout->setSizeConstraint( QLayout::Minimum );
delete drag;
drag = nd;
if (drag->parentWidget()!=this) {
drag->setParent(this);
drag->move(0,0);
}
layout->addWidget( drag );
layout->addWidget( dockbackButton );
layout->addWidget( d->dummy );
layout->addWidget( d->toDesktopButton );
layout->addWidget( stayButton );
bool dontShowDummy=drag->isVisibleTo(this) || dockbackButton->isVisibleTo(this) ||
d->toDesktopButton->isVisibleTo(this) || stayButton->isVisibleTo(this) ||
closeButton->isVisibleTo(this);
for (Q3PtrListIterator<K3DockButton_Private> it( d->btns );it.current();++it) {
layout->addWidget(it.current());
dontShowDummy=dontShowDummy || (it.current()->isVisibleTo(this));
}
if (dontShowDummy) d->dummy->hide(); else d->dummy->show();
layout->addWidget( closeButton );
layout->activate();
kDebug(282)<<"KdockWidgetHeader::setDragPanel:minimum height="<<layout->minimumSize().height();
//FIXME somebody left this here, but we don't know what the hell it's for.
drag->setFixedHeight( closeButton->height()); // /*layout->minimumS*/sizeHint().height() );
}
void K3DockWidgetHeader::addButton(K3DockButton_Private* btn) {
if (!btn) return;
if (btn->parentWidget()!=this) {
btn->setParent(this);
}
btn->setFixedSize( closeButton->size() );
if (!d->btns.containsRef(btn)) d->btns.append(btn);
btn->show();
delete layout;
layout = new QHBoxLayout( this );
layout->setSizeConstraint( QLayout::Minimum );
layout->addWidget( drag );
layout->addWidget( dockbackButton );
layout->addWidget( d->toDesktopButton );
layout->addWidget( d->dummy);
layout->addWidget( stayButton );
bool dontShowDummy=drag->isVisibleTo(this) || dockbackButton->isVisibleTo(this) ||
d->toDesktopButton->isVisibleTo(this) || stayButton->isVisibleTo(this) ||
closeButton->isVisibleTo(this);
for (Q3PtrListIterator<K3DockButton_Private> it( d->btns );it.current();++it) {
layout->addWidget(it.current());
dontShowDummy=dontShowDummy || (it.current()->isVisibleTo(this));
}
if (dontShowDummy) d->dummy->hide(); else d->dummy->show();
layout->addWidget( closeButton );
layout->activate();
drag->setFixedHeight( layout->minimumSize().height() );
}
void K3DockWidgetHeader::removeButton(K3DockButton_Private* btn) {
if (btn->parentWidget()==this) {
if (d->btns.containsRef(btn)) d->btns.removeRef(btn);
delete btn;
}
}
void K3DockWidgetHeader::slotStayClicked()
{
setDragEnabled(!stayButton->isChecked());
}
bool K3DockWidgetHeader::dragEnabled() const
{
return drag->isEnabled();
}
void K3DockWidgetHeader::showUndockButton(bool show)
{
kDebug(282)<<"K3DockWidgetHeader::showUndockButton("<<show<<")";
if( d->showToDesktopButton == show )
return;
d->showToDesktopButton = show;
if( !show || d->topLevel )
d->toDesktopButton->hide( );
else
d->toDesktopButton->show( );
}
void K3DockWidgetHeader::setDragEnabled(bool b)
{
stayButton->setChecked(!b);
closeButton->setEnabled(b);
drag->setEnabled(b);
}
#ifndef NO_KDE2
void K3DockWidgetHeader::saveConfig( KConfigGroup* cg )
{
cg->writeEntry( QString("%1:stayButton").arg(parent()->name()), stayButton->isChecked() );
}
void K3DockWidgetHeader::loadConfig( KConfigGroup* cg )
{
setDragEnabled( !cg->readEntry( QString("%1:stayButton").arg(parent()->name()), false ) );
}
#endif
/*************************************************************************/
class K3DockManager::K3DockManagerPrivate
{
public:
/**
* This rectangle is used to highlight the current dockposition. It stores global screen coordinates.
*/
QRect dragRect;
/**
* This rectangle is used to erase the previously highlighted dockposition. It stores global screen coordinates.
*/
QRect oldDragRect;
/**
* This flag stores the information if dragging is ready to start. Used between mousePress and mouseMove event.
*/
bool readyToDrag;
/**
* This variable stores the offset of the mouse cursor to the upper left edge of the current drag widget.
*/
QPoint dragOffset;
/**
* These flags store information about the splitter behavior
*/
bool splitterOpaqueResize;
bool splitterKeepSize;
bool splitterHighResolution;
QPointer<K3DockWidget> mainDockWidget;
QList<QObject*> containerDocks;
QPointer<K3DockWidget> leftContainer;
QPointer<K3DockWidget> topContainer;
QPointer<K3DockWidget> rightContainer;
QPointer<K3DockWidget> bottomContainer;
int m_readDockConfigMode;
};
/*************************************************************************/
-K3DockWidget::K3DockWidget( K3DockManager* dockManager, const char* name, const QPixmap &pixmap, QWidget* parent, const QString& strCaption, const QString& strTabPageLabel, Qt::WFlags f)
+K3DockWidget::K3DockWidget( K3DockManager* dockManager, const char* name, const QPixmap &pixmap, QWidget* parent, const QString& strCaption, const QString& strTabPageLabel, Qt::WindowFlags f)
#ifdef BORDERLESS_WINDOWS
: QWidget( parent, name, f )//| WType_Dialog | WStyle_Customize | WStyle_NoBorder )
#else
: QWidget( parent, name, f )
#endif
,formerBrotherDockWidget(0L)
,currentDockPos(DockNone)
,formerDockPos(DockNone)
,widget(0L)
,pix(new QPixmap(pixmap))
,prevSideDockPosBeforeDrag(DockNone)
,isGroup(false)
{
d = new K3DockWidgetPrivate(); // create private data
d->_parent = parent;
layout = new QVBoxLayout( this );
layout->setSizeConstraint( QLayout::Minimum );
manager = dockManager;
manager->childDock->append( this );
installEventFilter( manager );
eDocking = DockFullDocking;
sDocking = DockFullSite;
header = 0L;
setHeader( new K3DockWidgetHeader( this, "AutoCreatedDockHeader" ) );
if( strCaption.isNull() )
setWindowTitle( name );
else
setWindowTitle( strCaption);
if( strTabPageLabel == " ")
setTabPageLabel( windowTitle());
else
setTabPageLabel( strTabPageLabel);
isTabGroup = false;
d->isContainer =false;
setIcon( pixmap);
widget = 0L;
QObject::connect(this, SIGNAL(hasUndocked()), manager->main, SLOT(slotDockWidgetUndocked()) );
applyToWidget( parent, QPoint(0,0) );
}
void K3DockWidget::setPixmap(const QPixmap& pixmap) {
delete pix;
pix=new QPixmap(pixmap);
setIcon(*pix);
K3DockTabGroup *dtg=parentDockTabGroup();
if (dtg)
dtg->changeTab(this,pixmap,dtg->tabLabel(this));
QWidget *contWid=parentDockContainer();
if (contWid) {
K3DockContainer *x = dynamic_cast<K3DockContainer*>(contWid);
if (x) {
x->setPixmap(this,pixmap);
}
}
}
const QPixmap& K3DockWidget::pixmap() const {
return *pix;
}
K3DockWidget::~K3DockWidget()
{
d->pendingDtor = true;
if ( !manager->undockProcess ){
d->blockHasUndockedSignal = true;
undock();
d->blockHasUndockedSignal = false;
}
if (latestK3DockContainer()) {
K3DockContainer *x = dynamic_cast<K3DockContainer*>(latestK3DockContainer());
if (x) {
x->removeWidget(this);
}
}
emit iMBeingClosed();
if (manager->d) manager->d->containerDocks.removeAll(this);
manager->childDock->removeAll( this );
delete pix;
delete d; // destroy private data
d=0;
}
void K3DockWidget::paintEvent(QPaintEvent* pe)
{
QWidget::paintEvent(pe);
QPainter paint;
paint.begin( this );
QStyleOption option;
option.init(this);
option.rect = QRect( 0, 0, width(), height() );
style()->drawPrimitive (QStyle::PE_FrameDockWidget, &option, &paint, this);
paint.end();
}
void K3DockWidget::leaveEvent(QEvent *e)
{
QWidget::leaveEvent(e);
#ifdef BORDERLESS_WINDOWS
if (parent()) return;
// setCursor(QCursor(ArrowCursor));
#endif
}
void K3DockWidget::mousePressEvent(QMouseEvent* mme)
{
#ifdef BORDERLESS_WINDOWS
if (!parent())
{
kDebug(282)<<"K3DockWidget::mousePressEvent";
bool bbottom;
bool bleft;
bool bright;
bool btop;
int styleheight;
QPoint mp;
mp=mme->pos();
styleheight=2*style().pixelMetric(QStyle::PM_DefaultFrameWidth,this);
bbottom=mp.y()>=height()-styleheight;
btop=mp.y()<=styleheight;
bleft=mp.x()<=styleheight;
bright=mp.x()>=width()-styleheight;
kDebug(282)<<"mousemovevent";
d->resizing=true;
if (bright)
{
if (btop)
{
d->resizeMode=K3DockWidgetPrivate::ResizeTopRight;
d->resizePos=QPoint(width(),0)-mme->pos();
}
else
{
d->resizePos=QPoint(width(),height())-mme->pos();
if (bbottom) d->resizeMode=K3DockWidgetPrivate::ResizeBottomRight;
else d->resizeMode=K3DockWidgetPrivate::ResizeRight;
}
}
else if (bleft)
{
if (btop) setCursor(QCursor(Qt::SizeFDiagCursor));
else
if (bbottom) setCursor(QCursor(Qt::SizeBDiagCursor));
else setCursor(QCursor(Qt::SizeHorCursor));
}
else
if (bbottom)
{
d->resizeMode=K3DockWidgetPrivate::ResizeBottom;
d->resizePos=QPoint(0,height())-mme->pos();
}
else
if (btop) setCursor(QCursor(Qt::SizeVerCursor));
else d->resizing=false;
if (d->resizing) grabMouse(cursor());
}
#endif
QWidget::mousePressEvent(mme);
}
void K3DockWidget::mouseReleaseEvent(QMouseEvent* ev)
{
#ifdef BORDERLESS_WINDOWS
d->resizing=false;
releaseMouse();
#endif
QWidget::mouseReleaseEvent(ev);
}
void K3DockWidget::mouseMoveEvent(QMouseEvent* mme)
{
QWidget::mouseMoveEvent(mme);
#ifdef BORDERLESS_WINDOWS
if (parent()) return;
if (d->resizing)
{
switch (d->resizeMode)
{
case K3DockWidgetPrivate::ResizeRight:
resize(mme->pos().x()+d->resizePos.x(),height());
break;
case K3DockWidgetPrivate::ResizeBottomRight:
resize(mme->pos().x()+d->resizePos.x(),mme->pos().y()+d->resizePos.y());
break;
case K3DockWidgetPrivate::ResizeBottom:
resize(width(),mme->pos().y()+d->resizePos.y());
break;
default:
break;
}
return;
}
bool bbottom;
bool bleft;
bool bright;
bool btop;
int styleheight;
QPoint mp;
mp=mme->pos();
styleheight=2*style().pixelMetric(QStyle::PM_DefaultFrameWidth,this);
bbottom=mp.y()>=height()-styleheight;
btop=mp.y()<=styleheight;
bleft=mp.x()<=styleheight;
bright=mp.x()>=width()-styleheight;
kDebug(282)<<"mousemovevent";
if (bright)
{
if (btop) setCursor(QCursor(Qt::SizeBDiagCursor));
else
if (bbottom) setCursor(QCursor(Qt::SizeFDiagCursor));
else setCursor(QCursor(Qt::SizeHorCursor));
}
else if (bleft)
{
if (btop) setCursor(QCursor(Qt::SizeFDiagCursor));
else
if (bbottom) setCursor(QCursor(Qt::SizeBDiagCursor));
else setCursor(QCursor(Qt::SizeHorCursor));
}
else
if (bbottom || btop) setCursor(QCursor(Qt::SizeVerCursor));
else setCursor(QCursor(Qt::ArrowCursor));
#endif
}
void K3DockWidget::setLatestK3DockContainer(QWidget* container)
{
if (container)
{
if (dynamic_cast<K3DockContainer*>(container))
d->container=container;
else
d->container=0;
}
}
QWidget* K3DockWidget::latestK3DockContainer()
{
if (!(d->container)) return 0;
if (dynamic_cast<K3DockContainer*>(d->container.operator->())) return d->container;
return 0;
}
K3DockWidgetAbstractHeader *K3DockWidget::getHeader() {
return header;
}
void K3DockWidget::setHeader( K3DockWidgetAbstractHeader* h )
{
if ( !h ) return;
if ( header ){
delete header;
delete layout;
header = h;
layout = new QVBoxLayout( this );
layout->setSizeConstraint( QLayout::Minimum );
layout->addWidget( header );
setWidget( widget );
} else {
header = h;
layout->addWidget( header );
}
// kDebug(282)<<windowTitle()<<": K3DockWidget::setHeader";
setEnableDocking(eDocking);
}
void K3DockWidget::setEnableDocking( int pos )
{
eDocking = pos;
if( header ) {
if ( header->inherits( "K3DockWidgetHeader" ) )
( ( K3DockWidgetHeader* ) header )->showUndockButton( pos & DockDesktop );
updateHeader();
}
}
int K3DockWidget::enableDocking() const
{
return eDocking;
}
void K3DockWidget::setDockSite( int pos )
{
sDocking = pos;
}
int K3DockWidget::dockSite() const
{
return sDocking;
}
void K3DockWidget::updateHeader()
{
if ( parent() ){
#ifdef BORDERLESS_WINDOWS
layout->setMargin(0);
setMouseTracking(false);
setCursor(QCursor(Qt::ArrowCursor));
#endif
if ( (parent() == manager->main) || isGroup || (eDocking == K3DockWidget::DockNone) ){
header->hide();
} else {
header->setTopLevel( false );
if (widget && dynamic_cast<K3DockContainer*>(widget))
header->hide();
else
header->show();
}
} else {
header->setTopLevel( true );
header->show();
#ifdef BORDERLESS_WINDOWS
layout->setMargin(2*style().pixelMetric(QStyle::PM_DefaultFrameWidth,this));
setMouseTracking(true);
#endif
}
}
void K3DockWidget::applyToWidget( QWidget* s, const QPoint& p )
{
if ( parent() != s )
{
hide();
setParent(s);
move(0,0);
}
if ( s && s->inherits("K3DockMainWindow") ){
((K3DockMainWindow*)s)->setView( this );
}
if ( manager && s == manager->main ){
setGeometry( QRect(QPoint(0,0), manager->main->geometry().size()) );
}
if ( !s )
{
move(p);
#ifndef NO_KDE2
#if HAVE_X11
if (d->transient && d->_parent)
XSetTransientForHint( QX11Info::display(), winId(), d->_parent->winId() );
#ifdef BORDERLESS_WINDOWS
KWindowSystem::setType( winId(), NET::Override); //d->windowType );
// setWFlags(WStyle_Customize | WStyle_NoBorder | WStyle_Tool);
#else
KWindowSystem::setType( winId(), d->windowType );
#endif // BORDERLESS_WINDOW
#endif // HAVE_X11
#endif
}
updateHeader();
setIcon(*pix);
}
void K3DockWidget::show()
{
if ( parent() || manager->main->isVisible() ) {
if ( !parent() ){
emit manager->setDockDefaultPos( this );
emit setDockDefaultPos();
if ( parent() ){
makeDockVisible();
} else {
QWidget::show();
}
} else {
QWidget::show();
}
}
}
#ifndef NO_KDE2
void K3DockWidget::setDockWindowType (NET::WindowType windowType)
{
d->windowType = windowType;
applyToWidget( parentWidget(), QPoint(0,0) );
}
#endif
void K3DockWidget::setDockWindowTransient (QWidget *parent, bool transientEnabled)
{
d->_parent = parent;
d->transient = transientEnabled;
applyToWidget( parentWidget(), QPoint(0,0) );
}
QWidget *K3DockWidget::transientTo() {
if (d->transient && d->_parent) return d->_parent; else return 0;
}
bool K3DockWidget::event( QEvent *event )
{
switch ( event->type() )
{
#undef FocusIn
case QEvent::FocusIn:
if (widget && !d->pendingFocusInEvent) {
d->pendingFocusInEvent = true;
widget->setFocus();
}
d->pendingFocusInEvent = false;
break;
case QEvent::ChildRemoved:
if ( widget == ((QChildEvent*)event)->child() ) widget = 0L;
break;
case QEvent::Show:
if ( widget ) widget->show();
emit manager->change();
break;
case QEvent::Hide:
if ( widget ) widget->hide();
emit manager->change();
break;
case QEvent::WindowTitleChange:
if ( parentWidget() ){
if ( parent()->inherits("K3DockSplitter") ){
((K3DockSplitter*)(parent()))->updateName();
}
if ( parentDockTabGroup() ){
setDockTabName( parentDockTabGroup() );
parentDockTabGroup()->setTabLabel( this, tabPageLabel() );
}
}
break;
case QEvent::Close:
emit iMBeingClosed();
break;
default:
break;
}
return QWidget::event( event );
}
K3DockWidget *K3DockWidget::findNearestDockWidget(DockPosition pos)
{
if (!parent()) return 0;
if (!parent()->inherits("K3DockSplitter")) return 0;
Qt::Orientation orientation= ( ( pos == K3DockWidget::DockLeft) ||
( pos == K3DockWidget::DockRight ) ) ? Qt::Vertical : Qt::Horizontal;
if (((K3DockSplitter*)(parent()))->orientation() == orientation)
{
K3DockWidget *neighbor=
( ( pos == K3DockWidget::DockLeft ) || ( pos == K3DockWidget::DockTop ) ) ?
static_cast<K3DockWidget*>( ( ( K3DockSplitter* )( parent() ) )->getFirst() ) :
static_cast<K3DockWidget*>( ( ( K3DockSplitter* )( parent() ) )->getLast() );
if (neighbor==this)
return (static_cast<K3DockWidget*>(parent()->parent())->findNearestDockWidget(pos));
else
if (neighbor->getWidget() && qobject_cast<K3DockTabGroup*>(neighbor->getWidget()))
return (K3DockWidget*)(((K3DockTabGroup*)neighbor->getWidget())->page(0));
else
return neighbor;
}
else
return (static_cast<K3DockWidget*>(parent()->parent())->findNearestDockWidget(pos));
return 0;
}
K3DockWidget* K3DockWidget::manualDock( K3DockWidget* target, DockPosition dockPos, int spliPos, QPoint pos, bool check, int tabIndex )
{
if (this == target)
return 0L; // docking to itself not possible
// kDebug(282)<<"manualDock called ";
bool success = true; // tested flag
// Check to make sure that we can dock in to the position wee
// were told to dock in to
if ( !(eDocking & (int)dockPos) ){
success = false;
// kDebug(282)<<"K3DockWidget::manualDock(): success = false (1)";
}
// fix for apps which use a value > 100%. The splitter position must be between 0..100
// The old behavior on high resolution was 0..10000. So likely the value is >100.
if (spliPos > 100) {
spliPos = spliPos / 100;
kDebug(282) << "K3DockWidget::manualDock(): fix splitter position: " << spliPos;
}
K3DockWidget *tmpTarget = 0;
switch (dockPos) {
case K3DockWidget::DockLeft:
tmpTarget=dockManager()->d->leftContainer;
break;
case K3DockWidget::DockRight:
tmpTarget=dockManager()->d->rightContainer;
break;
case K3DockWidget::DockBottom:
tmpTarget=dockManager()->d->bottomContainer;
break;
case K3DockWidget::DockTop:
tmpTarget=dockManager()->d->topContainer;
break;
default:
tmpTarget = 0;
}
//If we're not the target, and the target is our dock manager's main window
//dock into the temp target as chosen above
if ( tmpTarget && target && this != tmpTarget && target == dockManager()->d->mainDockWidget )
return manualDock(tmpTarget,DockCenter,spliPos,pos,check,tabIndex);
// check if the target allows us to oock into the requested position
if ( target && !(target->sDocking & (int)dockPos) ){
success = false;
// kDebug(282)<<"K3DockWidget::manualDock(): success = false (2)";
}
/* if we have a parent, and it's not a K3DockSplitter, and we don't have a parent
* dock tab group, and our parent isn't a K3DockContainer, and we have no explicit
* parent dock container...we can't do much yet */
if ( parent() && !parent()->inherits("K3DockSplitter") && !parentDockTabGroup() &&
!(dynamic_cast<K3DockContainer*>(parent())) && !parentDockContainer()){
// kDebug(282)<<"K3DockWidget::manualDock(): success = false (3)";
// kDebug(282)<<parent()->name();
success = false;
}
/* If all of our attempts to dock back so far have failed and we have a target
* and we're not being called recursively (i guess that's what check is for)
* then attempt to dock back to ourselves. */
if ( !success ){
// try to make another manualDock
K3DockWidget* dock_result = 0L;
if ( target && !check ){
K3DockWidget::DockPosition another__dockPos = K3DockWidget::DockNone;
switch ( dockPos ){
case K3DockWidget::DockLeft : another__dockPos = K3DockWidget::DockRight ; break;
case K3DockWidget::DockRight : another__dockPos = K3DockWidget::DockLeft ; break;
case K3DockWidget::DockTop : another__dockPos = K3DockWidget::DockBottom; break;
case K3DockWidget::DockBottom: another__dockPos = K3DockWidget::DockTop ; break;
default: break;
}
dock_result = target->manualDock( this, another__dockPos, spliPos, pos, true, tabIndex );
}
return dock_result;
}
// end check block
d->blockHasUndockedSignal = true;
undock();
d->blockHasUndockedSignal = false;
//we have no docking target, so we're undocking
if ( !target ){
move( pos );
show();
emit manager->change();
return this;
}
// kDebug(282)<<"Looking for K3DockTabGroup";
K3DockTabGroup* parentTab = target->parentDockTabGroup();
if ( parentTab ){
// add to existing TabGroup
applyToWidget( parentTab );
parentTab->insertTab( this, icon() ? *icon() : QPixmap(),
tabPageLabel(), tabIndex );
QWidget *wantTransient=parentTab->transientTo();
target->setDockWindowTransient(wantTransient,wantTransient);
setDockTabName( parentTab );
if( !toolTipStr.isEmpty())
parentTab->setTabToolTip( this, toolTipStr);
currentDockPos = K3DockWidget::DockCenter;
emit manager->change();
return (K3DockWidget*)parentTab->parent();
}
else
{
// kDebug(282)<<"Looking for K3DockContainer";
QWidget *contWid=target->parentDockContainer();
if (!contWid) contWid=target->widget;
if (contWid)
{
K3DockContainer *cont=dynamic_cast<K3DockContainer*>(contWid);
if (cont)
{
if (latestK3DockContainer() && (latestK3DockContainer()!=contWid)) {
K3DockContainer* dc = dynamic_cast<K3DockContainer*>(latestK3DockContainer());
if (dc) {
dc->removeWidget(this);
}
}
// kDebug(282)<<"K3DockContainerFound";
applyToWidget( contWid );
cont->insertWidget( this, icon() ? *icon() : QPixmap(),
tabPageLabel(), tabIndex );
setLatestK3DockContainer(contWid);
// setDockTabName( parentTab );
if( !toolTipStr.isEmpty())
cont->setToolTip( this, toolTipStr);
currentDockPos = K3DockWidget::DockCenter;
emit manager->change();
return (K3DockWidget*)(cont->parentDockWidget());
}
}
}
// create a new dockwidget that will contain the target and this
QWidget* parentDock = target->parentWidget();
K3DockWidget* newDock = new K3DockWidget( manager, "tempName", QPixmap(""), parentDock );
newDock->currentDockPos = target->currentDockPos;
if ( dockPos == K3DockWidget::DockCenter ){
newDock->isTabGroup = true;
} else {
newDock->isGroup = true;
}
newDock->eDocking = (target->eDocking & eDocking) & (~(int)K3DockWidget::DockCenter);
newDock->applyToWidget( parentDock );
if ( !parentDock ){
// dock to a toplevel dockwidget means newDock is toplevel now
newDock->move( target->frameGeometry().topLeft() );
newDock->resize( target->geometry().size() );
if ( target->isVisibleToTLW() ) newDock->show();
}
// redirect the dockback button to the new dockwidget
if( target->formerBrotherDockWidget ) {
newDock->setFormerBrotherDockWidget(target->formerBrotherDockWidget);
if( formerBrotherDockWidget )
target->loseFormerBrotherDockWidget();
}
newDock->formerDockPos = target->formerDockPos;
// HERE SOMETING CREATING CONTAINERS SHOULD BE ADDED !!!!!
if ( dockPos == K3DockWidget::DockCenter )
{
K3DockTabGroup* tab = new K3DockTabGroup( newDock );
tab->setObjectName( QLatin1String( "_dock_tab" ) );
QObject::connect(tab, SIGNAL(currentChanged(QWidget*)), d, SLOT(slotFocusEmbeddedWidget(QWidget*)));
newDock->setWidget( tab );
target->applyToWidget( tab );
applyToWidget( tab );
tab->insertTab( target, target->icon() ? *(target->icon()) : QPixmap(),
target->tabPageLabel() );
if( !target->toolTipString().isEmpty())
tab->setTabToolTip( target, target->toolTipString());
tab->insertTab( this, icon() ? *icon() : QPixmap(),
tabPageLabel(), tabIndex );
QRect geom=newDock->geometry();
QWidget *wantTransient=tab->transientTo();
newDock->setDockWindowTransient(wantTransient,wantTransient);
newDock->setGeometry(geom);
if( !toolTipString().isEmpty())
tab->setTabToolTip( this, toolTipString());
setDockTabName( tab );
tab->show();
currentDockPos = DockCenter;
target->formerDockPos = target->currentDockPos;
target->currentDockPos = DockCenter;
}
else {
// if to dock not to the center of the target dockwidget,
// dock to newDock
K3DockSplitter* panner = 0L;
if ( dockPos == K3DockWidget::DockTop || dockPos == K3DockWidget::DockBottom ) panner = new K3DockSplitter( newDock, "_dock_split_", Qt::Horizontal, spliPos );
if ( dockPos == K3DockWidget::DockLeft || dockPos == K3DockWidget::DockRight ) panner = new K3DockSplitter( newDock, "_dock_split_", Qt::Vertical , spliPos );
newDock->setWidget( panner );
panner->setOpaqueResize(manager->splitterOpaqueResize());
panner->setKeepSize(manager->splitterKeepSize());
panner->setFocusPolicy( Qt::NoFocus );
target->applyToWidget( panner );
applyToWidget( panner );
target->formerDockPos = target->currentDockPos;
if ( dockPos == K3DockWidget::DockRight) {
panner->activate( target, this );
currentDockPos = K3DockWidget::DockRight;
target->currentDockPos = K3DockWidget::DockLeft;
}
else if( dockPos == K3DockWidget::DockBottom) {
panner->activate( target, this );
currentDockPos = K3DockWidget::DockBottom;
target->currentDockPos = K3DockWidget::DockTop;
}
else if( dockPos == K3DockWidget::DockTop) {
panner->activate( this, target );
currentDockPos = K3DockWidget::DockTop;
target->currentDockPos = K3DockWidget::DockBottom;
}
else if( dockPos == K3DockWidget::DockLeft) {
panner->activate( this, target );
currentDockPos = K3DockWidget::DockLeft;
target->currentDockPos = K3DockWidget::DockRight;
}
target->show();
show();
panner->show();
}
if ( parentDock ){
if ( parentDock->inherits("K3DockSplitter") ){
K3DockSplitter* sp = (K3DockSplitter*)parentDock;
sp->deactivate();
if ( sp->getFirst() == target )
sp->activate( newDock, 0L );
else
sp->activate( 0L, newDock );
}
}
newDock->show();
emit target->docking( this, dockPos );
emit manager->replaceDock( target, newDock );
emit manager->change();
return newDock;
}
K3DockTabGroup* K3DockWidget::parentDockTabGroup() const
{
if ( !parent() ) return 0L;
QWidget* candidate = parentWidget()->parentWidget();
if ( candidate && candidate->inherits("K3DockTabGroup") ) return (K3DockTabGroup*)candidate;
return 0L;
}
QWidget *K3DockWidget::parentDockContainer() const
{
if (!parent()) return 0L;
QWidget* candidate = parentWidget()->parentWidget();
if (candidate && dynamic_cast<K3DockContainer*>(candidate)) return candidate;
return 0L;
}
void K3DockWidget::setForcedFixedWidth(int w)
{
d->forcedWidth=w;
setFixedWidth(w);
if (!parent()) return;
if (parent()->inherits("K3DockSplitter"))
qobject_cast<K3DockSplitter*>(parent())->setForcedFixedWidth(this,w);
}
void K3DockWidget::setForcedFixedHeight(int h)
{
d->forcedHeight=h;
setFixedHeight(h);
if (!parent()) return;
if (parent()->inherits("K3DockSplitter"))
qobject_cast<K3DockSplitter*>(parent())->setForcedFixedHeight(this,h);
}
int K3DockWidget::forcedFixedWidth()
{
return d->forcedWidth;
}
int K3DockWidget::forcedFixedHeight()
{
return d->forcedHeight;
}
void K3DockWidget::restoreFromForcedFixedSize()
{
d->forcedWidth=-1;
d->forcedHeight=-1;
setMinimumWidth(0);
setMaximumWidth(32000);
setMinimumHeight(0);
setMaximumHeight(32000);
if (!parent()) return;
if (parent()->inherits("K3DockSplitter"))
qobject_cast<K3DockSplitter*>(parent())->restoreFromForcedFixedSize(this);
}
void K3DockWidget::toDesktop()
{
QPoint p = mapToGlobal( QPoint( -30, -30 ) );
if( p.x( ) < 0 )
p.setX( 0 );
if( p.y( ) < 0 )
p.setY( 0 );
manualDock( 0, DockDesktop, 50, p );
}
K3DockWidget::DockPosition K3DockWidget::currentDockPosition() const
{
return currentDockPos;
}
void K3DockWidget::undock()
{
// kDebug(282) << "K3DockWidget::undock : undocking " << name();
manager->d->dragRect = QRect ();
manager->drawDragRectangle ();
QWidget* parentW = parentWidget();
if ( !parentW ){
hide();
if (!d->blockHasUndockedSignal)
emit hasUndocked();
return;
}
formerDockPos = currentDockPos;
currentDockPos = K3DockWidget::DockDesktop;
manager->blockSignals(true);
manager->undockProcess = true;
bool isV = parentW->isVisibleToTLW();
//UNDOCK HAS TO BE IMPLEMENTED CORRECTLY :)
K3DockTabGroup* parentTab = parentDockTabGroup();
if ( parentTab ){
d->index = parentTab->indexOf( this); // memorize the page position in the tab widget
parentTab->removePage( this );
/*
QWidget *wantTransient=parentTab->transientTo();
target->setDockWindowTransient(wantTransient,wantTransient);
*/
setFormerBrotherDockWidget((K3DockWidget*)parentTab->page(0));
applyToWidget( 0L );
if ( parentTab->count() == 1 ){
// last subdock widget in the tab control
K3DockWidget* lastTab = (K3DockWidget*)parentTab->page(0);
parentTab->removePage( lastTab );
/* QWidget *wantTransient=parentTab->transientTo();
target->setDockWindowTransient(wantTransient,wantTransient);*/
lastTab->applyToWidget( 0L );
lastTab->move( parentTab->mapToGlobal(parentTab->frameGeometry().topLeft()) );
// K3DockTabGroup always have a parent that is a K3DockWidget
K3DockWidget* parentOfTab = (K3DockWidget*)parentTab->parent();
delete parentTab; // K3DockTabGroup
QWidget* parentOfDockWidget = parentOfTab->parentWidget();
if ( !parentOfDockWidget ){
if ( isV ) lastTab->show();
} else {
if ( parentOfDockWidget->inherits("K3DockSplitter") ){
K3DockSplitter* split = (K3DockSplitter*)parentOfDockWidget;
lastTab->applyToWidget( split );
split->deactivate();
if ( split->getFirst() == parentOfTab ){
split->activate( lastTab );
if ( ((K3DockWidget*)split->parent())->splitterOrientation == Qt::Vertical )
emit ((K3DockWidget*)split->getAnother(parentOfTab))->docking( parentOfTab, K3DockWidget::DockLeft );
else
emit ((K3DockWidget*)split->getAnother(parentOfTab))->docking( parentOfTab, K3DockWidget::DockTop );
} else {
split->activate( 0L, lastTab );
if ( ((K3DockWidget*)split->parent())->splitterOrientation == Qt::Vertical )
emit ((K3DockWidget*)split->getAnother(parentOfTab))->docking( parentOfTab, K3DockWidget::DockRight );
else
emit ((K3DockWidget*)split->getAnother(parentOfTab))->docking( parentOfTab, K3DockWidget::DockBottom );
}
split->show();
} else {
lastTab->applyToWidget( parentOfDockWidget );
}
lastTab->show();
}
manager->blockSignals(false);
emit manager->replaceDock( parentOfTab, lastTab );
lastTab->currentDockPos = parentOfTab->currentDockPos;
emit parentOfTab->iMBeingClosed();
manager->blockSignals(true);
delete parentOfTab;
} else {
setDockTabName( parentTab );
}
} else {
/*********************************************************************************************/
//QWidget* containerWidget = (QWidget*)parent();
bool undockedFromContainer=false;
if (d->container)
{
// kDebug(282)<<"undocked from dockcontainer";
undockedFromContainer=true;
K3DockContainer* dc = dynamic_cast<K3DockContainer*>(d->container.operator->());
if (dc) {
dc->undockWidget(this);
setFormerBrotherDockWidget(dc->parentDockWidget());
}
applyToWidget( 0L );
}
if (!undockedFromContainer) {
/*********************************************************************************************/
if ( parentW->inherits("K3DockSplitter") ){
K3DockSplitter* parentSplitterOfDockWidget = (K3DockSplitter*)parentW;
d->splitPosInPercent = parentSplitterOfDockWidget->separatorPosInPercent();
K3DockWidget* secondWidget = (K3DockWidget*)parentSplitterOfDockWidget->getAnother( this );
K3DockWidget* group = (K3DockWidget*)parentSplitterOfDockWidget->parentWidget();
setFormerBrotherDockWidget(secondWidget);
applyToWidget( 0L );
group->hide();
if ( !group->parentWidget() ){
secondWidget->applyToWidget( 0L, group->frameGeometry().topLeft() );
secondWidget->resize( group->width(), group->height() );
} else {
QWidget* obj = group->parentWidget();
secondWidget->applyToWidget( obj );
if ( obj->inherits("K3DockSplitter") ){
K3DockSplitter* parentOfGroup = (K3DockSplitter*)obj;
parentOfGroup->deactivate();
if ( parentOfGroup->getFirst() == group )
parentOfGroup->activate( secondWidget );
else
parentOfGroup->activate( 0L, secondWidget );
}
}
secondWidget->currentDockPos = group->currentDockPos;
secondWidget->formerDockPos = group->formerDockPos;
delete parentSplitterOfDockWidget;
manager->blockSignals(false);
emit manager->replaceDock( group, secondWidget );
emit group->iMBeingClosed();
manager->blockSignals(true);
delete group;
if ( isV ) secondWidget->show();
} else {
if (!d->pendingDtor) {
// don't reparent in the dtor of this
applyToWidget( 0L );
}
}
/*********************************************************************************************/
}
}
manager->blockSignals(false);
if (!d->blockHasUndockedSignal)
emit manager->change();
manager->undockProcess = false;
if (!d->blockHasUndockedSignal)
emit hasUndocked();
}
void K3DockWidget::setWidget( QWidget* mw )
{
if ( !mw ) return;
if ( mw->parent() != this ){
mw->setParent(this);
mw->move(0,0);
}
#ifdef BORDERLESS_WINDOWS
if (!mw->ownCursor()) mw->setCursor(QCursor(Qt::ArrowCursor));
#endif
widget = mw;
delete layout;
/*
* Qt 4.1 does not fill the background if not asked so explicitly.
* However code from Qt3/KDE3 could except that the background is filled.
*/
widget->setAutoFillBackground( true );
layout = new QVBoxLayout( this );
layout->setSizeConstraint( QLayout::Minimum );
K3DockContainer* dc = dynamic_cast<K3DockContainer*>(widget);
if (dc)
{
d->isContainer=true;
manager->d->containerDocks.append(this);
}
else
{
d->isContainer=false;
}
{
header->show();
layout->addWidget( header );
layout->addWidget( widget,1 );
}
updateHeader();
emit widgetSet(mw);
}
QWidget* K3DockWidget::getWidget() const
{
return widget;
}
void K3DockWidget::setDockTabName( K3DockTabGroup* tab )
{
QString listOfName;
QString listOfCaption;
for ( int i = 0; i < tab->count(); ++i ) {
QWidget *w = tab->page( i );
listOfCaption.append( w->windowTitle() ).append(",");
listOfName.append( w->name() ).append(",");
}
listOfCaption.remove( listOfCaption.length()-1, 1 );
listOfName.remove( listOfName.length()-1, 1 );
tab->parentWidget()->setName( listOfName.toUtf8() );
tab->parentWidget()->setWindowTitle( listOfCaption );
tab->parentWidget()->repaint(); // K3DockWidget->repaint
if ( tab->parentWidget()->parent() )
if ( tab->parentWidget()->parent()->inherits("K3DockSplitter") )
((K3DockSplitter*)(tab->parentWidget()->parent()))->updateName();
}
bool K3DockWidget::mayBeHide() const
{
bool f = (parent() != manager->main);
return ( !isGroup && !isTabGroup && f && isVisible() && ( eDocking != (int)K3DockWidget::DockNone ) );
}
bool K3DockWidget::mayBeShow() const
{
bool f = (parent() != manager->main);
return ( !isGroup && !isTabGroup && f && !isVisible() );
}
K3DockManager* K3DockWidget::dockManager() const
{
return manager;
}
void K3DockWidget::setToolTipString(const QString& ttStr)
{
toolTipStr = ttStr;
}
const QString& K3DockWidget::toolTipString() const
{
return toolTipStr;
}
void K3DockWidget::changeHideShowState()
{
if ( mayBeHide() ){
undock();
return;
}
if ( mayBeShow() ){
if ( manager->main->inherits("K3DockMainWindow") ){
((K3DockMainWindow*)manager->main)->makeDockVisible(this);
} else {
makeDockVisible();
}
}
}
void K3DockWidget::makeDockVisible()
{
if ( parentDockTabGroup() ){
parentDockTabGroup()->showPage( this );
}
if (parentDockContainer()) {
QWidget *contWid=parentDockContainer();
K3DockContainer *x = dynamic_cast<K3DockContainer*>(contWid);
if (x) {
x->showWidget(this);
}
}
if ( isVisible() ) return;
QWidget* p = parentWidget();
while ( p ){
if ( !p->isVisible() )
p->show();
p = p->parentWidget();
}
if( !parent() ) // is undocked
dockBack();
show();
}
void K3DockWidget::setFormerBrotherDockWidget(K3DockWidget *dockWidget)
{
formerBrotherDockWidget = dockWidget;
if( formerBrotherDockWidget )
QObject::connect( formerBrotherDockWidget, SIGNAL(iMBeingClosed()),
this, SLOT(loseFormerBrotherDockWidget()) );
}
void K3DockWidget::loseFormerBrotherDockWidget()
{
if( formerBrotherDockWidget )
QObject::disconnect( formerBrotherDockWidget, SIGNAL(iMBeingClosed()),
this, SLOT(loseFormerBrotherDockWidget()) );
formerBrotherDockWidget = 0L;
repaint();
}
void K3DockWidget::dockBack()
{
if( formerBrotherDockWidget) {
// search all children if it tries to dock back to a child
bool found = false;
QList<K3DockWidget *> cl = findChildren<K3DockWidget *>();
foreach ( K3DockWidget *obj, cl ) {
while ( !found && obj!= 0 ) {
QWidget* widg = qobject_cast<QWidget*>(obj);
if( widg == formerBrotherDockWidget)
found = true;
}
}
if( !found) {
// can dock back to the former brother dockwidget
manualDock( formerBrotherDockWidget, formerDockPos, d->splitPosInPercent, QPoint(0,0), false, d->index);
formerBrotherDockWidget = 0L;
makeDockVisible();
return;
}
}
// else dockback to the dockmainwindow (default behavior)
manualDock( ((K3DockMainWindow*)manager->main)->getMainDockWidget(), formerDockPos, d->splitPosInPercent, QPoint(0,0), false, d->index);
formerBrotherDockWidget = 0L;
if (parent())
makeDockVisible();
}
bool K3DockWidget::isDockBackPossible() const
{
if( !(formerBrotherDockWidget) || !(formerBrotherDockWidget->dockSite() & formerDockPos))
return false;
else
return true;
}
void K3DockWidget::setTabPageLabel( const QString& label)
{
tabPageTitle = label;
}
const QString& K3DockWidget::tabPageLabel() const
{
return tabPageTitle;
}
/**************************************************************************************/
K3DockManager::K3DockManager( QWidget* mainWindow , const char* name )
:QObject( mainWindow, name )
,main(mainWindow)
,currentDragWidget(0L)
,currentMoveWidget(0L)
,childDockWidgetList(0L)
,autoCreateDock(0L)
,storeW(0)
,storeH(0)
,dragging(false)
,undockProcess(false)
,dropCancel(true)
{
d = new K3DockManagerPrivate;
d->readyToDrag = false;
d->mainDockWidget=0;
#ifndef NO_KDE2
d->splitterOpaqueResize = KGlobalSettings::opaqueResize();
#else
d->splitterOpaqueResize = false;
#endif
d->splitterKeepSize = false;
d->splitterHighResolution = false;
d->m_readDockConfigMode = WrapExistingWidgetsOnly; // default as before
main->installEventFilter( this );
undockProcess = false;
menuData = new Q3PtrList<MenuDockData>;
menuData->setAutoDelete( true );
menuData->setAutoDelete( true );
#ifndef NO_KDE2
menu = new K3PopupMenu();
#else
menu = new Q3PopupMenu();
#endif
connect( menu, SIGNAL(aboutToShow()), SLOT(slotMenuPopup()) );
connect( menu, SIGNAL(activated(int)), SLOT(slotMenuActivated(int)) );
childDock = new QList<QObject*>();
}
void K3DockManager::setMainDockWidget2(K3DockWidget *w)
{
d->mainDockWidget=w;
}
K3DockManager::~K3DockManager()
{
delete menuData;
delete menu;
// ### FIXME: this seems to crash but why? (KDE4)
K3DockWidget * obj;
Q_FOREACH ( QObject *o, *childDock ) {
obj = (K3DockWidget*) ( o );
delete obj;
}
delete childDock;
delete d;
d=0;
}
void K3DockManager::activate()
{
K3DockWidget * obj;
foreach ( QObject *o, *childDock ) {
obj=(K3DockWidget*)o;
if ( obj->widget ) obj->widget->show();
if ( !obj->parentDockTabGroup() ){
obj->show();
}
}
if ( !main->inherits("QDialog") ) main->show();
}
bool K3DockManager::eventFilter( QObject *obj, QEvent *event )
{
if ( obj->inherits("K3DockWidgetAbstractHeaderDrag") ){
K3DockWidget* pDockWdgAtCursor = 0L;
K3DockWidget* curdw = ((K3DockWidgetAbstractHeaderDrag*)obj)->dockWidget();
switch ( event->type() ){
case QEvent::MouseButtonDblClick:
if (curdw->currentDockPos == K3DockWidget::DockDesktop) curdw->dockBack();
else
{
curdw->toDesktop();
// curdw->manualDock (0, K3DockWidget::DockDesktop);
}
break;
case QEvent::MouseButtonPress:
if ( ((QMouseEvent*)event)->button() == Qt::LeftButton ){
if ( curdw->eDocking != (int)K3DockWidget::DockNone ){
dropCancel = true;
curdw->setFocus();
qApp->processOneEvent();
currentDragWidget = curdw;
currentMoveWidget = 0L;
childDockWidgetList = new QWidgetList();
childDockWidgetList->append( curdw );
findChildDockWidget( curdw, childDockWidgetList );
//d->oldDragRect = QRect(); should fix rectangle not erased problem
d->dragRect = QRect(curdw->geometry());
QPoint p = curdw->mapToGlobal(QPoint(0,0));
d->dragRect.moveTopLeft(p);
drawDragRectangle();
d->readyToDrag = true;
d->dragOffset = QCursor::pos()-currentDragWidget->mapToGlobal(QPoint(0,0));
}
}
break;
case QEvent::MouseButtonRelease:
if ( ((QMouseEvent*)event)->button() == Qt::LeftButton ){
if ( dragging ){
if ( !dropCancel )
drop();
else
cancelDrop();
}
if (d->readyToDrag) {
d->readyToDrag = false;
//d->oldDragRect = QRect(); should fix rectangle not erased problem
d->dragRect = QRect(curdw->geometry());
QPoint p = curdw->mapToGlobal(QPoint(0,0));
d->dragRect.moveTopLeft(p);
drawDragRectangle();
currentDragWidget = 0L;
delete childDockWidgetList;
childDockWidgetList = 0L;
}
dragging = false;
dropCancel = true;
}
break;
case QEvent::MouseMove:
if ( dragging ) {
#ifdef BORDERLESS_WINDOWS
//BEGIN TEST
K3DockWidget *oldMoveWidget;
if (!curdw->parent())
{
curdw->move(QCursor::pos()-d->dragOffset);
pDockWdgAtCursor = findDockWidgetAt( QCursor::pos()-QPoint(0,d->dragOffset.y()+3) );
oldMoveWidget = currentMoveWidget;
}
else
{
pDockWdgAtCursor = findDockWidgetAt( QCursor::pos() );
oldMoveWidget = currentMoveWidget;
}
//END TEST
#else
pDockWdgAtCursor = findDockWidgetAt( QCursor::pos() );
K3DockWidget* oldMoveWidget = currentMoveWidget;
#endif
if ( currentMoveWidget && pDockWdgAtCursor == currentMoveWidget ) { //move
dragMove( currentMoveWidget, currentMoveWidget->mapFromGlobal( QCursor::pos() ) );
break;
} else {
if (dropCancel && curdw) {
d->dragRect = QRect(curdw->geometry());
QPoint p = curdw->mapToGlobal(QPoint(0,0));
d->dragRect.moveTopLeft(p);
}else
d->dragRect = QRect();
drawDragRectangle();
}
if ( !pDockWdgAtCursor && !(curdw->eDocking & (int)K3DockWidget::DockDesktop) ){
// just moving at the desktop
currentMoveWidget = pDockWdgAtCursor;
curPos = K3DockWidget::DockDesktop;
} else {
if ( oldMoveWidget && pDockWdgAtCursor != currentMoveWidget ) { //leave
currentMoveWidget = pDockWdgAtCursor;
curPos = K3DockWidget::DockDesktop;
}
}
if ( oldMoveWidget != pDockWdgAtCursor && pDockWdgAtCursor ) { //enter pDockWdgAtCursor
currentMoveWidget = pDockWdgAtCursor;
curPos = K3DockWidget::DockDesktop;
}
} else {
if (d->readyToDrag) {
d->readyToDrag = false;
}
if ( (((QMouseEvent*)event)->state() == Qt::LeftButton) &&
(curdw->eDocking != (int)K3DockWidget::DockNone) ) {
startDrag( curdw);
}
}
break;
default:
break;
}
}
return QObject::eventFilter( obj, event );
}
K3DockWidget* K3DockManager::findDockWidgetAt( const QPoint& pos )
{
dropCancel = true;
if (!currentDragWidget)
return 0L; // pointer access safety
if (currentDragWidget->eDocking == (int)K3DockWidget::DockNone ) return 0L;
QWidget* p = QApplication::topLevelAt( pos );
if ( !p ) {
dropCancel = false;
return 0L;
}
#if defined(_OS_WIN32_) || defined(Q_OS_WIN32)
p = p->topLevelWidget();
#endif
QWidget* w = 0L;
findChildDockWidget( w, p, p->mapFromGlobal(pos) );
if ( !w ){
if ( !p->inherits("K3DockWidget") ) {
return 0L;
}
w = p;
}
if ( findChild<K3DockSplitter*>("_dock_split_")) return 0L;
if ( findChild<K3DockTabGroup*>( "_dock_tab" )) return 0L;
if (dynamic_cast<K3DockContainer*>(w)) return 0L;
if (!childDockWidgetList) return 0L;
if ( childDockWidgetList->indexOf(w) != -1 ) return 0L;
if ( currentDragWidget->isGroup && ((K3DockWidget*)w)->parentDockTabGroup() ) return 0L;
K3DockWidget* www = (K3DockWidget*)w;
if ( www->sDocking == (int)K3DockWidget::DockNone ) return 0L;
if( !www->widget )
return 0L;
K3DockWidget::DockPosition curPos = K3DockWidget::DockDesktop;
QPoint cpos = www->mapFromGlobal( pos );
int ww = www->widget->width() / 3;
int hh = www->widget->height() / 3;
if ( cpos.y() <= hh ){
curPos = K3DockWidget::DockTop;
} else
if ( cpos.y() >= 2*hh ){
curPos = K3DockWidget::DockBottom;
} else
if ( cpos.x() <= ww ){
curPos = K3DockWidget::DockLeft;
} else
if ( cpos.x() >= 2*ww ){
curPos = K3DockWidget::DockRight;
} else
curPos = K3DockWidget::DockCenter;
if ( !(www->sDocking & (int)curPos) ) return 0L;
if ( !(currentDragWidget->eDocking & (int)curPos) ) return 0L;
if ( www->manager != this ) return 0L;
dropCancel = false;
return www;
}
void K3DockManager::findChildDockWidget( QWidget*& ww, const QWidget* p, const QPoint& pos )
{
if ( !p->children().isEmpty() ) {
QWidget *w;
foreach( QObject* o, p->children() )
{
if ( o->isWidgetType() ) {
w = (QWidget*)o;
if ( w->isVisible() && w->geometry().contains(pos) ) {
if ( w->inherits("K3DockWidget") ) ww = w;
findChildDockWidget( ww, w, w->mapFromParent(pos) );
return;
}
}
}
}
return;
}
void K3DockManager::findChildDockWidget( const QWidget* p, QWidgetList*& list )
{
if ( !p->children().isEmpty() ) {
QWidget *w;
foreach( QObject* o, p->children() )
{
if ( o->isWidgetType() ) {
w = (QWidget*)o;
if ( w->isVisible() ) {
if ( w->inherits("K3DockWidget") ) list->append( w );
findChildDockWidget( w, list );
}
}
}
}
return;
}
void K3DockManager::startDrag( K3DockWidget* w )
{
if(( w->currentDockPos == K3DockWidget::DockLeft) || ( w->currentDockPos == K3DockWidget::DockRight)
|| ( w->currentDockPos == K3DockWidget::DockTop) || ( w->currentDockPos == K3DockWidget::DockBottom)) {
w->prevSideDockPosBeforeDrag = w->currentDockPos;
if ( w->parentWidget()->inherits("K3DockSplitter") ){
K3DockSplitter* parentSplitterOfDockWidget = (K3DockSplitter*)(w->parentWidget());
w->d->splitPosInPercent = parentSplitterOfDockWidget->separatorPosInPercent();
}
}
curPos = K3DockWidget::DockDesktop;
dragging = true;
QApplication::setOverrideCursor(QCursor(Qt::SizeAllCursor));
}
void K3DockManager::dragMove( K3DockWidget* dw, QPoint pos )
{
QPoint p = dw->mapToGlobal( dw->widget->pos() );
K3DockWidget::DockPosition oldPos = curPos;
QSize r = dw->widget->size();
if ( dw->parentDockTabGroup() ){
curPos = K3DockWidget::DockCenter;
if ( oldPos != curPos ) {
d->dragRect.setRect( p.x()+2, p.y()+2, r.width()-4, r.height()-4 );
}
return;
}
int w = r.width() / 3;
int h = r.height() / 3;
if ( pos.y() <= h ){
curPos = K3DockWidget::DockTop;
w = r.width();
} else
if ( pos.y() >= 2*h ){
curPos = K3DockWidget::DockBottom;
p.setY( p.y() + 2*h );
w = r.width();
} else
if ( pos.x() <= w ){
curPos = K3DockWidget::DockLeft;
h = r.height();
} else
if ( pos.x() >= 2*w ){
curPos = K3DockWidget::DockRight;
p.setX( p.x() + 2*w );
h = r.height();
} else
{
curPos = K3DockWidget::DockCenter;
p.setX( p.x() + w );
p.setY( p.y() + h );
}
if ( oldPos != curPos ) {
d->dragRect.setRect( p.x(), p.y(), w, h );
drawDragRectangle();
}
}
void K3DockManager::cancelDrop()
{
QApplication::restoreOverrideCursor();
delete childDockWidgetList;
childDockWidgetList = 0L;
d->dragRect = QRect(); // cancel drawing
drawDragRectangle(); // only the old rect will be deleted
}
void K3DockManager::drop()
{
d->dragRect = QRect(); // cancel drawing
drawDragRectangle(); // only the old rect will be deleted
QApplication::restoreOverrideCursor();
delete childDockWidgetList;
childDockWidgetList = 0L;
if ( dropCancel ) return;
if ( !currentMoveWidget && (!(currentDragWidget->eDocking & (int)K3DockWidget::DockDesktop)) ) {
d->dragRect = QRect(); // cancel drawing
drawDragRectangle(); // only the old rect will be deleted
return;
}
if ( !currentMoveWidget && !currentDragWidget->parent() ) {
currentDragWidget->move( QCursor::pos() - d->dragOffset );
}
else {
// curPos is the current target DockPosition.
// currentDragWidget->prevSideDockPosBeforeDrag is where the dockwidget comes from.
// currentDragWidget->formerDockPos is the position *before* the dockwidget was in
// position currentDragWidget->prevSideDockPosBeforeDrag.
int splitPos = currentDragWidget->d->splitPosInPercent;
K3DockWidget::DockPosition previousPosition = currentDragWidget->prevSideDockPosBeforeDrag;
// kDebug() << splitPos;
// kDebug() << "curPos: " << curPos;
// kDebug() << "formerDockPos: " << currentDragWidget->formerDockPos;
// kDebug() << "prevSideDockPosBeforeDrag: " << currentDragWidget->prevSideDockPosBeforeDrag;
// Now we *need* to "invert" the procentual value, if the dockwidget moves from top/left
// to bottom/right or vice versa. This keeps the dockwidget's size on its new position.
// A special case is, when the dock position was DockNone, then we have to look for the
// formerDockPos to get things right.
if( (curPos != previousPosition)
&& (curPos != K3DockWidget::DockCenter) && (curPos != K3DockWidget::DockDesktop)) {
if (previousPosition == K3DockWidget::DockNone)
previousPosition = currentDragWidget->formerDockPos;
switch( previousPosition ) {
case K3DockWidget::DockLeft:
if(curPos != K3DockWidget::DockTop && curPos != K3DockWidget::DockLeft)
splitPos = 100 - splitPos;
break;
case K3DockWidget::DockRight:
if(curPos != K3DockWidget::DockBottom && curPos != K3DockWidget::DockRight)
splitPos = 100 - splitPos;
break;
case K3DockWidget::DockTop:
if(curPos != K3DockWidget::DockLeft && curPos != K3DockWidget::DockTop )
splitPos = 100 - splitPos;
break;
case K3DockWidget::DockBottom:
if(curPos != K3DockWidget::DockRight && curPos != K3DockWidget::DockBottom )
splitPos = 100 - splitPos;
break;
default: break;
}
}
// set new prevSideDockPosBeforeDrag
currentDragWidget->prevSideDockPosBeforeDrag = curPos;
currentDragWidget->manualDock( currentMoveWidget, curPos , splitPos, QCursor::pos() - d->dragOffset );
currentDragWidget->makeDockVisible();
}
}
static QDomElement createStringEntry(QDomDocument &doc, const QString &tagName, const QString &str)
{
QDomElement el = doc.createElement(tagName);
el.appendChild(doc.createTextNode(str));
return el;
}
static QDomElement createBoolEntry(QDomDocument &doc, const QString &tagName, bool b)
{
return createStringEntry(doc, tagName, QLatin1String(b? "true" : "false"));
}
static QDomElement createNumberEntry(QDomDocument &doc, const QString &tagName, int n)
{
return createStringEntry(doc, tagName, QString::number(n));
}
static QDomElement createRectEntry(QDomDocument &doc, const QString &tagName, const QRect &rect)
{
QDomElement el = doc.createElement(tagName);
QDomElement xel = doc.createElement("x");
xel.appendChild(doc.createTextNode(QString::number(rect.x())));
el.appendChild(xel);
QDomElement yel = doc.createElement("y");
yel.appendChild(doc.createTextNode(QString::number(rect.y())));
el.appendChild(yel);
QDomElement wel = doc.createElement("width");
wel.appendChild(doc.createTextNode(QString::number(rect.width())));
el.appendChild(wel);
QDomElement hel = doc.createElement("height");
hel.appendChild(doc.createTextNode(QString::number(rect.height())));
el.appendChild(hel);
return el;
}
static QDomElement createListEntry(QDomDocument &doc, const QString &tagName,
const QString &subTagName, const QStringList &list)
{
QDomElement el = doc.createElement(tagName);
foreach( const QString &s, list )
{
QDomElement subel = doc.createElement(subTagName);
subel.appendChild(doc.createTextNode(s));
el.appendChild(subel);
}
return el;
}
static QString stringEntry(QDomElement &base, const QString &tagName)
{
return base.namedItem(tagName).firstChild().toText().data();
}
static bool boolEntry(QDomElement &base, const QString &tagName)
{
return base.namedItem(tagName).firstChild().toText().data() == "true";
}
static int numberEntry(QDomElement &base, const QString &tagName)
{
return stringEntry(base, tagName).toInt();
}
static QRect rectEntry(QDomElement &base, const QString &tagName)
{
QDomElement el = base.namedItem(tagName).toElement();
int x = numberEntry(el, "x");
int y = numberEntry(el, "y");
int width = numberEntry(el, "width");
int height = numberEntry(el, "height");
return QRect(x, y, width, height);
}
static Q3StrList listEntry(QDomElement &base, const QString &tagName, const QString &subTagName)
{
Q3StrList list;
for( QDomNode n = base.namedItem(tagName).firstChild(); !n.isNull(); n = n.nextSibling() )
{
QDomElement subel = n.toElement();
if (subel.tagName() == subTagName)
list.append(subel.firstChild().toText().data().toLatin1().constData());
}
return list;
}
void K3DockManager::writeConfig(QDomElement &base)
{
// First of all, clear the tree under base
while (!base.firstChild().isNull())
base.removeChild(base.firstChild());
QDomDocument doc = base.ownerDocument();
QStringList nameList;
QString mainWidgetStr;
// collect widget names
QStringList nList;
Q_FOREACH( QObject *o, *childDock )
{
#ifdef __GNUC__ // ### KDE4
# warning "Can dw be 0 and what should we do in the case that it is?"
#endif
K3DockWidget* dw = qobject_cast<K3DockWidget*> ( o );
if ( !dw )
continue;
if ( dw->parent() == main )
mainWidgetStr = dw->objectName();
nList.append( dw->objectName() );
}
for (QObjectList::iterator it = d->containerDocks.begin();it != d->containerDocks.end();++it)
{
K3DockContainer* dc = dynamic_cast<K3DockContainer*>(((K3DockWidget*)(*it))->widget);
if (dc) {
dc->prepareSave(nList);
}
}
QStringList::Iterator nListIt=nList.begin();
while ( nListIt!=nList.end() ) {
K3DockWidget *obj = getDockWidgetFromName( *nListIt);
if ((obj->isGroup && (!obj->d->isContainer)) && (nameList.indexOf( obj->firstName) == -1
|| nameList.indexOf(obj->lastName) == -1)) {
// Skip until children are saved (why?)
++nListIt;
// nList.next();
//falk? if ( !nList.current() ) nList.first();
continue;
}
QDomElement groupEl;
if (obj->d->isContainer) {
K3DockContainer* x = dynamic_cast<K3DockContainer*>(obj->widget);
if (x) {
groupEl=doc.createElement("dockContainer");
x->save(groupEl);
}
} else
if (obj->isGroup) {
//// Save a group
groupEl = doc.createElement("splitGroup");
groupEl.appendChild(createStringEntry(doc, "firstName", obj->firstName));
groupEl.appendChild(createStringEntry(doc, "secondName", obj->lastName));
groupEl.appendChild(createNumberEntry(doc, "orientation", (int)obj->splitterOrientation));
groupEl.appendChild(createNumberEntry(doc, "separatorPos", ((K3DockSplitter*)obj->widget)->separatorPosInPercent()));
} else if (obj->isTabGroup) {
//// Save a tab group
groupEl = doc.createElement("tabGroup");
QStringList list;
for ( int i = 0; i < ((K3DockTabGroup*)obj->widget)->count(); ++i )
list.append( ((K3DockTabGroup*)obj->widget)->page( i )->name() );
groupEl.appendChild(createListEntry(doc, "tabs", "tab", list));
groupEl.appendChild(createNumberEntry(doc, "currentTab", ((K3DockTabGroup*)obj->widget)->currentPageIndex()));
if (!obj->parent()) {
groupEl.appendChild(createStringEntry(doc, "dockBackTo", obj->formerBrotherDockWidget ? obj->formerBrotherDockWidget->name() : ""));
groupEl.appendChild(createNumberEntry(doc, "dockBackToPos", obj->formerDockPos));
}
} else {
//// Save an ordinary dock widget
groupEl = doc.createElement("dock");
groupEl.appendChild(createStringEntry(doc, "tabCaption", obj->tabPageLabel()));
groupEl.appendChild(createStringEntry(doc, "tabToolTip", obj->toolTipString()));
if (!obj->parent()) {
groupEl.appendChild(createStringEntry(doc, "dockBackTo", obj->formerBrotherDockWidget ? obj->formerBrotherDockWidget->name() : ""));
groupEl.appendChild(createNumberEntry(doc, "dockBackToPos", obj->formerDockPos));
}
}
groupEl.appendChild(createStringEntry(doc, "name", QLatin1String(obj->name())));
groupEl.appendChild(createBoolEntry(doc, "hasParent", obj->parent()));
if ( !obj->parent() ) {
groupEl.appendChild(createRectEntry(doc, "geometry", QRect(main->frameGeometry().topLeft(), main->size())));
groupEl.appendChild(createBoolEntry(doc, "visible", obj->isVisible()));
}
if (obj->header && obj->header->inherits("K3DockWidgetHeader")) {
K3DockWidgetHeader *h = static_cast<K3DockWidgetHeader*>(obj->header);
groupEl.appendChild(createBoolEntry(doc, "dragEnabled", h->dragEnabled()));
}
base.appendChild(groupEl);
nameList.append(obj->name());
nList.erase(nListIt);
nListIt=nList.begin();
}
if (main->inherits("K3DockMainWindow")) {
K3DockMainWindow *dmain = (K3DockMainWindow*)main;
QString centralWidgetStr = QString(dmain->centralWidget()? dmain->centralWidget()->name() : "");
base.appendChild(createStringEntry(doc, "centralWidget", centralWidgetStr));
QString mainDockWidgetStr = QString(dmain->getMainDockWidget()? dmain->getMainDockWidget()->name() : "");
base.appendChild(createStringEntry(doc, "mainDockWidget", mainDockWidgetStr));
} else {
base.appendChild(createStringEntry(doc, "mainWidget", mainWidgetStr));
}
base.appendChild(createRectEntry(doc, "geometry", QRect(main->frameGeometry().topLeft(), main->size())));
}
void K3DockManager::readConfig(QDomElement &base)
{
if (base.namedItem("group").isNull()
&& base.namedItem("tabgroup").isNull()
&& base.namedItem("dock").isNull()
&& base.namedItem("dockContainer").isNull()) {
activate();
return;
}
autoCreateDock = new QObjectList;
bool isMainVisible = main->isVisible();
main->hide();
QObjectList::iterator it = childDock->begin();
K3DockWidget *obj1;
while ( it != childDock->end() ) {
obj1=(K3DockWidget*)(*it);
if ( !obj1->isGroup && !obj1->isTabGroup ) {
if ( obj1->parent() )
obj1->undock();
else
obj1->hide();
}
++it;
}
// firstly, recreate all common dockwidgets
for( QDomNode n = base.firstChild(); !n.isNull(); n = n.nextSibling() )
{
QDomElement childEl = n.toElement();
if (childEl.tagName() != "dock") continue;
// Read an ordinary dock widget
K3DockWidget *obj = getDockWidgetFromName(stringEntry(childEl, "name"));
obj->setTabPageLabel(stringEntry(childEl, "tabCaption"));
obj->setToolTipString(stringEntry(childEl, "tabToolTip"));
if (!boolEntry(childEl, "hasParent")) {
QRect r = rectEntry(childEl, "geometry");
obj = getDockWidgetFromName(stringEntry(childEl, "name"));
obj->applyToWidget(0);
obj->setGeometry(r);
if (boolEntry(childEl, "visible"))
obj->QWidget::show();
}
if (obj && obj->header && obj->header->inherits("K3DockWidgetHeader")) {
K3DockWidgetHeader *h = static_cast<K3DockWidgetHeader*>(obj->header);
h->setDragEnabled(boolEntry(childEl, "dragEnabled"));
}
}
// secondly, now iterate again and create the groups and tabwidgets, apply the dockwidgets to them
for( QDomNode n = base.firstChild(); !n.isNull(); n = n.nextSibling() )
{
QDomElement childEl = n.toElement();
if (childEl.isNull()) continue;
K3DockWidget *obj = 0;
if (childEl.tagName() == "dockContainer") {
K3DockWidget *cont=getDockWidgetFromName(stringEntry(childEl, "name"));
kDebug(282)<<"dockContainer: "<<stringEntry(childEl,"name");
if (!(cont->d->isContainer)) {
kDebug(282)<<"restoration of dockContainer is only supported for already existing dock containers";
} else {
K3DockContainer *dc=dynamic_cast<K3DockContainer*>(cont->getWidget());
if (!dc) kDebug(282)<<"Error while trying to handle dockcontainer configuration restoration";
else {
dc->load(childEl);
removeFromAutoCreateList(cont);
}
}
}
else
if (childEl.tagName() == "splitGroup") {
// Read a group
QString name = stringEntry(childEl, "name");
QString firstName = stringEntry(childEl, "firstName");
QString secondName = stringEntry(childEl, "secondName");
int orientation = numberEntry(childEl, "orientation");
int separatorPos = numberEntry(childEl, "separatorPos");
K3DockWidget *first = getDockWidgetFromName(firstName);
K3DockWidget *second = getDockWidgetFromName(secondName);
if (first && second) {
obj = first->manualDock(second,
(orientation == (int)Qt::Vertical)? K3DockWidget::DockLeft : K3DockWidget::DockTop,
separatorPos);
if (obj)
obj->setName(name.toLatin1().constData());
}
} else if (childEl.tagName() == "tabGroup") {
// Read a tab group
QString name = stringEntry(childEl, "name");
Q3StrList list = listEntry(childEl, "tabs", "tab");
K3DockWidget *d1 = getDockWidgetFromName( list.first() );
list.next();
K3DockWidget *d2 = getDockWidgetFromName( list.current() );
K3DockWidget *obj = d2->manualDock( d1, K3DockWidget::DockCenter );
if (obj) {
K3DockTabGroup *tab = (K3DockTabGroup*)obj->widget;
list.next();
while (list.current() && obj) {
K3DockWidget *tabDock = getDockWidgetFromName(list.current());
obj = tabDock->manualDock(d1, K3DockWidget::DockCenter);
list.next();
}
if (obj) {
obj->setName(name.toLatin1().constData());
tab->showPage(tab->page(numberEntry(childEl, "currentTab")));
}
}
} else {
continue;
}
if (!boolEntry(childEl, "hasParent")) {
QRect r = rectEntry(childEl, "geometry");
obj = getDockWidgetFromName(stringEntry(childEl, "name"));
obj->applyToWidget(0);
obj->setGeometry(r);
if (boolEntry(childEl, "visible"))
obj->QWidget::show();
}
if (obj && obj->header && obj->header->inherits("K3DockWidgetHeader")) {
K3DockWidgetHeader *h = static_cast<K3DockWidgetHeader*>(obj->header);
h->setDragEnabled(boolEntry(childEl, "dragEnabled"));
}
}
// thirdly, now that all ordinary dockwidgets are created,
// iterate them again and link them with their corresponding dockwidget for the dockback action
for( QDomNode n = base.firstChild(); !n.isNull(); n = n.nextSibling() )
{
QDomElement childEl = n.toElement();
if (childEl.tagName() != "dock" && childEl.tagName() != "tabGroup")
continue;
K3DockWidget *obj = 0;
if (!boolEntry(childEl, "hasParent")) {
// Read a common toplevel dock widget
obj = getDockWidgetFromName(stringEntry(childEl, "name"));
QString name = stringEntry(childEl, "dockBackTo");
if (!name.isEmpty()) {
obj->setFormerBrotherDockWidget(getDockWidgetFromName(name));
}
obj->formerDockPos = K3DockWidget::DockPosition(numberEntry(childEl, "dockBackToPos"));
obj->updateHeader();
}
}
if (main->inherits("K3DockMainWindow")) {
K3DockMainWindow *dmain = (K3DockMainWindow*)main;
QString mv = stringEntry(base, "centralWidget");
if (!mv.isEmpty() && getDockWidgetFromName(mv) ) {
K3DockWidget *mvd = getDockWidgetFromName(mv);
mvd->applyToWidget(dmain);
mvd->show();
dmain->setCentralWidget(mvd);
}
QString md = stringEntry(base, "mainDockWidget");
if (!md.isEmpty() && getDockWidgetFromName(md)) {
K3DockWidget *mvd = getDockWidgetFromName(md);
dmain->setMainDockWidget(mvd);
}
} else {
QString mv = stringEntry(base, "mainWidget");
if (!mv.isEmpty() && getDockWidgetFromName(mv)) {
K3DockWidget *mvd = getDockWidgetFromName(mv);
mvd->applyToWidget(main);
mvd->show();
}
// only resize + move non-mainwindows
QRect mr = rectEntry(base, "geometry");
main->move(mr.topLeft());
main->resize(mr.size());
}
if (isMainVisible)
main->show();
if (d->m_readDockConfigMode == WrapExistingWidgetsOnly) {
finishReadDockConfig(); // remove empty dockwidgets
}
}
void K3DockManager::removeFromAutoCreateList(K3DockWidget* pDockWidget)
{
if (!autoCreateDock) return;
autoCreateDock->removeAt(autoCreateDock->indexOf(pDockWidget));
}
void K3DockManager::finishReadDockConfig()
{
delete autoCreateDock;
autoCreateDock = 0;
}
void K3DockManager::setReadDockConfigMode(int mode)
{
d->m_readDockConfigMode = mode;
}
#ifndef NO_KDE2
void K3DockManager::writeConfig( KConfig* c, const QString &_group )
{
//debug("BEGIN Write Config");
if (!c) {
c = KSharedConfig::openConfig().data();
}
QString group = _group.isEmpty() ? "dock_setting_default" : _group;
KConfigGroup cg(c, group);
cg.writeEntry( "Version", DOCK_CONFIG_VERSION );
QStringList nameList;
QStringList findList;
K3DockWidget* obj;
// collect K3DockWidget's name
QStringList nList;
Q_FOREACH( QObject* o, *childDock )
{
#ifdef __GNUC__ // ### KDE4
# warning "Can obj be 0 and what should we do in the case that it is?"
#endif
obj = qobject_cast<K3DockWidget*> ( o );
if ( !obj )
continue;
//debug(" +Add subdock %s", obj->objectName());
nList.append( obj->objectName() );
if ( obj->parent() == main )
cg.writeEntry( "Main:view", obj->objectName() );
}
// kDebug(282)<<QString("list size: %1").arg(nList.count());
for (QObjectList::iterator it = d->containerDocks.begin() ;it != d->containerDocks.end(); ++it)
{
K3DockContainer* dc = dynamic_cast<K3DockContainer*>(((K3DockWidget*)(*it))->widget);
if (dc) {
dc->prepareSave(nList);
}
}
// kDebug(282)<<QString("new list size: %1").arg(nList.count());
QStringList::Iterator nListIt=nList.begin();
while ( nListIt!=nList.end() ){
//debug(" -Try to save %s", nList.current());
obj = getDockWidgetFromName( *nListIt );
QString cname = obj->name();
if ( obj->header ){
obj->header->saveConfig( &cg );
}
if (obj->d->isContainer) {
K3DockContainer* x = dynamic_cast<K3DockContainer*>(obj->widget);
if (x) {
x->save(c,group);
}
}
/*************************************************************************************************/
if ( obj->isGroup ){
if ( (findList.indexOf( obj->firstName ) != -1) && (findList.indexOf( obj->lastName ) != -1 )){
cg.writeEntry( cname+":type", "GROUP");
if ( !obj->parent() ){
cg.writeEntry( cname+":parent", "___null___");
cg.writeEntry( cname+":geometry", QRect(obj->frameGeometry().topLeft(), obj->size()) );
cg.writeEntry( cname+":visible", QVariant(obj->isVisible()) );
} else {
cg.writeEntry( cname+":parent", "yes");
}
cg.writeEntry( cname+":first_name", obj->firstName );
cg.writeEntry( cname+":last_name", obj->lastName );
cg.writeEntry( cname+":orientation", QVariant(obj->splitterOrientation) );
cg.writeEntry( cname+":sepPos", QVariant(((K3DockSplitter*)obj->widget)->separatorPosInPercent()) );
nameList.append( obj->name() );
findList.append( obj->name() );
//debug(" Save %s", nList.current());
nList.erase(nListIt);
nListIt=nList.begin(); //nList.first();
} else {
/*************************************************************************************************/
//debug(" Skip %s", nList.current());
//if ( findList.find( obj->firstName ) == -1 )
// debug(" ? Not found %s", obj->firstName);
//if ( findList.find( obj->lastName ) == -1 )
// debug(" ? Not found %s", obj->lastName);
++nListIt;
// if ( !nList.current() ) nList.first();
if (nListIt==nList.end()) nListIt=nList.begin();
}
} else {
/*************************************************************************************************/
if ( obj->isTabGroup){
cg.writeEntry( cname+":type", "TAB_GROUP");
if ( !obj->parent() ){
cg.writeEntry( cname+":parent", "___null___");
cg.writeEntry( cname+":geometry", QRect(obj->frameGeometry().topLeft(), obj->size()) );
cg.writeEntry( cname+":visible", QVariant(obj->isVisible()) );
cg.writeEntry( cname+":dockBackTo", obj->formerBrotherDockWidget ? obj->formerBrotherDockWidget->name() : "");
cg.writeEntry( cname+":dockBackToPos", QVariant(obj->formerDockPos) );
} else {
cg.writeEntry( cname+":parent", "yes");
}
QStringList list;
for ( int i = 0; i < ((K3DockTabGroup*)obj->widget)->count(); ++i )
list.append( ((K3DockTabGroup*)obj->widget)->page( i )->name() );
cg.writeEntry( cname+":tabNames", list );
cg.writeEntry( cname+":curTab", QVariant(((K3DockTabGroup*)obj->widget)->currentPageIndex()) );
nameList.append( obj->name() );
findList.append( obj->name() ); // not really need !!!
//debug(" Save %s", nList.current());
nList.erase(nListIt);
nListIt=nList.begin();
} else {
/*************************************************************************************************/
cg.writeEntry( cname+":tabCaption", obj->tabPageLabel());
cg.writeEntry( cname+":tabToolTip", obj->toolTipString());
if ( !obj->parent() ){
cg.writeEntry( cname+":type", "NULL_DOCK");
cg.writeEntry( cname+":geometry", QRect(obj->frameGeometry().topLeft(), obj->size()) );
cg.writeEntry( cname+":visible", QVariant(obj->isVisible()) );
cg.writeEntry( cname+":dockBackTo", obj->formerBrotherDockWidget ? obj->formerBrotherDockWidget->name() : "");
cg.writeEntry( cname+":dockBackToPos", QVariant(obj->formerDockPos) );
} else {
cg.writeEntry( cname+":type", "DOCK");
}
nameList.append( cname.toLatin1().constData() );
//debug(" Save %s", nList.current());
findList.append( obj->name() );
nList.erase(nListIt);
nListIt=nList.begin();
}
}
}
cg.writeEntry( "NameList", nameList );
cg.writeEntry( "Main:Geometry", QRect(main->frameGeometry().topLeft(), main->size()) );
cg.writeEntry( "Main:visible", QVariant(main->isVisible()) ); // curently nou use
if ( main->inherits("K3DockMainWindow") ){
K3DockMainWindow* dmain = (K3DockMainWindow*)main;
// for K3DockMainWindow->setView() in readConfig()
cg.writeEntry( "Main:view", dmain->centralWidget() ? dmain->centralWidget()->name():"" );
cg.writeEntry( "Main:dock", dmain->getMainDockWidget() ? dmain->getMainDockWidget()->name() :"" );
}
c->sync();
//debug("END Write Config");
}
#include <qmessagebox.h>
void K3DockManager::readConfig( KConfig* c, const QString &_group )
{
if (!c) {
c = KSharedConfig::openConfig().data();
}
QString group = _group.isEmpty() ? "dock_setting_default" : _group;
KConfigGroup cg(c, group );
QStringList nameList;
nameList = cg.readEntry( "NameList", QStringList() );
QString ver = cg.readEntry( "Version", "0.0.1" );
if ( nameList.isEmpty() || ver != DOCK_CONFIG_VERSION ){
activate();
return;
}
autoCreateDock = new QObjectList();
bool isMainVisible = main->isVisible();
// if (isMainVisible) // CCC
//QMessageBox::information(0,"","hallo");
//COMMENTED4TESTING main->hide();
QObjectList::iterator it = childDock->begin();
K3DockWidget * obj;
while ( it != childDock->end() ){
obj = (K3DockWidget*)(*it);
++it;
if ( !obj->isGroup && !obj->isTabGroup ) {
if ( obj->parent() ) obj->undock(); else obj->hide();
}
}
// firstly, only the common dockwidgets,
// they must be restored before e.g. tabgroups are restored
foreach( const QString &oname, nameList )
{
cg = KConfigGroup( c, group );
QString type = cg.readEntry( oname + ":type", QString() );
obj = 0L;
if ( type == "NULL_DOCK" || cg.readEntry( oname + ":parent", QString() ) == "___null___" ){
QRect r = cg.readEntry( oname + ":geometry",QRect() );
obj = getDockWidgetFromName( oname );
obj->applyToWidget( 0L );
obj->setGeometry(r);
cg = KConfigGroup( c, group );
obj->setTabPageLabel(cg.readEntry( oname + ":tabCaption", QString() ));
obj->setToolTipString(cg.readEntry( oname + ":tabToolTip", QString() ));
if ( cg.readEntry( oname + ":visible",QVariant(false) ).toBool() ){
obj->QWidget::show();
}
}
if ( type == "DOCK" ){
obj = getDockWidgetFromName( oname );
obj->setTabPageLabel(cg.readEntry( oname + ":tabCaption", QString() ));
obj->setToolTipString(cg.readEntry( oname + ":tabToolTip", QString() ));
}
if (obj && obj->d->isContainer) {
dynamic_cast<K3DockContainer*>(obj->widget)->load(c,group);
removeFromAutoCreateList(obj);
}
if ( obj && obj->header){
obj->header->loadConfig( &cg );
}
}
// secondly, after the common dockwidgets, restore the groups and tabgroups
foreach( const QString &oname, nameList )
{
cg = KConfigGroup( c, group );
QString type = cg.readEntry( oname + ":type", QString() );
obj = 0L;
if ( type == "GROUP" ){
K3DockWidget* first = getDockWidgetFromName( cg.readEntry( oname + ":first_name", QString() ) );
K3DockWidget* last = getDockWidgetFromName( cg.readEntry( oname + ":last_name", QString() ) );
int sepPos = cg.readEntry( oname + ":sepPos",0 );
Qt::Orientation p = (Qt::Orientation)cg.readEntry( oname + ":orientation",0 );
if ( first && last ){
obj = first->manualDock( last, ( p == Qt::Vertical ) ? K3DockWidget::DockLeft : K3DockWidget::DockTop, sepPos );
if (obj){
obj->setName( oname.toLatin1().constData() );
}
}
}
if ( type == "TAB_GROUP" ){
K3DockWidget* tabDockGroup = 0L;
const QStringList list = cg.readEntry( oname+":tabNames",QStringList() );
QStringList::const_iterator listit = list.begin();
K3DockWidget* d1 = getDockWidgetFromName( *listit++ );
K3DockWidget* d2 = getDockWidgetFromName( *listit++ );
tabDockGroup = d2->manualDock( d1, K3DockWidget::DockCenter );
if ( tabDockGroup ){
K3DockTabGroup* tab = dynamic_cast<K3DockTabGroup*>(tabDockGroup->widget);
while ( listit != list.end() && tabDockGroup ){
K3DockWidget* tabDock = getDockWidgetFromName( *listit++ );
tabDockGroup = tabDock->manualDock( d1, K3DockWidget::DockCenter );
}
if ( tabDockGroup ){
tabDockGroup->setName( oname.toLatin1().constData() );
cg = KConfigGroup( c, group );
if (tab)
tab->showPage( tab->page( cg.readEntry( oname+":curTab",0 ) ) );
}
}
obj = tabDockGroup;
}
if (obj && obj->d->isContainer) dynamic_cast<K3DockContainer*>(obj->widget)->load(c,group);
if ( obj && obj->header){
obj->header->loadConfig( &cg );
}
}
// thirdly, now that all ordinary dockwidgets are created,
// iterate them again and link the toplevel ones of them with their corresponding dockwidget for the dockback action
foreach( const QString &oname, nameList )
{
cg = KConfigGroup( c, group );
QString type = cg.readEntry( oname + ":type", QString() );
obj = 0L;
if ( type == "NULL_DOCK" || cg.readEntry( oname + ":parent", QString() ) == "___null___" ){
obj = getDockWidgetFromName( oname );
cg = KConfigGroup( c, group );
QString name = cg.readEntry( oname + ":dockBackTo", QString() );
if (!name.isEmpty()) {
obj->setFormerBrotherDockWidget(getDockWidgetFromName( name ));
}
obj->formerDockPos = K3DockWidget::DockPosition(cg.readEntry( oname + ":dockBackToPos",0 ));
}
}
if ( main->inherits("K3DockMainWindow") ){
K3DockMainWindow* dmain = (K3DockMainWindow*)main;
cg = KConfigGroup( c, group );
QString mv = cg.readEntry( "Main:view" );
if ( !mv.isEmpty() && getDockWidgetFromName( mv ) ){
K3DockWidget* mvd = getDockWidgetFromName( mv );
mvd->applyToWidget( dmain );
mvd->show();
dmain->setView( mvd );
}
cg = KConfigGroup( c, group );
QString md = cg.readEntry( "Main:dock" );
if ( !md.isEmpty() && getDockWidgetFromName( md ) ){
K3DockWidget* mvd = getDockWidgetFromName( md );
dmain->setMainDockWidget( mvd );
}
} else {
cg = KConfigGroup( c, group );
QString mv = cg.readEntry( "Main:view" );
if ( !mv.isEmpty() && getDockWidgetFromName( mv ) ){
K3DockWidget* mvd = getDockWidgetFromName( mv );
mvd->applyToWidget( main );
mvd->show();
}
}
// delete all autocreate dock
if (d->m_readDockConfigMode == WrapExistingWidgetsOnly) {
finishReadDockConfig(); // remove empty dockwidgets
}
cg = KConfigGroup( c, group );
QRect mr = cg.readEntry("Main:Geometry", QRect());
if (!main->inherits("K3DockMainWindow"))
main->move(mr.topLeft());
main->resize(mr.size());
if ( isMainVisible ) main->show();
}
#endif
void K3DockManager::dumpDockWidgets() {
QObjectList::iterator it = childDock->begin();
K3DockWidget * obj;
while ( it != childDock->end() ) {
obj = (K3DockWidget*)(*it);
++it;
kDebug(282) << "K3DockManager::dumpDockWidgets:" << obj->name();
}
}
K3DockWidget* K3DockManager::getDockWidgetFromName( const QString& dockName )
{
QObjectList::iterator it = childDock->begin();
K3DockWidget * obj;
while ( it != childDock->end() ) {
obj=(K3DockWidget*)(*it);
++it;
if ( obj->objectName() == dockName ) return obj;
}
K3DockWidget* autoCreate = 0L;
if ( autoCreateDock ){
kDebug(282)<<"Autocreating dock: "<<dockName;
autoCreate = new K3DockWidget( this, dockName.toLatin1().constData(), QPixmap("") );
autoCreateDock->append( autoCreate );
}
return autoCreate;
}
void K3DockManager::setSplitterOpaqueResize(bool b)
{
d->splitterOpaqueResize = b;
}
bool K3DockManager::splitterOpaqueResize() const
{
return d->splitterOpaqueResize;
}
void K3DockManager::setSplitterKeepSize(bool b)
{
d->splitterKeepSize = b;
}
bool K3DockManager::splitterKeepSize() const
{
return d->splitterKeepSize;
}
void K3DockManager::setSplitterHighResolution(bool b)
{
d->splitterHighResolution = b;
}
bool K3DockManager::splitterHighResolution() const
{
return d->splitterHighResolution;
}
void K3DockManager::slotMenuPopup()
{
menu->clear();
menuData->clear();
QObjectList::iterator it = childDock->begin();
K3DockWidget * obj;
while ( it != childDock->end() ) {
obj=(K3DockWidget*)(*it);
++it;
if ( obj->mayBeHide() ) {
menu->insertItem( i18n("Hide %1", obj->windowTitle()));
menuData->append( new MenuDockData( obj, true ) );
}
if ( obj->mayBeShow() ) {
menu->insertItem( i18n("Show %1", obj->windowTitle()));
menuData->append( new MenuDockData( obj, false ) );
}
}
}
void K3DockManager::slotMenuActivated( int id )
{
MenuDockData* data = menuData->at( id );
data->dock->changeHideShowState();
}
K3DockWidget* K3DockManager::findWidgetParentDock( QWidget* w ) const
{
QObjectList::iterator it = childDock->begin();
K3DockWidget * dock;
K3DockWidget * found = 0L;
while (it != childDock->end() ) {
dock=(K3DockWidget*)(*it);
++it;
if ( dock->widget == w ){ found = dock; break; }
}
return found;
}
void K3DockManager::makeWidgetDockVisible( QWidget* w )
{
findWidgetParentDock(w)->makeDockVisible();
}
Q3PopupMenu* K3DockManager::dockHideShowMenu() const
{
return menu;
}
void K3DockManager::drawDragRectangle()
{
#ifdef BORDERLESS_WINDOWS
return
#endif
if (d->oldDragRect == d->dragRect)
return;
int i;
QRect oldAndNewDragRect[2];
oldAndNewDragRect[0] = d->oldDragRect;
oldAndNewDragRect[1] = d->dragRect;
// 2 calls, one for the old and one for the new drag rectangle
for (i = 0; i <= 1; i++) {
if (oldAndNewDragRect[i].isEmpty())
continue;
K3DockWidget* pDockWdgAtRect = (K3DockWidget*) QApplication::widgetAt( oldAndNewDragRect[i].topLeft() );
if (!pDockWdgAtRect)
continue;
bool isOverMainWdg = false;
//bool unclipped;
K3DockMainWindow* pMain = 0L;
K3DockWidget* pTLDockWdg = 0L;
QWidget* topWdg;
if (pDockWdgAtRect->topLevelWidget() == main) {
isOverMainWdg = true;
topWdg = pMain = (K3DockMainWindow*) main;
//unclipped = pMain->testWFlags( WPaintUnclipped );
//pMain->setWFlags( WPaintUnclipped );
}
else {
topWdg = pTLDockWdg = (K3DockWidget*) pDockWdgAtRect->topLevelWidget();
//unclipped = pTLDockWdg->testWFlags( WPaintUnclipped );
//pTLDockWdg->setWFlags( WPaintUnclipped );
}
// draw the rectangle unclipped over the main dock window
QPainter p;
p.begin( topWdg );
/*
if ( !unclipped ) {
if (isOverMainWdg)
pMain->clearWFlags(WPaintUnclipped);
else
pTLDockWdg->clearWFlags(WPaintUnclipped);
}
*/
// draw the rectangle
//p.setRasterOp(Qt::NotXorROP);
QRect r = oldAndNewDragRect[i];
r.moveTopLeft( r.topLeft() - topWdg->mapToGlobal(QPoint(0,0)) );
p.drawRect(r.x(), r.y(), r.width(), r.height());
p.end();
}
// memorize the current rectangle for later removing
d->oldDragRect = d->dragRect;
}
void K3DockManager::setSpecialLeftDockContainer(K3DockWidget* container) {
d->leftContainer=container;
}
void K3DockManager::setSpecialTopDockContainer(K3DockWidget* container) {
d->topContainer=container;
}
void K3DockManager::setSpecialRightDockContainer(K3DockWidget* container) {
d->rightContainer=container;
}
void K3DockManager::setSpecialBottomDockContainer(K3DockWidget* container) {
d->bottomContainer=container;
}
K3DockArea::K3DockArea( QWidget* parent, const char *name)
:QWidget( parent, name)
{
QString new_name = QString(name) + QString("_DockManager");
dockManager = new K3DockManager( this, new_name.toLatin1().constData() );
mainDockWidget = 0L;
}
K3DockArea::~K3DockArea()
{
delete dockManager;
}
K3DockManager* K3DockArea::manager()
{
return dockManager;
}
K3DockWidget* K3DockArea::createDockWidget( const QString& name, const QPixmap &pixmap, QWidget* parent, const QString& strCaption, const QString& strTabPageLabel)
{
return new K3DockWidget( dockManager, name.toLatin1().constData(), pixmap, parent, strCaption, strTabPageLabel );
}
void K3DockArea::activateDock()
{
dockManager->activate();
}
Q3PopupMenu* K3DockArea::dockHideShowMenu()
{
return dockManager->dockHideShowMenu();
}
void K3DockArea::makeDockVisible( K3DockWidget* dock )
{
if ( dock )
dock->makeDockVisible();
}
void K3DockArea::makeDockInvisible( K3DockWidget* dock )
{
if ( dock )
dock->undock();
}
void K3DockArea::makeWidgetDockVisible( QWidget* widget )
{
makeDockVisible( dockManager->findWidgetParentDock(widget) );
}
void K3DockArea::writeDockConfig(QDomElement &base)
{
dockManager->writeConfig(base);
}
void K3DockArea::readDockConfig(QDomElement &base)
{
dockManager->readConfig(base);
}
void K3DockArea::slotDockWidgetUndocked()
{
QObject* pSender = (QObject*) sender();
if (!pSender->inherits("K3DockWidget")) return;
K3DockWidget* pDW = (K3DockWidget*) pSender;
emit dockWidgetHasUndocked( pDW);
}
void K3DockArea::resizeEvent(QResizeEvent *rsize)
{
QWidget::resizeEvent(rsize);
if (!children().isEmpty()){
#ifndef NO_KDE2
// kDebug(282)<<"K3DockArea::resize";
#endif
QList<QWidget *> list = findChildren<QWidget*>();
foreach( QWidget *w, list )
{
w->setGeometry(QRect(QPoint(0,0),size()));
}
#if 0
K3DockSplitter *split;
// for (unsigned int i=0;i<children()->count();i++)
{
// QPtrList<QObject> list(children());
// QObject *obj=((QPtrList<QObject*>)children())->at(i);
QObject *obj=children()->getFirst();
if (split = dynamic_cast<K3DockSplitter*>(obj))
{
split->setGeometry( QRect(QPoint(0,0), size() ));
// break;
}
}
#endif
}
}
#ifndef NO_KDE2
void K3DockArea::writeDockConfig( KConfig* c, const QString &group )
{
dockManager->writeConfig( c, group );
}
void K3DockArea::readDockConfig( KConfig* c, const QString &group )
{
dockManager->readConfig( c, group );
}
#endif
void K3DockArea::setMainDockWidget( K3DockWidget* mdw )
{
if ( mainDockWidget == mdw ) return;
mainDockWidget = mdw;
mdw->applyToWidget(this);
}
K3DockWidget* K3DockArea::getMainDockWidget()
{
return mainDockWidget;
}
// KDOCKCONTAINER - AN ABSTRACTION OF THE KDOCKTABWIDGET
K3DockContainer::K3DockContainer(){m_overlapMode=false; m_childrenListBegin=0; m_childrenListEnd=0;}
K3DockContainer::~K3DockContainer(){
if (m_childrenListBegin)
{
struct ListItem *tmp=m_childrenListBegin;
while (tmp)
{
struct ListItem *tmp2=tmp->next;
free(tmp->data);
delete tmp;
tmp=tmp2;
}
m_childrenListBegin=0;
m_childrenListEnd=0;
}
}
void K3DockContainer::activateOverlapMode(int nonOverlapSize) {
m_nonOverlapSize=nonOverlapSize;
m_overlapMode=true;
if (parentDockWidget() && parentDockWidget()->parent()) {
kDebug(282)<<"K3DockContainer::activateOverlapMode: recalculating sizes";
K3DockSplitter *sp= qobject_cast<K3DockSplitter*>(parentDockWidget()->parent());
if (sp)
sp->resizeEvent(0);
}
}
void K3DockContainer::deactivateOverlapMode() {
if (!m_overlapMode) return;
m_overlapMode=false;
if (parentDockWidget() && parentDockWidget()->parent()) {
kDebug(282)<<"K3DockContainer::deactivateOverlapMode: recalculating sizes";
K3DockSplitter *sp= qobject_cast<K3DockSplitter*>(parentDockWidget()->parent());
if (sp)
sp->resizeEvent(0);
}
}
bool K3DockContainer::isOverlapMode() {
return m_overlapMode;
}
bool K3DockContainer::dockDragEnter(K3DockWidget*, QMouseEvent *) { return false;}
bool K3DockContainer::dockDragMove(K3DockWidget*, QMouseEvent *) { return false;}
bool K3DockContainer::dockDragLeave(K3DockWidget*, QMouseEvent *) { return false;}
K3DockWidget *K3DockContainer::parentDockWidget(){return 0;}
QStringList K3DockContainer::containedWidgets() const {
QStringList tmp;
for (struct ListItem *it=m_childrenListBegin;it;it=it->next) {
tmp<<QString(it->data);
}
return tmp;
}
void K3DockContainer::showWidget(K3DockWidget *) {
}
void K3DockContainer::insertWidget (K3DockWidget *dw, const QPixmap &, const QString &, int &)
{
struct ListItem *it=new struct ListItem;
it->data=strdup(dw->name());
it->next=0;
if (m_childrenListEnd)
{
m_childrenListEnd->next=it;
it->prev=m_childrenListEnd;
m_childrenListEnd=it;
}
else
{
it->prev=0;
m_childrenListEnd=it;
m_childrenListBegin=it;
}
}
void K3DockContainer::removeWidget (K3DockWidget *dw){
for (struct ListItem *tmp=m_childrenListBegin;tmp;tmp=tmp->next)
{
if (!strcmp(tmp->data,dw->name()))
{
free(tmp->data);
if (tmp->next) tmp->next->prev=tmp->prev;
if (tmp->prev) tmp->prev->next=tmp->next;
if (tmp==m_childrenListBegin) m_childrenListBegin=tmp->next;
if (tmp==m_childrenListEnd) m_childrenListEnd=tmp->prev;
delete tmp;
break;
}
}
}
//m_children.remove(dw->name());}
void K3DockContainer::undockWidget (K3DockWidget *){;}
void K3DockContainer::setToolTip(K3DockWidget *, QString &){;}
void K3DockContainer::setPixmap(K3DockWidget*,const QPixmap&){;}
void K3DockContainer::load (KConfig*, const QString&){;}
void K3DockContainer::save (KConfig*, const QString&){;}
void K3DockContainer::load (QDomElement&){;}
void K3DockContainer::save (QDomElement&){;}
void K3DockContainer::prepareSave(QStringList &names)
{
for (struct ListItem *tmp=m_childrenListBegin;tmp; tmp=tmp->next)
names.removeAll(tmp->data);
// for (uint i=0;i<m_children.count();i++)
// {
// names.remove(m_children.at(i));
// }
}
K3DockTabGroup::K3DockTabGroup( QWidget *parent, const char *name )
:QTabWidget( parent )
{
setObjectName( QLatin1String(name) );
}
K3DockTabGroup::~K3DockTabGroup()
{
}
QWidget *K3DockTabGroup::transientTo() {
QWidget *tT=0;
for (int i=0;i<count();i++) {
K3DockWidget *dw=qobject_cast<K3DockWidget*>(page(i));
QWidget *tmp;
if ((tmp=dw->transientTo())) {
if (!tT) tT=tmp;
else {
if (tT!=tmp) {
kDebug(282)<<"K3DockTabGroup::transientTo: widget mismatch";
return 0;
}
}
}
}
kDebug(282)<<"K3DockTabGroup::transientTo: "<<(tT?"YES":"NO");
return tT;
}
void K3DockWidgetAbstractHeader::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void K3DockWidgetAbstractHeaderDrag::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void K3DockWidgetHeaderDrag::virtual_hook( int id, void* data )
{ K3DockWidgetAbstractHeaderDrag::virtual_hook( id, data ); }
void K3DockWidgetHeader::virtual_hook( int id, void* data )
{ K3DockWidgetAbstractHeader::virtual_hook( id, data ); }
void K3DockTabGroup::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void K3DockWidget::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void K3DockManager::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void K3DockMainWindow::virtual_hook( int, void* )
{ /* KMainWindow::virtual_hook( id, data ); */ }
void K3DockArea::virtual_hook( int, void* )
{ /*KMainWindow::virtual_hook( id, data );*/ }
#ifndef NO_INCLUDE_MOCFILES // for Qt-only projects, because tmake doesn't take this name
#include "moc_k3dockwidget.cpp"
#endif
//kate: indent-mode csands; space-indent on; indent-width 2;
diff --git a/kde3support/kdeui/k3dockwidget.h b/kde3support/kdeui/k3dockwidget.h
index acc6efc67d..c638718179 100644
--- a/kde3support/kdeui/k3dockwidget.h
+++ b/kde3support/kdeui/k3dockwidget.h
@@ -1,1510 +1,1510 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Max Judin <novaprint@mtu-net.ru>
Copyright (C) 2000 Falk Brettschneider <falk@kdevelop.org>
Copyright (C) 2002,2003 Joseph Wenninger <jowenn@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 version 2 as published by the Free Software Foundation.
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.
*/
/*
activities:
-----------
05/2001 - : useful patches, bugfixes by Christoph Cullmann <crossfire@babylon2k.de>,
Joseph Wenninger <jowenn@bigfoot.com> and Falk Brettschneider
03/2001 - 05/2001 : maintained and enhanced by Falk Brettschneider <falk@kdevelop.org>
03/2000 : class documentation added by Falk Brettschneider <gigafalk@yahoo.com>
10/1999 - 03/2000 : programmed by Max Judin <novaprint@mtu-net.ru>
C++ classes in this file:
-------------------------
- K3DockWidgetAbstractHeader - minor helper class
- K3DockWidgetAbstractHeaderDrag - minor helper class
- K3DockWidgetHeaderDrag - drag panel in a dockwidget title bar
- K3DockWidgetHeader - dockwidget title bar containing the drag panel
- K3DockTabGroup - minor helper class
- K3DockWidget - IMPORTANT CLASS: the one and only dockwidget class
- K3DockManager - helper class
- K3DockMainWindow - IMPORTANT CLASS: a special KMainWindow that can have dockwidgets
- K3DockArea - like K3DockMainWindow but inherits just QWidget
IMPORTANT Note: This file compiles also in Qt-only mode by using the NO_KDE2 precompiler definition!
*/
#ifndef KDOCKWIDGET_H
#define KDOCKWIDGET_H
#define _KDOCKWIDGET_2_2_
#include <kde3support_export.h>
#include <QtCore/QPoint>
#include <Qt3Support/Q3PtrList>
#include <QFrame>
#include <QtXml/QDomElement>
#include <QTabWidget>
#ifndef NO_KDE2
#include <kxmlguiwindow.h>
#include <netwm_def.h>
#else
#include <Qt3Support/Q3MainWindow>
#include "exportdockclass.h"
#include "dummykmainwindow.h"
#endif
class K3DockSplitter;
class K3DockManager;
class K3DockWidget;
class K3DockButton_Private;
class K3DockWidgetPrivate;
class K3DockWidgetHeaderPrivate;
class K3DockArea;
class Q3PopupMenu;
class QVBoxLayout;
class QHBoxLayout;
class QPixmap;
#ifndef NO_KDE2
class KConfigGroup;
#else
#endif
class K3DockContainer;
namespace K3MDI
{
class MainWindow;
}
/**
* An abstract base clase for all dockwidget headers (and member of the dockwidget class set).
* See the class description of K3DockWidgetHeader!
* More or less a minor helper class for the dockwidget class set.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockWidgetAbstractHeader : public QFrame
{
Q_OBJECT
public:
/**
* Constructs this.
*
* @param parent the parent widget (usually a dockwidget)
* @param name the object instance name
*/
K3DockWidgetAbstractHeader( K3DockWidget* parent, const char* name = 0L );
/**
* Destructs this.
*/
virtual ~K3DockWidgetAbstractHeader(){}
/**
* Provides things concerning to switching to toplevel mode. Must be overridden by an inheriting class.
*/
virtual void setTopLevel( bool ){}
#ifndef NO_KDE2
/**
* Provides saving the current configuration. Must be overridden by an inheriting class.
*/
virtual void saveConfig( KConfigGroup* ){}
/**
* Provides loading the current configuration. Must be overridden by an inheriting class
*/
virtual void loadConfig( KConfigGroup* ){}
#endif
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockWidgetAbstractHeaderPrivate;
K3DockWidgetAbstractHeaderPrivate *d;
};
/**
* An abstract class for all dockwidget drag-panels of a dockwidgets (and member of the dockwidget class set).
* See the class description of K3DockWidgetHeaderDrag!
* More or less a minor helper class for the dockwidget class set.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockWidgetAbstractHeaderDrag : public QFrame
{
Q_OBJECT
public:
/**
* Constructs this.
*
* @param parent the parent widget (usually a dockwidget header)
* @param dock the dockwidget where it belongs to
* @param name the object instance name
*/
K3DockWidgetAbstractHeaderDrag( K3DockWidgetAbstractHeader* parent,
K3DockWidget* dock, const char* name = 0L );
/**
* Destructs this.
*/
virtual ~K3DockWidgetAbstractHeaderDrag();
/**
* @return the dockwidget where this belongs to
*/
K3DockWidget* dockWidget() const;
private:
/**
* the dockwidget where this belongs to
*/
K3DockWidget* dw;
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockWidgetAbstractHeaderDragPrivate;
K3DockWidgetAbstractHeaderDragPrivate *d;
};
/**
* This special widget is the panel one can grip with the mouses (and member of the dockwidget class set).
* The widget for dragging, so to speak.
* Usually it is located in the K3DockWidgetHeader.
* More or less a minor helper class for the dockwidget class set.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockWidgetHeaderDrag : public K3DockWidgetAbstractHeaderDrag
{
Q_OBJECT
public:
/**
* Constructs this.
*
* @param parent the parent widget (usually a dockwidget header)
* @param dock the dockwidget where it belongs to
* @param name the object instance name
*/
K3DockWidgetHeaderDrag( K3DockWidgetAbstractHeader* parent, K3DockWidget* dock,
const char* name = 0L );
/**
* Destructs this.
*/
virtual ~K3DockWidgetHeaderDrag();
protected:
/**
* Draws the drag panel (a double line)
*/
virtual void paintEvent( QPaintEvent* );
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockWidgetHeaderDragPrivate;
K3DockWidgetHeaderDragPrivate *d;
};
/**
* The header (additional bar) for a K3DockWidget s (and member of the dockwidget class set).
* It have got the buttons located there. And it is for recording and reading the button states.
* More or less a minor helper class for the dockwidget class set.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockWidgetHeader : public K3DockWidgetAbstractHeader
{
Q_OBJECT
public:
/**
* Constructs this.
*
* @param parent the parent widget (usually a dockwidget)
* @param name the object instance name
*/
K3DockWidgetHeader( K3DockWidget* parent, const char* name = 0L );
/**
* Destructs this.
*/
virtual ~K3DockWidgetHeader();
/**
* Hides the close button and stay button when switching to toplevel or vice versa shows them.
*
* @param t toplevel or not
*/
virtual void setTopLevel( bool t);
/**
* Sets the drag panel of this header.
*
* @param nd A pointer to the new drag panel
*/
void setDragPanel( K3DockWidgetHeaderDrag* nd );
/**
* Get the drag panel of this header.
*/
K3DockWidgetHeaderDrag *dragPanel();
bool dragEnabled() const;
void setDragEnabled(bool b);
void showUndockButton(bool show);
void forceCloseButtonHidden(bool enable=true);
#ifndef NO_KDE2
/**
* Saves the current button state to a KDE config container object.
*
* @param c the configuration safe
*/
virtual void saveConfig( KConfigGroup* c);
/**
* Loads the current button state from a KDE config container object.
*
* @param c the configuration safe
*/
virtual void loadConfig( KConfigGroup* c);
#endif
/**
* add an arbitrary button to the dockwidget header
* NOT PART OF THE PUBLIC API (you don't have access the class definition anyways, without special
* header file copying. (don't do it))
*/
void addButton(K3DockButton_Private*);
/**
* remove an arbtrary button from the dockwidget header
* NOT PART OF THE PUBLIC API (you don't have access the class definition anyways, without special
* header file copying. (don't do it))
*/
void removeButton(K3DockButton_Private*);
protected Q_SLOTS:
/**
* Sets dragging the dockwidget off when the stay button is pressed down and vice versa.
*/
void slotStayClicked();
protected:
/**
* A layout manager for placing the embedded buttons (close and stay)
*/
QHBoxLayout* layout;
/**
* a little button for closing (undocking and hiding) the dockwidget
*/
K3DockButton_Private* closeButton;
/**
* a little button for enabling/disabling dragging the dockwidget with the mouse
*/
K3DockButton_Private* stayButton;
/**
* a little button for dock back the dockwidget to its previous dockwidget
*/
K3DockButton_Private* dockbackButton;
/**
* the drag panel (double line)
*/
K3DockWidgetHeaderDrag* drag;
protected:
virtual void virtual_hook( int id, void* data );
private:
K3DockWidgetHeaderPrivate *d;
};
/**
* It just hides the special implementation of a dockwidget tab groups (and is member of the dockwidget class set).
* An abstraction what it is currently.
* In general it is like QTabWidget but is more useful for the dockwidget class set.
* More or less a minor helper class for the dockwidget class set.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockTabGroup : public QTabWidget
{
Q_OBJECT
public:
/**
* Constructs this. It just calls the method of the base class.
*/
K3DockTabGroup( QWidget *parent = 0, const char *name = 0 );
/**
* Destructs a K3DockTabGroup.
*/
virtual ~K3DockTabGroup();
QWidget *transientTo();
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockTabGroupPrivate;
K3DockTabGroupPrivate *d;
};
/**
* Floatable widget that can be dragged around with the mouse and
* encapsulate the actual widgets (and member of the dockwidget class
* set).
*
* You just grip the double-lined panel, tear it off its parent
* widget, drag it somewhere and let it loose. Depending on the
* position where you leave it, the dockwidget becomes a toplevel
* window on the desktop (floating mode) or docks to a new widget
* (dock mode). Note: A K3DockWidget can only be docked to a
* K3DockWidget.
*
* If you want to use this kind of widget, your main application
* window has to be a K3DockMainWindow. That is because it has
* got several additional dock management features, for instance a
* K3DockManager that has an overview over all dockwidgets and and
* a dockmovemanager (internal class) that handles the dock process.
*
* Usually you create an K3DockWidget that covers the actual widget in this way:
* \code
* ...
* K3DockMainWindow* mainWidget;
* ...
* K3DockWidget* dock = 0L;
* dock = mainWidget->createDockWidget( "Any window caption", nicePixmap, 0L, i18n("window caption")); // 0L==no parent
* QWidget* actualWidget = new QWidget( dock);
* dock->setWidget( actualWidget); // embed it
* dock->setToolTipString(i18n("That's me")); // available when appearing as tab page
* ...
* \endcode
*
* See K3DockMainWindow how a dockwidget is docked in.
*
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockWidget: public QWidget
{
Q_OBJECT
friend class K3DockManager;
friend class K3DockSplitter;
friend class K3DockMainWindow;
friend class K3DockArea;
public:
/**
* Construct a dockwidget.
*
* Initially, docking to another and docking to this is allowed for
* every @p DockPosition. It is supposed to be no (tab) group. It will
* taken under control of its dockmanager.
*
* @param dockManager The responsible manager (dock helper)
* @param name Object instance name
* @param pixmap An icon (for instance shown when docked centered)
* @param parent Parent widget
* @param strCaption Title of the dockwidget window (shown when toplevel)
* @param strTabPageLabel The title of the tab page (shown when in tab page mode), if it is "", only the icon will be shown, if it is 0L, the label is set to strCaption
* @param f Qt::WidgetFlags widget flags
*/
K3DockWidget( K3DockManager* dockManager, const char* name,
const QPixmap &pixmap, QWidget* parent = 0L, const QString& strCaption = QString(),
- const QString& strTabPageLabel = QLatin1String( " " ), Qt::WFlags f = 0);
+ const QString& strTabPageLabel = QLatin1String( " " ), Qt::WindowFlags f = 0);
/**
* Destructs a dockwidget.
*/
virtual ~K3DockWidget();
/**
* The possible positions where a dockwidget can dock to another dockwidget
*/
enum DockPosition
{
DockNone = 0,
DockTop = 0x0001,
DockLeft = 0x0002,
DockRight = 0x0004,
DockBottom = 0x0008,
DockCenter = 0x0010,
DockDesktop= 0x0020,
DockToSpecialSites=0x0040,
DockCorner = DockTop | DockLeft | DockRight | DockBottom,
DockFullSite = DockCorner | DockCenter,
DockFullDocking = DockFullSite | DockDesktop
};
/**
* This is a key method of this class! Use it to dock dockwidgets to
* another dockwidget at the right position within its
* K3DockMainWindow or a toplevel dockwidget.
*
*
* If the target is null, it will become a toplevel dockwidget at position pos;
* Note: Docking to another dockwidget means exactly:
* A new parent dockwidget will be created, that replaces the target dockwidget and contains another single helper widget (tab widget or panner)
* which contains both dockwidgets, this and the target dockwidget. So consider parent<->child relationships change completely during such actions.
*
* @param target The dockwidget to dock to
* @param dockPos One of the DockPositions this is going to dock to
* @param spliPos The split relation (in percent, or percent*100 in high resolution) between both dockwidgets, target and this
* @param pos The dock position, mainly of interest for docking to the desktop (as toplevel dockwidget)
* @param check Only for internal use;
* @param tabIndex The position index of the tab widget (when in tab page mode), -1 (default) means append
* @return result The group dockwidget that replaces the target dockwidget and will be grandparent of target and @p this.
*
* @note Since KDE 3.5 the splitter position @p spliPos is always a value between [0..100]. If
* the value is > 100, it will be treated like the old code and normalized to a value between
* 0 and 100. Example: If the value was 4000, it will be fixed to 40. In short: The old and
* the new behavior both work, so it is compatible with older KDE-versions.
*/
K3DockWidget* manualDock( K3DockWidget* target, DockPosition dockPos, int spliPos = 50, QPoint pos = QPoint(0,0), bool check = false, int tabIndex = -1);
/**
* Specify where it is either possible or impossible for this to dock to another dockwidget.
*
* @param pos An OR'ed set of @p DockPositions
*/
void setEnableDocking( int pos );
/**
* @return Where it is either possible or impossible for this to dock to another dockwidget (an OR'ed set of DockPositions).
*/
int enableDocking() const;
/**
* Specify where it is either possible or impossible for another dockwidget to dock to this.
*
* @param pos An OR'ed set of @p DockPositions
*/
void setDockSite( int pos );
/**
* @return There it is either possible or impossible for another dockwidget to dock to this (an OR'ed set of @p DockPositions).
*/
int dockSite() const;
/**
* Sets the embedded widget.
*
* A QLayout takes care about proper resizing, automatically.
*
* @param w The pointer to the dockwidget's child widget.
*/
void setWidget( QWidget* w);
/**
* Get the embedded widget.
*
* @return The pointer to the dockwidget's child widget, 0L if there's no such child.
*/
QWidget* getWidget() const;
/**
* Sets the header of this dockwidget.
*
* A QLayout takes care about proper resizing, automatically.
* The header contains the drag panel, the close button and the stay button.
*
* @param ah A base class pointer to the dockwidget header
*/
void setHeader( K3DockWidgetAbstractHeader* ah);
/**
* get the pointer to the header widget
*/
K3DockWidgetAbstractHeader *getHeader();
/**
* Normally it simply shows the dockwidget.
*
* But additionally, if it is docked to a tab widget (@p DockCenter), it is set as the active (visible) tab page.
*/
void makeDockVisible();
/**
* @return If it may be possible to hide this.
*
* There are reasons that it's impossible:
* @li It is a (tab) group.
* @li It is already invisible ;-)
* @li The parent of this is the K3DockMainWindow.
* @li It isn't able to dock to another widget.
*/
bool mayBeHide() const;
/**
* @return If it may be possible to show this.
* There are reasons that it's impossible:
* @li It is a (tab) group.
* @li It is already visible ;-)
* @li The parent of this is the @p K3DockMainWindow.
*/
bool mayBeShow() const;
/**
* @return The dockmanager that is responsible for this.
*/
K3DockManager* dockManager() const;
/**
* Stores a string for a tooltip.
*
* That tooltip string has only a meaning when this dockwidget is shown as tab page.
* In this case the tooltip is shown when one holds the mouse cursor on the tab page header.
* Such tooltip will for instance be useful, if you use only icons there.
* Note: Setting an empty string switches the tooltip off.
*
* @param ttStr A string for the tooltip on the tab.
*/
void setToolTipString(const QString& ttStr);
/**
* @return The tooltip string being shown on the appropriate tab page header when in dock-centered mode.
*/
const QString& toolTipString() const;
/**
* @return result @p true, if a dockback is possible, otherwise @p false.
*/
bool isDockBackPossible() const;
/**
* Sets a string that is used for the label of the tab page when in tab page mode
* @param label The new tab page label.
*/
void setTabPageLabel( const QString& label);
/**
* @return A string that is used for the label of the tab page when in tab page mode.
*/
const QString& tabPageLabel() const;
/**
* Catches and processes some QWidget events that are interesting for dockwidgets.
*/
virtual bool event( QEvent * );
/**
* Add dockwidget management actions to QWidget::show.
*/
virtual void show();
/**
* @return the parent widget of this if it inherits class K3DockTabGroup
*/
K3DockTabGroup* parentDockTabGroup() const;
QWidget *parentDockContainer() const;
#ifndef NO_KDE2
/**
* Sets the type of the dock window
*
* @param windowType is type of dock window
*/
void setDockWindowType (NET::WindowType windowType);
#endif
void setDockWindowTransient (QWidget *parent, bool transientEnabled);
/**
* Returns the widget this dockwidget is set transient to, otherwise 0
*/
QWidget *transientTo();
/**
* Lookup the nearest dockwidget docked left/right/top/bottom to this one or return 0
*
* @param pos is the position the wanted widget is docked to this one
*/
K3DockWidget *findNearestDockWidget(DockPosition pos);
/**
* Allows changing the pixmap which is used for the caption or dock tabs
*
* @param pixmap is the pixmap to set
*/
void setPixmap(const QPixmap& pixmap=QPixmap());
/**
* Returns the dockwidget's associated caption/dock tab pixmap
*/
const QPixmap& pixmap() const;
/**
* @return the current dock position.
*/
K3DockWidget::DockPosition currentDockPosition() const;
public Q_SLOTS:
/**
* subject to changes. It doesn't completely work yet without small hacks from within the calling application (Perhaps
* KDE 3.1.x oder 3.2
* width is in pixel. It only affects a widget, which is placed directly into a horizontal K3DockSplitter
**/
void setForcedFixedWidth(int);
/**
* subject to changes. It doesn't completely work yet without small hacks from within the calling application (Perhaps
* KDE 3.1.x oder 3.2
* height is in pixel. It only affects a widget, which is placed directly into a vertical K3DockSplitter
**/
void setForcedFixedHeight(int);
void restoreFromForcedFixedSize();
int forcedFixedWidth();
int forcedFixedHeight();
/**
* Docks a dockwidget back to the dockwidget that was the neighbor
widget before the current dock position.
*/
void dockBack();
/**
* Toggles the visibility state of the dockwidget if it is able to be shown or to be hidden.
*/
void changeHideShowState();
/**
* Undocks this. It means it becomes a toplevel widget framed by the system window manager.
* A small panel at the top of this undocked widget gives the possibility to drag it into
* another dockwidget by mouse (docking).
*/
void undock();
/**
* Docks the widget to the desktop (as a toplevel widget)
*/
void toDesktop( );
protected:
friend class K3MdiMainFrm;
friend class K3MDI::MainWindow;
/**
* Checks some conditions and shows or hides the dockwidget header (drag panel).
* The header is hidden if:
* @li the parent widget is the K3DockMainWindow
* @li this is a (tab) group dockwidget
* @li it is not able to dock to another dockwidget
*/
void updateHeader();
void setLatestK3DockContainer(QWidget *);
QWidget *latestK3DockContainer();
void setFormerBrotherDockWidget(K3DockWidget *);
Q_SIGNALS:
/**
*is emitted after the setWidget method has finished
*/
void widgetSet(QWidget*);
/**
* Emitted when another dock widget is docking to this.
*
* @param dw the dockwidget that is docking to this
* @param dp the DockPosition where it wants to dock to
*/
void docking( K3DockWidget* dw, K3DockWidget::DockPosition dp);
/**
* Signals that the dock default position is set.
*/
void setDockDefaultPos();
/**
* Emitted when the close button of the panel ( K3DockWidgetHeader) has been clicked.
*/
void headerCloseButtonClicked();
/**
* Emitted when the dockback button of the panel ( K3DockWidgetHeader) has been clicked.
*/
void headerDockbackButtonClicked();
/**
* Emitted when the widget processes a close event.
*/
void iMBeingClosed();
/**
* Emitted when the widget has undocked.
*/
void hasUndocked();
protected Q_SLOTS:
/** Does several things here when it has noticed that the former brother widget (closest neighbor) gets lost.
* The former brother widget is needed for a possible dockback action, to speak with the Beatles:
* "To get back to where you once belonged" ;-)
*/
void loseFormerBrotherDockWidget();
virtual void paintEvent(QPaintEvent*);
virtual void mousePressEvent(QMouseEvent*);
virtual void mouseReleaseEvent(QMouseEvent*);
virtual void mouseMoveEvent(QMouseEvent*);
virtual void leaveEvent(QEvent*);
protected:
friend class K3DockWidgetHeader;
/**
* earlier closest neighbor widget, so it's possible to dock back to it.
*/
K3DockWidget* formerBrotherDockWidget;
/**
* the current dock position.
*/
DockPosition currentDockPos;
/**
* the former dock position when it really was at another position before.
*/
DockPosition formerDockPos;
/**
* a string used as tooltip for the tab page header when in dock-centered mode.
*/
QString toolTipStr;
/**
* a string used as title of the tab page when in tab page mode
*/
QString tabPageTitle;
private:
/**
* Sets the caption (window title) of the given tab widget.
*
* @param g the group (tab) widget
*/
void setDockTabName( K3DockTabGroup* g);
/**
* Reparent to s or set this to the K3DockMainWindow's view if s is that dockmainwindow.
* If s is O, simply move the widget.
*
* @param s the target widget to reparent to
* @param p the point to move to (if it doesn't reparent)
*/
void applyToWidget( QWidget* s, const QPoint& p = QPoint(0,0) );
/**
* A base class pointer to the header of this dockwidget
*/
K3DockWidgetAbstractHeader* header;
/**
* the embedded widget
*/
QWidget* widget;
/**
* the layout manager that takes care about proper resizing and moving the embedded widget and the header
*/
QVBoxLayout* layout;
/**
* the responsible dockmanager
*/
K3DockManager* manager;
/**
* an icon for the tab widget header
*/
QPixmap* pix;
/**
* Information about the ability for docking to another dockwidget.
*/
int eDocking;
/**
* Information which site of this dockwidget is free for docking of other dockwidgets.
*/
int sDocking;
/**
* Previous side (left,right,top,bottom) where this dockwidget was before a dragging action, none if it wasn't dragged before.
*/
K3DockWidget::DockPosition prevSideDockPosBeforeDrag;
// GROUP data
QString firstName;
QString lastName;
Qt::Orientation splitterOrientation;
bool isGroup;
bool isTabGroup;
protected:
virtual void virtual_hook( int id, void* data );
private:
K3DockWidgetPrivate *d;
};
/**
* The manager that knows all dockwidgets and handles the dock process (and member of the dockwidget class set).
* More or less a helper class for the K3DockWidget class set but of interest for some functionality
* that can be called within a K3DockMainWindow or a K3DockWidget .
*
* An important feature is the ability to read or save the current state of all things concerning to
* dockwidgets to KConfig .
*
* The dockmanager is also often used when a certain dockwidget or a child of such dockwidget must be found.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockManager: public QObject
{
Q_OBJECT
friend class K3DockWidget;
friend class K3DockMainWindow;
public:
enum EnReadDockConfigMode {
Unknown,
WrapExistingWidgetsOnly,
RestoreAllDockwidgets
};
public:
/**
* Constructs a dockmanager. Some initialization happen:
* @li It installs an event filter for the main window,
* @li a control list for dock objects
* @li a control list for menu items concerning to menus provided by the dockmanager
* @li Some state variables are set
*
* @param mainWindow the main window controlled by this
* @param name the internal QOject name
*/
K3DockManager( QWidget* mainWindow, const char* name = 0L );
/**
* Destructs a dockmanager.
*/
virtual ~K3DockManager();
void dumpDockWidgets();
#ifndef NO_KDE2
/**
* Saves the current state of the dockmanager and of all controlled widgets.
* State means here to save the geometry, visibility, parents, internal object names, orientation,
* separator positions, dockwidget-group information, tab widget states (if it is a tab group) and
* last but not least some necessary things for recovering the dockmainwindow state.
*
* @param c the KDE configuration saver
* @param group the name of the section in KConfig
*/
void writeConfig( KConfig* c = 0L, const QString &group = QString() );
/**
* Like writeConfig but reads the whole stuff in.
*
* In order to restore a window configuration
* from a config file, it looks up widgets by name
* (QObject::name) in the childDock variable of
* K3DockManager. This list in turn contains all
* K3DockWidgets (according to the K3DockWidget constructor).
* So in principle, in order to restore a window layout,
* one must first construct all widgets, put each of them in a
* K3DockWidget and then call readConfig(). And for all that
* to work, each widget must have a unique name.
*
* @param c the KDE configuration saver
* @param group the name of the section in KConfig
*/
void readConfig ( KConfig* c = 0L, const QString &group = QString() );
#endif
void setMainDockWidget2(K3DockWidget *);
/**
* Saves the current dock window layout into a DOM tree below the given element.
*/
void writeConfig(QDomElement &base);
/**
* Reads the current dock window layout from a DOM tree below the given element.
*/
void readConfig(QDomElement &base);
/**
* Shows all encapsulated widgets of all controlled dockwidgets and shows all dockwidgets which are
* parent of a dockwidget tab group.
*/
void activate();
/**
* It's more or less a method that catches several events which are interesting for the dockmanager.
* Mainly mouse events during the drag process of a dockwidgets are of interest here.
*
* @param object the object that sends the event
* @param event the event
* @return the return value of the method call of the base class method
*/
virtual bool eventFilter( QObject * object, QEvent * event );
/**
* This method finds out what a widgets' dockwidget is. That means the dockmanager has a look at all
* dockwidgets it knows and tells you when one of those dockwidgets covers the given widget.
*
* @param w any widget that is supposed to be encapsulated by one of the controlled dockwidgets
* @return the dockwidget that encapsulates that widget, otherwise 0
*/
K3DockWidget* findWidgetParentDock( QWidget* w) const;
/**
* Works like makeDockVisible() but can be called for widgets that covered by a dockwidget.
*
* @param w the widget that is encapsulated by a dockwidget that turns to visible.
*/
void makeWidgetDockVisible( QWidget* w );
/**
* @return the popupmenu for showing/hiding dockwidgets
*/
Q3PopupMenu* dockHideShowMenu() const;
/**
* @param dockName an internal QObject name
* @return the dockwidget that has got that internal QObject name
*/
K3DockWidget* getDockWidgetFromName( const QString& dockName );
/**
* Enables opaque resizing. Opaque resizing defaults to KGlobalSettings::opaqueResize().
* Call this method before you create any dock widgets!
*/
void setSplitterOpaqueResize(bool b=true);
/**
* Returns true if opaque resizing is enabled, false otherwise.
*/
bool splitterOpaqueResize() const;
/**
* Try to preserve the widget's size. Works like KeepSize resize mode
* of QSplitter. Off by default.
* Call this method before you create any dock widgets!
*/
void setSplitterKeepSize(bool b=true);
/**
* Returns true if the KeepSize is enabled, false otherwise.
*/
bool splitterKeepSize() const;
/**
* Operate the splitter with a higher resolution. Off by default.
* Call this method before you create any dock widgets!
* If high resolution is used all splitter position parameters
* are percent*100 instead of percent.
* @note Since KDE 3.5 this is ignored. Internally the splitter always
* calcualtes in high resolution values. For KDE 4, this will be removed.
*/
void setSplitterHighResolution(bool b=true);
/**
* Returns true if the splitter uses the high resolution, false otherwise.
*/
bool splitterHighResolution() const;
void setSpecialLeftDockContainer(K3DockWidget* container);
void setSpecialTopDockContainer(K3DockWidget* container);
void setSpecialRightDockContainer(K3DockWidget* container);
void setSpecialBottomDockContainer(K3DockWidget* container);
void removeFromAutoCreateList(K3DockWidget* pDockWidget);
void finishReadDockConfig();
void setReadDockConfigMode(int mode);
Q_SIGNALS:
/**
* Signals changes of the docking state of a dockwidget. Usually the dock-toolbar will be updated then.
*/
void change();
/**
* Signals a dockwidget is replaced with another one.
*/
void replaceDock( K3DockWidget* oldDock, K3DockWidget* newDock );
/**
* Signals a dockwidget without parent (toplevel) is shown.
*/
void setDockDefaultPos( K3DockWidget* );
private Q_SLOTS:
/**
* Clears the popupmenu for showing/hiding dockwidgets and fills it with the current states of all controlled dockwidgets.
*/
void slotMenuPopup();
/**
* This method assumes a menuitem of the popupmenu for showing/hiding dockwidgets is selected and toggles that state.
*
* @param id the popupmenu id of the selected menuitem
*/
void slotMenuActivated( int id);
/* clears the old drawn drag rectangle (oldDragRect) from screen and
* draws the new current drag rectangle (dragRect) depending on the current mouse position.
* This highlights the dockwidget which is the currently chosen target during a dock action.
*/
void drawDragRectangle();
private:
/**
* A data structure containing data about every dockwidget that is under control.
*/
struct MenuDockData
{
MenuDockData( K3DockWidget* _dock, bool _hide )
{
dock = _dock;
hide = _hide;
}
~MenuDockData(){}
K3DockWidget* dock;
bool hide;
};
/**
* Finds the K3DockWidget at the position given as parameter
*
* @param pos global (desktop) position of the wanted dockwidget
* @return the dockwidget at that position
*/
K3DockWidget* findDockWidgetAt( const QPoint& pos );
/**
* Finds the QWidget recursively at the position given as parameter
*
* @param w a variable where the method puts the QWidget at that position (instead of a return value)
* @param p the parent widget where the recursive search should start from
* @param pos global (desktop) position of the wanted dockwidget
*/
void findChildDockWidget( QWidget*& w, const QWidget* p, const QPoint& pos );
/**
* Finds all dockwidgets which are child, grandchild and so on of p.
*
* @param p the parent widget where the recursive search starts from
* @param l the widget list that contains the search result after the return of this method
*/
void findChildDockWidget( const QWidget* p, QWidgetList*& l);
/**
* Sets a dockwidget in drag mode.
*/
void startDrag( K3DockWidget* );
/**
* Moves a dockwidget that is in drag mode.
*
* @param d the dockwidget which is dragged
* @param pos the new position of the dragged dockwidget
*/
void dragMove( K3DockWidget* d, QPoint pos );
/**
* Aborts the drag mode. Restores the cursor and hides the drag indicator.
*/
void cancelDrop();
/**
* Finishes the drag mode. If the user let it drop on an other dockwidget, it will possibly be docked (if allowed),
* if the user drops it outside of the application window it becomes toplevel.
*/
void drop();
// class members
/**
* Usually the K3DockMainWindow but not necessarily.
*/
QWidget* main;
/**
* The dockwidget that is being dragged at the moment
*/
K3DockWidget* currentDragWidget;
/**
* The target dockwidget where the currentDragWidget is dropped
*/
K3DockWidget* currentMoveWidget; // widget where mouse moving
/**
* It is of interest during the dock process. Then it contains all child dockwidgets.
*/
QWidgetList* childDockWidgetList;
/**
* The dockposition where the dockwidget would be docked to, if we dropped it here.
*/
K3DockWidget::DockPosition curPos;
/**
* A QList of all objects that are important for docking.
* Some serve as group widgets of dockwidgets, others encapsulate normal widgets.
*/
QObjectList* childDock;
/**
* Contains dockwidgets that are created automatically by the dockmanager. For internal use.
*/
QObjectList* autoCreateDock;
/**
* For storing the width during the dragging of a dockwidget.
*/
int storeW;
/**
* For storing the height during the dragging of a dockwidget.
*/
int storeH;
/**
* State variable if there is a drag process active.
*/
bool dragging;
/**
* State variable if there is an undock process active
*/
bool undockProcess;
/**
* The dockmanager sets it to true if the user cancels the drag by moving the cursor
* on a invalid drop place
*/
bool dropCancel;
/**
* A popup menu that contains one menuitem for each dockwidget that shows the current visibility state and
* to show or hide the appropriate dockwidget.
*/
Q3PopupMenu* menu;
/**
* An internal list containing data for the menuitems for the visibility popup menu.
*/
Q3PtrList<MenuDockData> *menuData;
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockManagerPrivate;
K3DockManagerPrivate *d;
};
/**
* A special kind of KMainWindow that is able to have dockwidget child widgets (and member of the dockwidget class set).
*
* The main widget should be a K3DockWidget where other K3DockWidget can be docked to
* the left, right, top, bottom or to the middle.
* Note: dock to the middle means to drop on a dockwidget and to unite them to a new widget, a tab control.
*
* Furthermore, the K3DockMainWindow has got the K3DockManager and some data about the dock states.
*
* If you've got some dockwidgets, you can dock them to the dockmainwindow to initialize a start scene:
* Here an example:
* \code
* DockApplication::DockApplication( const char* name) : K3DockMainWindow( name)
* {
* ...
* K3DockWidget* mainDock;
* mainDock = createDockWidget( "Falk's MainDockWidget", mainPixmap, 0L, "main_dock_widget");
* AnyContentsWidget* cw = new AnyContentsWidget( mainDock);
* mainDock->setWidget( cw);
* // allow others to dock to the 4 sides
* mainDock->setDockSite(K3DockWidget::DockCorner);
* // forbit docking abilities of mainDock itself
* mainDock->setEnableDocking(K3DockWidget::DockNone);
* setView( mainDock); // central widget in a KDE mainwindow
* setMainDockWidget( mainDock); // master dockwidget
* ...
* K3DockWidget* dockLeft;
* dockLeft = createDockWidget( "Intially left one", anyOtherPixmap, 0L, i18n("The left dockwidget"));
* AnotherWidget* aw = new AnotherWidget( dockLeft);
* dockLeft->setWidget( aw);
* dockLeft->manualDock( mainDock, // dock target
* K3DockWidget::DockLeft, // dock site
* 20 ); // relation target/this (in percent)
* ...
* \endcode
*
* Docking is fully dynamic at runtime. That means you can always move dockwidgets via drag and drop.
*
* And last but not least you can use the popupmenu for showing or hiding any controlled dockwidget
* of this class and insert it to your main menu bar or anywhere else.
*
* @author Max Judin (documentation: Falk Brettschneider).
*/
class KDE3SUPPORT_EXPORT K3DockMainWindow : public KXmlGuiWindow
{
Q_OBJECT
friend class K3DockManager;
public:
/**
* Constructs a dockmainwindow. It calls its base class constructor and does additional things concerning
* to the dock stuff:
* @li information about the dock state of this' children gets initialized
* @li a dockmanager is created...
* @li ...and gets initialized
* @li the main dockwidget is set to 0
*
* @param parent Parent widget for the dock main widget
* @param name internal object name
* @param f Qt::WidgetFlags widget flags
*/
- K3DockMainWindow( QWidget* parent = 0L, const char *name = 0L, Qt::WFlags f = Qt::Window);
+ K3DockMainWindow( QWidget* parent = 0L, const char *name = 0L, Qt::WindowFlags f = Qt::Window);
/**
* Destructs a dockmainwindow.
*/
virtual ~K3DockMainWindow();
/**
* Returns the dockmanager of this. (see K3DockManager)
* @return pointer to the wanted dockmanager
*/
K3DockManager* manager() const;
/**
* Sets a new main dockwidget.
* Additionally, the toolbar is re-initialized.
*
* @param dockwidget dockwidget that become the new main dockwidget
*/
void setMainDockWidget( K3DockWidget* dockwidget);
/**
* Returns the main dockwidget.
*
* @return pointer to the main dockwidget
*/
K3DockWidget* getMainDockWidget() const;
/**
* This is one of the most important methods!
* The K3DockMainWindow creates a new dockwidget object here that usually should encapsulate the user's widget.
* The new dockwidget is automatically taken under control by the dockmanager of the dockmainwindow.
*
* @param name QObject name (default dockwidget caption)
* @param pixmap window icon (for instance shown when docked as tabwidget entry)
* @param parent parent widget for the new dockwidget
* @param strCaption window title (shown when toplevel)
* @param strTabPageLabel title of the tab page (visible when in tab page mode), if it is "", only the icon will be shown; if it is 0L, the label is set to strCaption
* @return a pointer to the new created dockwidget
*/
K3DockWidget* createDockWidget( const QString& name, const QPixmap &pixmap, QWidget* parent = 0L,
const QString& strCaption = QString(), const QString& strTabPageLabel = QLatin1String( " " ) );
/**
* Saves the current dock window layout into a DOM tree below the given element.
*/
void writeDockConfig(QDomElement &base);
/**
* Reads the current dock window layout from a DOM tree below the given element.
*/
void readDockConfig(QDomElement &base);
#ifndef NO_KDE2
/**
* It writes the current dock state in the given section of KConfig.
*
* @param c KDE class for saving configurations
* @param group name of section to write to
*/
void writeDockConfig( KConfig* c = 0L, const QString &group = QString() );
/**
* It reads the current dock state from the given section of KConfig.
*
* @param c KDE class for saving configurations
* @param group name of section to read from
*/
void readDockConfig ( KConfig* c = 0L, const QString &group = QString() );
#endif
/**
* It runs through all dockwidgets which are under control of the dockmanager and calls show() for every
* encapsulated widget and show() for the dockwidget itself if it is not in tab mode.
* Additionally, if the main dockwidget is not a QDialog, it will be shown.
*/
void activateDock();
/**
* Returns a popup menu that contains entries for all controlled dockwidgets making hiding and showing
* them possible.
*
* @return the wanted popup menu
*/
Q3PopupMenu* dockHideShowMenu() const;
/**
* This method shows the given dockwidget.
* The clue is that it also considers the dockwidget could be a tab page
* and must set to be the activate one.
*
* @param dock the dockwidget that is to be shown
*/
void makeDockVisible( K3DockWidget* dock );
/**
* This method hides the given dockwidget.
*
* @param dock the dockwidget that is to be shown
*/
void makeDockInvisible( K3DockWidget* dock );
/**
* This is an overloaded member function, provided for convenience.
* It differs from the above function only in what argument(s) it accepts.
*/
void makeWidgetDockVisible( QWidget* widget );
/**
* This method calls the base class method.
* If the given widget inherits K3DockWidget, applyToWidget(this) is called.
*
* @param widget any widget that should become the main view
*/
void setView( QWidget * widget );
Q_SIGNALS:
/**
* Signals a certain dockwidget is undocked now.
*/
void dockWidgetHasUndocked(K3DockWidget*);
protected:
/**
* A pointer to the main dockwidget (where one can manualDock() to
*/
K3DockWidget* mainDockWidget;
/**
* A pointer to the manager for the dock process
*/
K3DockManager* dockManager;
protected Q_SLOTS:
/**
* Called whenever one of the dockwidgets of this has been undocked.
*/
void slotDockWidgetUndocked();
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockMainWindowPrivate;
K3DockMainWindowPrivate *d;
};
class KDE3SUPPORT_EXPORT K3DockArea : public QWidget
{
Q_OBJECT
friend class K3DockManager;
public:
K3DockArea( QWidget* parent = 0L, const char *name = 0L);
virtual ~K3DockArea();
K3DockManager* manager();
void setMainDockWidget( K3DockWidget* );
K3DockWidget* getMainDockWidget();
K3DockWidget* createDockWidget( const QString& name, const QPixmap &pixmap, QWidget* parent = 0L,
const QString& strCaption = QString(), const QString& strTabPageLabel = QLatin1String( " " ) );
void writeDockConfig(QDomElement &base);
void readDockConfig(QDomElement &base);
#ifndef NO_KDE2
void writeDockConfig( KConfig* c = 0L, const QString &group = QString() );
void readDockConfig ( KConfig* c = 0L, const QString &group = QString() );
#endif
void activateDock();
Q3PopupMenu* dockHideShowMenu();
void makeDockVisible( K3DockWidget* dock );
void makeDockInvisible( K3DockWidget* dock );
void makeWidgetDockVisible( QWidget* widget );
//void setView( QWidget* );
Q_SIGNALS:
/**
* Signals a certain dockwidget is undocked now.
*/
void dockWidgetHasUndocked(K3DockWidget*);
protected:
K3DockWidget* mainDockWidget;
K3DockManager* dockManager;
protected Q_SLOTS:
void slotDockWidgetUndocked();
public:
virtual void resizeEvent(QResizeEvent *);
protected:
virtual void virtual_hook( int id, void* data );
private:
class K3DockMainWindowPrivate;
K3DockMainWindowPrivate *d;
};
#endif
diff --git a/kde3support/kdeui/k3iconview.cpp b/kde3support/kdeui/k3iconview.cpp
index 5ad41daa29..234594fd78 100644
--- a/kde3support/kdeui/k3iconview.cpp
+++ b/kde3support/kdeui/k3iconview.cpp
@@ -1,736 +1,736 @@
/* This file is part of the KDE libraries
Copyright (C) 1999 Torben Weis <weis@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 version 2 as published by the Free Software Foundation.
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 "k3iconview.h"
#include <QtCore/QTimer>
#include <QPainter>
#include <QPixmapCache>
#include <QActionEvent>
#include "kwordwrap.h"
#include <kconfig.h>
#include <kdebug.h>
#include <kiconeffect.h>
#include <kglobalsettings.h>
#include <kcursor.h>
#include <QApplication>
class K3IconView::K3IconViewPrivate
{
public:
K3IconViewPrivate() {
mode = K3IconView::Execute;
fm = 0L;
doAutoSelect = true;
textHeight = 0;
dragHoldItem = 0L;
}
K3IconView::Mode mode;
bool doAutoSelect;
QFontMetrics *fm;
QPixmapCache maskCache;
int textHeight;
Q3IconViewItem *dragHoldItem;
QTimer dragHoldTimer;
QTimer doubleClickIgnoreTimer;
};
-K3IconView::K3IconView( QWidget *parent, const char *name, Qt::WFlags f )
+K3IconView::K3IconView( QWidget *parent, const char *name, Qt::WindowFlags f )
: Q3IconView( parent, name, f )
{
d = new K3IconViewPrivate;
connect( this, SIGNAL(onViewport()),
this, SLOT(slotOnViewport()) );
connect( this, SIGNAL(onItem(Q3IconViewItem*)),
this, SLOT(slotOnItem(Q3IconViewItem*)) );
slotSettingsChanged( KGlobalSettings::SETTINGS_MOUSE );
connect( KGlobalSettings::self(), SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int)) );
m_pCurrentItem = 0L;
m_pAutoSelect = new QTimer( this );
connect( m_pAutoSelect, SIGNAL(timeout()),
this, SLOT(slotAutoSelect()) );
connect( &d->dragHoldTimer, SIGNAL(timeout()), this, SLOT(slotDragHoldTimeout()) );
}
K3IconView::~K3IconView()
{
delete d->fm;
delete d;
}
void K3IconView::setMode( K3IconView::Mode mode )
{
d->mode = mode;
}
K3IconView::Mode K3IconView::mode() const
{
return d->mode;
}
void K3IconView::slotOnItem( Q3IconViewItem *item )
{
if ( item ) {
if ( m_bUseSingle ) {
if ( m_bChangeCursorOverItem )
viewport()->setCursor(QCursor(Qt::PointingHandCursor));
if ( (m_autoSelectDelay > -1) ) {
m_pAutoSelect->setSingleShot( true );
m_pAutoSelect->start( m_autoSelectDelay );
}
}
m_pCurrentItem = item;
}
}
void K3IconView::slotOnViewport()
{
if ( m_bUseSingle && m_bChangeCursorOverItem )
viewport()->unsetCursor();
m_pAutoSelect->stop();
m_pCurrentItem = 0L;
}
void K3IconView::slotSettingsChanged(int category)
{
if ( category != KGlobalSettings::SETTINGS_MOUSE )
return;
m_bUseSingle = KGlobalSettings::singleClick();
//kDebug() << "K3IconView::slotSettingsChanged for mouse, usesingle=" << m_bUseSingle;
disconnect( this, SIGNAL( mouseButtonClicked( int, Q3IconViewItem *,
const QPoint & ) ),
this, SLOT( slotMouseButtonClicked( int, Q3IconViewItem *,
const QPoint & ) ) );
// disconnect( this, SIGNAL( doubleClicked( QIconViewItem *,
// const QPoint & ) ),
// this, SLOT( slotExecute( QIconViewItem *,
// const QPoint & ) ) );
if( m_bUseSingle ) {
connect( this, SIGNAL( mouseButtonClicked( int, Q3IconViewItem *,
const QPoint & ) ),
this, SLOT( slotMouseButtonClicked( int, Q3IconViewItem *,
const QPoint & ) ) );
}
else {
// connect( this, SIGNAL( doubleClicked( QIconViewItem *,
// const QPoint & ) ),
// this, SLOT( slotExecute( QIconViewItem *,
// const QPoint & ) ) );
}
m_bChangeCursorOverItem = KGlobalSettings::changeCursorOverIcon();
m_autoSelectDelay = m_bUseSingle ? KGlobalSettings::autoSelectDelay() : -1;
if( !m_bUseSingle || !m_bChangeCursorOverItem )
viewport()->unsetCursor();
}
void K3IconView::slotAutoSelect()
{
// check that the item still exists
if( index( m_pCurrentItem ) == -1 || !d->doAutoSelect )
return;
//Give this widget the keyboard focus.
if( !hasFocus() )
setFocus();
Qt::KeyboardModifiers keybstate = QApplication::keyboardModifiers();
Q3IconViewItem* previousItem = currentItem();
setCurrentItem( m_pCurrentItem );
if( m_pCurrentItem ) {
//Shift pressed?
if( (keybstate & Qt::ShiftModifier) ) {
//Temporary implementation of the selection until QIconView supports it
bool block = signalsBlocked();
blockSignals( true );
//No Ctrl? Then clear before!
if( !(keybstate & Qt::ControlModifier) )
clearSelection();
bool select = !m_pCurrentItem->isSelected();
bool update = viewport()->updatesEnabled();
viewport()->setUpdatesEnabled( false );
//Calculate the smallest rectangle that contains the current Item
//and the one that got the autoselect event
QRect r;
QRect redraw;
if ( previousItem ) {
r = QRect( qMin( previousItem->x(), m_pCurrentItem->x() ),
qMin( previousItem->y(), m_pCurrentItem->y() ),
0, 0 );
if ( previousItem->x() < m_pCurrentItem->x() )
r.setWidth( m_pCurrentItem->x() - previousItem->x() + m_pCurrentItem->width() );
else
r.setWidth( previousItem->x() - m_pCurrentItem->x() + previousItem->width() );
if ( previousItem->y() < m_pCurrentItem->y() )
r.setHeight( m_pCurrentItem->y() - previousItem->y() + m_pCurrentItem->height() );
else
r.setHeight( previousItem->y() - m_pCurrentItem->y() + previousItem->height() );
r = r.normalized();
}
else
r = QRect( 0, 0, 0, 0 );
//Check for each item whether it is within the rectangle.
//If yes, select it
for( Q3IconViewItem* i = firstItem(); i; i = i->nextItem() ) {
if( i->intersects( r ) ) {
redraw = redraw.unite( i->rect() );
setSelected( i, select, true );
}
}
blockSignals( block );
viewport()->setUpdatesEnabled( update );
repaintContents( redraw, false );
emit selectionChanged();
if( selectionMode() == Q3IconView::Single )
emit selectionChanged( m_pCurrentItem );
//setSelected( m_pCurrentItem, true, (keybstate & ControlButton), (keybstate & ShiftButton) );
}
else if( (keybstate & Qt::ControlModifier) )
setSelected( m_pCurrentItem, !m_pCurrentItem->isSelected(), true );
else
setSelected( m_pCurrentItem, true );
}
else
kDebug() << "K3IconView: That's not supposed to happen!!!!";
}
void K3IconView::emitExecute( Q3IconViewItem *item, const QPoint &pos )
{
if ( d->mode != Execute )
{
// kDebug() << "K3IconView::emitExecute : not in execute mode !";
return;
}
Qt::KeyboardModifiers keybstate = QApplication::keyboardModifiers();
m_pAutoSelect->stop();
//Don't emit executed if in SC mode and Shift or Ctrl are pressed
if( !( m_bUseSingle && ((keybstate & Qt::ShiftModifier) || (keybstate & Qt::ControlModifier)) ) ) {
setSelected( item, false );
viewport()->unsetCursor();
emit executed( item );
emit executed( item, pos );
}
}
void K3IconView::updateDragHoldItem( QDropEvent *e )
{
Q3IconViewItem *item = findItem( e->pos() );
if ( d->dragHoldItem != item)
{
d->dragHoldItem = item;
if( item )
{
d->dragHoldTimer.setSingleShot( true );
d->dragHoldTimer.start( 1000 );
}
else
{
d->dragHoldTimer.stop();
}
}
}
void K3IconView::focusOutEvent( QFocusEvent *fe )
{
m_pAutoSelect->stop();
Q3IconView::focusOutEvent( fe );
}
void K3IconView::leaveEvent( QEvent *e )
{
m_pAutoSelect->stop();
Q3IconView::leaveEvent( e );
}
void K3IconView::contentsMousePressEvent( QMouseEvent *e )
{
if( (selectionMode() == Extended) && (e->modifiers() & Qt::ShiftModifier) && !(e->modifiers() & Qt::ControlModifier) ) {
bool block = signalsBlocked();
blockSignals( true );
clearSelection();
blockSignals( block );
}
Q3IconView::contentsMousePressEvent( e );
d->doAutoSelect = false;
}
void K3IconView::contentsMouseDoubleClickEvent ( QMouseEvent * e )
{
Q3IconView::contentsMouseDoubleClickEvent( e );
Q3IconViewItem* item = findItem( e->pos() );
if( item ) {
if( (e->button() == Qt::LeftButton) && !m_bUseSingle )
emitExecute( item, e->globalPos() );
emit doubleClicked( item, e->globalPos() );
}
d->doubleClickIgnoreTimer.setSingleShot(true);
d->doubleClickIgnoreTimer.start(0);
}
void K3IconView::slotMouseButtonClicked( int btn, Q3IconViewItem *item, const QPoint &pos )
{
//kDebug() << " K3IconView::slotMouseButtonClicked() item=" << item;
if( d->doubleClickIgnoreTimer.isActive() )
return; // Ignore double click
if( (btn == Qt::LeftButton) && item )
emitExecute( item, pos );
}
void K3IconView::contentsMouseReleaseEvent( QMouseEvent *e )
{
d->doAutoSelect = true;
Q3IconView::contentsMouseReleaseEvent( e );
}
void K3IconView::contentsDragEnterEvent( QDragEnterEvent *e )
{
updateDragHoldItem( e );
Q3IconView::contentsDragEnterEvent( e );
}
void K3IconView::contentsDragLeaveEvent( QDragLeaveEvent *e )
{
d->dragHoldTimer.stop();
d->dragHoldItem = 0L;
Q3IconView::contentsDragLeaveEvent( e );
}
void K3IconView::contentsDragMoveEvent( QDragMoveEvent *e )
{
updateDragHoldItem( e );
Q3IconView::contentsDragMoveEvent( e );
}
void K3IconView::contentsDropEvent( QDropEvent* e )
{
d->dragHoldTimer.stop();
Q3IconView::contentsDropEvent( e );
}
void K3IconView::slotDragHoldTimeout()
{
Q3IconViewItem *tmp = d->dragHoldItem;
d->dragHoldItem = 0L;
emit held( tmp );
}
void K3IconView::takeItem( Q3IconViewItem * item )
{
if ( item == d->dragHoldItem )
{
d->dragHoldTimer.stop();
d->dragHoldItem = 0L;
}
Q3IconView::takeItem( item );
}
void K3IconView::cancelPendingHeldSignal()
{
d->dragHoldTimer.stop();
d->dragHoldItem = 0L;
}
void K3IconView::wheelEvent( QWheelEvent *e )
{
if (horizontalScrollBar() && (arrangement() == Q3IconView::TopToBottom)) {
QWheelEvent ce(e->pos(), e->delta(), e->buttons(), e->modifiers(), Qt::Horizontal);
QApplication::sendEvent( horizontalScrollBar(), &ce);
if (ce.isAccepted()) {
e->accept();
return;
}
}
Q3IconView::wheelEvent(e);
}
void K3IconView::setFont( const QFont &font )
{
delete d->fm;
d->fm = 0L;
Q3IconView::setFont( font );
}
QFontMetrics *K3IconView::itemFontMetrics() const
{
if (!d->fm) {
// QIconView creates one too, but we can't access it
d->fm = new QFontMetrics( font() );
}
return d->fm;
}
QPixmap K3IconView::selectedIconPixmap( QPixmap *pix, const QColor &col ) const
{
QPixmap m;
if ( d->maskCache.find( QString::number( pix->serialNumber() ), m ) )
return m;
m = *pix;
{
QPainter p(&m);
QColor h = col;
h.setAlphaF(0.5);
p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
p.fillRect(m.rect(), h);
p.end();
}
d->maskCache.insert( QString::number( pix->serialNumber() ), m );
return m;
}
int K3IconView::iconTextHeight() const
{
return d->textHeight > 0 ? d->textHeight : ( wordWrapIconText() ? 99 : 1 );
}
void K3IconView::setIconTextHeight( int n )
{
int oldHeight = iconTextHeight();
if ( n > 1 )
d->textHeight = n;
else
d->textHeight = 1;
// so that Qt still shows the tooltip when even a wrapped text is too long
setWordWrapIconText( false );
// update view if needed
if ( iconTextHeight() != oldHeight )
setFont( font() ); // hack to recalc items
}
/////////////
struct K3IconViewItem::K3IconViewItemPrivate
{
QSize m_pixmapSize;
};
void K3IconViewItem::init()
{
m_wordWrap = 0L;
d = 0L;
calcRect();
}
K3IconViewItem::~K3IconViewItem()
{
delete m_wordWrap;
delete d;
}
void K3IconViewItem::calcRect( const QString& text_ )
{
Q_ASSERT( iconView() );
if ( !iconView() )
return;
delete m_wordWrap;
m_wordWrap = 0L;
#ifndef NDEBUG // be faster for the end-user, such a bug will have been fixed before hand :)
if ( !qobject_cast<K3IconView*>(iconView()) )
{
kWarning() << "K3IconViewItem used in a " << iconView()->metaObject()->className() << " !!";
return;
}
#endif
//kDebug() << "K3IconViewItem::calcRect - " << text();
K3IconView *view = static_cast<K3IconView *>(iconView());
QRect itemIconRect = pixmapRect();
QRect itemTextRect = textRect();
QRect itemRect = rect();
int pw = 0;
int ph = 0;
#ifndef QT_NO_PICTURE
if ( picture() ) {
QRect br = picture()->boundingRect();
pw = br.width() + 2;
ph = br.height() + 2;
} else
#endif
{
// Qt uses unknown_icon if no pixmap. Let's see if we need that - I doubt it
if (!pixmap())
return;
pw = pixmap()->width() + 2;
ph = pixmap()->height() + 2;
}
itemIconRect.setWidth( pw );
#if 1 // FIXME
// There is a bug in Qt which prevents the item from being placed
// properly when the pixmapRect is not at the top of the itemRect, so we
// have to increase the height of the pixmapRect and leave it at the top
// of the itemRect...
if ( d && !d->m_pixmapSize.isNull() )
itemIconRect.setHeight( d->m_pixmapSize.height() + 2 );
else
#endif
itemIconRect.setHeight( ph );
int tw = 0;
if ( d && !d->m_pixmapSize.isNull() )
tw = view->maxItemWidth() - ( view->itemTextPos() == Q3IconView::Bottom ? 0 :
d->m_pixmapSize.width() + 2 );
else
tw = view->maxItemWidth() - ( view->itemTextPos() == Q3IconView::Bottom ? 0 :
itemIconRect.width() );
QFontMetrics *fm = view->itemFontMetrics();
QString t;
QRect r;
// When is text_ set ? Doesn't look like it's ever set.
t = text_.isEmpty() ? text() : text_;
// Max text height
int nbLines = static_cast<K3IconView*>( iconView() )->iconTextHeight();
int height = nbLines > 0 ? fm->height() * nbLines : 0xFFFFFFFF;
// Should not be higher than pixmap if text is alongside icons
if ( view->itemTextPos() != Q3IconView::Bottom ) {
if ( d && !d->m_pixmapSize.isNull() )
height = qMin( d->m_pixmapSize.height() + 2, height );
else
height = qMin( itemIconRect.height(), height );
height = qMax( height, fm->height() );
}
// Calculate the word-wrap
QRect outerRect( 0, 0, tw - 6, height );
m_wordWrap = KWordWrap::formatText( *fm, outerRect, 0, t );
r = m_wordWrap->boundingRect();
int realWidth = qMax( qMin( r.width() + 4, tw ), fm->width( "X" ) );
itemTextRect.setWidth( realWidth );
itemTextRect.setHeight( r.height() );
int w = 0; int h = 0; int y = 0;
if ( view->itemTextPos() == Q3IconView::Bottom ) {
// If the pixmap size has been specified, use it
if ( d && !d->m_pixmapSize.isNull() )
{
w = qMax( itemTextRect.width(), d->m_pixmapSize.width() + 2 );
h = itemTextRect.height() + d->m_pixmapSize.height() + 2 + 1;
#if 0 // FIXME
// Waiting for the qt bug to be solved, the pixmapRect must
// stay on the top...
y = d->m_pixmapSize.height() + 2 - itemIconRect.height();
#endif
}
else {
w = qMax( itemTextRect.width(), itemIconRect.width() );
h = itemTextRect.height() + itemIconRect.height() + 1;
}
itemRect.setWidth( w );
itemRect.setHeight( h );
int width = qMax( w, QApplication::globalStrut().width() ); // see QIconViewItem::width()
int height = qMax( h, QApplication::globalStrut().height() ); // see QIconViewItem::height()
itemTextRect = QRect( ( width - itemTextRect.width() ) / 2, height - itemTextRect.height(),
itemTextRect.width(), itemTextRect.height() );
itemIconRect = QRect( ( width - itemIconRect.width() ) / 2, y,
itemIconRect.width(), itemIconRect.height() );
} else {
// If the pixmap size has been specified, use it
if ( d && !d->m_pixmapSize.isNull() )
{
h = qMax( itemTextRect.height(), d->m_pixmapSize.height() + 2 );
#if 0 // FIXME
// Waiting for the qt bug to be solved, the pixmapRect must
// stay on the top...
y = ( d->m_pixmapSize.height() + 2 - itemIconRect.height() ) / 2;
#endif
}
else
h = qMax( itemTextRect.height(), itemIconRect.height() );
w = itemTextRect.width() + itemIconRect.width() + 1;
itemRect.setWidth( w );
itemRect.setHeight( h );
int width = qMax( w, QApplication::globalStrut().width() ); // see QIconViewItem::width()
int height = qMax( h, QApplication::globalStrut().height() ); // see QIconViewItem::height()
itemTextRect = QRect( width - itemTextRect.width(), ( height - itemTextRect.height() ) / 2,
itemTextRect.width(), itemTextRect.height() );
if ( itemIconRect.height() > itemTextRect.height() ) // icon bigger than text -> center vertically
itemIconRect = QRect( 0, ( height - itemIconRect.height() ) / 2,
itemIconRect.width(), itemIconRect.height() );
else // icon smaller than text -> place in top or center with first line
itemIconRect = QRect( 0, qMax(( fm->height() - itemIconRect.height() ) / 2 + y, 0),
itemIconRect.width(), itemIconRect.height() );
if ( ( itemIconRect.height() <= 20 ) && ( itemTextRect.height() < itemIconRect.height() ) )
{
itemTextRect.setHeight( itemIconRect.height() - 2 );
itemTextRect.setY( itemIconRect.y() );
}
}
if ( itemIconRect != pixmapRect() )
setPixmapRect( itemIconRect );
if ( itemTextRect != textRect() )
setTextRect( itemTextRect );
if ( itemRect != rect() )
setItemRect( itemRect );
// Done by setPixmapRect, setTextRect and setItemRect ! [and useless if no rect changed]
//view->updateItemContainer( this );
}
void K3IconViewItem::paintItem( QPainter *p, const QColorGroup &cg )
{
Q3IconView* view = iconView();
Q_ASSERT( view );
if ( !view )
return;
#ifndef NDEBUG // be faster for the end-user, such a bug will have been fixed before hand :)
if ( !qobject_cast<K3IconView*>(view) )
{
kWarning() << "K3IconViewItem used in a " << view->metaObject()->className() << " !!";
return;
}
#endif
p->save();
paintPixmap(p, cg);
paintText(p, cg);
p->restore();
}
KWordWrap * K3IconViewItem::wordWrap()
{
return m_wordWrap;
}
void K3IconViewItem::paintPixmap( QPainter *p, const QColorGroup &cg )
{
K3IconView *kview = static_cast<K3IconView *>(iconView());
#ifndef QT_NO_PICTURE
if ( picture() ) {
QPicture *pic = picture();
if ( isSelected() ) {
// TODO something as nice as selectedIconPixmap if possible ;)
p->fillRect( pixmapRect( false ), QBrush( cg.color(QPalette::Highlight), Qt::Dense4Pattern) );
}
p->drawPicture( x()-pic->boundingRect().x(), y()-pic->boundingRect().y(), *pic );
} else
#endif
{
int iconX = pixmapRect( false ).x();
int iconY = pixmapRect( false ).y();
QPixmap *pix = pixmap();
if ( !pix || pix->isNull() )
return;
#if 1 // FIXME
// Move the pixmap manually because the pixmapRect is at the
// top of the itemRect
// (won't be needed anymore in future versions of qt)
if ( d && !d->m_pixmapSize.isNull() )
{
int offset = 0;
if ( kview->itemTextPos() == Q3IconView::Bottom )
offset = d->m_pixmapSize.height() - pix->height();
else
offset = ( d->m_pixmapSize.height() - pix->height() ) / 2;
if ( offset > 0 )
iconY += offset;
}
#endif
if ( isSelected() ) {
QPixmap selectedPix = kview->selectedIconPixmap( pix, cg.color( QPalette::Highlight ) );
p->drawPixmap( iconX, iconY, selectedPix );
} else {
p->drawPixmap( iconX, iconY, *pix );
}
}
}
void K3IconViewItem::paintText( QPainter *p, const QColorGroup &cg )
{
int textX = textRect( false ).x() + 2;
int textY = textRect( false ).y();
if ( isSelected() ) {
p->fillRect( textRect( false ), cg.color( QPalette::Highlight ) );
p->setPen( QPen( cg.color( QPalette::HighlightedText ) ) );
} else {
if ( iconView()->itemTextBackground() != Qt::NoBrush )
p->fillRect( textRect( false ), iconView()->itemTextBackground() );
p->setPen( cg.color( QPalette::Text ) );
}
int align = iconView()->itemTextPos() == Q3IconView::Bottom ? Qt::AlignHCenter : Qt::AlignLeft;
m_wordWrap->drawText( p, textX, textY, align | KWordWrap::Truncate );
}
QSize K3IconViewItem::pixmapSize() const
{
return d ? d->m_pixmapSize : QSize( 0, 0 );
}
void K3IconViewItem::setPixmapSize( const QSize& size )
{
if ( !d )
d = new K3IconViewItemPrivate;
d->m_pixmapSize = size;
}
#include "moc_k3iconview.cpp"
diff --git a/kde3support/kdeui/k3iconview.h b/kde3support/kdeui/k3iconview.h
index 30d9ee0693..ff3d2976e9 100644
--- a/kde3support/kdeui/k3iconview.h
+++ b/kde3support/kdeui/k3iconview.h
@@ -1,274 +1,274 @@
/* This file is part of the KDE libraries
Copyright (C) 1999 Torben Weis <weis@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 version 2 as published by the Free Software Foundation.
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 KICONVIEW_H
#define KICONVIEW_H
#include <kde3support_export.h>
#include <Qt3Support/Q3IconView>
/**
* @short A variant of QIconView that honors KDE's system-wide settings.
*
* This Widget extends the functionality of QIconView to honor the system
* wide settings for Single Click/Double Click mode, Auto Selection and
* Change Cursor over Link.
*
* There is a new signal executed(). It gets connected to either
* QIconView::clicked() or QIconView::doubleClicked() depending on the KDE
* wide Single Click/Double Click settings. It is strongly recommended that
* you use this signal instead of the above mentioned. This way you don't
* need to care about the current settings.
* If you want to get informed when the user selects something connect to the
* QIconView::selectionChanged() signal.
*
**/
class KDE3SUPPORT_EXPORT K3IconView : public Q3IconView
{
friend class K3IconViewItem;
Q_OBJECT
Q_ENUMS( Mode )
Q_PROPERTY( Mode mode READ mode WRITE setMode )
public:
- K3IconView( QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0 );
+ K3IconView( QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0 );
~K3IconView();
/**
* K3IconView has two different operating modes. Execute mode is depending
* on the configuration of single-click or double-click where the signal
* executed() will be emitted upon click/double-click.
* In Select mode, this signal will not be emitted.
*
* Default is Execute mode.
*/
enum Mode { Execute, Select };
/**
* Sets the mode to Execute or Select.
* @li In Execute mode, the signal executed()
* will be emitted when the user clicks/double-clicks an item.
* @li Select mode is
* the normal QIconView mode.
*
* Default is Execute.
*/
void setMode( Mode m );
/**
* @returns the current Mode, either Execute or Select.
*/
Mode mode() const;
/**
* Reimplemented for internal purposes
*/
virtual void setFont( const QFont & );
/**
* Set the maximum number of lines that will be used to display icon text.
* Setting this value will enable word-wrap, too.
*
* @param n Number of lines
*/
void setIconTextHeight( int n );
/**
* @return The height of icon text in lines
*/
int iconTextHeight() const;
/**
* Reimplemented for held() signal behavior internal purposes
*/
virtual void takeItem( Q3IconViewItem * item );
Q_SIGNALS:
/**
* This signal is emitted whenever the user executes an iconview item.
* That means depending on the KDE wide Single Click/Double Click
* setting the user clicked or double clicked on that item.
* @param item is the pointer to the executed iconview item.
*
* Note that you may not delete any QIconViewItem objects in slots
* connected to this signal.
*/
void executed( Q3IconViewItem *item );
/**
* This signal is emitted whenever the user executes an iconview item.
* That means depending on the KDE wide Single Click/Double Click
* setting the user clicked or double clicked on that item.
* @param item is the pointer to the executed iconview item.
* @param pos is the position where the user has clicked
*
* Note that you may not delete any QIconViewItem objects in slots
* connected to this signal.
*/
void executed( Q3IconViewItem *item, const QPoint &pos );
/**
* This signal is emitted whenever the user hold something on an iconview
* during a drag'n'drop.
* @param item is the pointer to the iconview item the hold event occur.
*
* Note that you may not delete any QIconViewItem objects in slots
* connected to this signal.
*/
void held( Q3IconViewItem *item );
/**
* This signal gets emitted whenever the user double clicks into the
* iconview.
* @param item is the pointer to the clicked iconview item.
* @param pos is the position where the user has clicked, and
*
* Note that you may not delete any QIconViewItem objects in slots
* connected to this signal.
*
* This signal is more or less here for the sake of completeness.
* You should normally not need to use this. In most cases it's better
* to use executed() instead.
*/
void doubleClicked( Q3IconViewItem *item, const QPoint &pos );
protected Q_SLOTS:
void slotOnItem( Q3IconViewItem *item );
void slotOnViewport();
void slotSettingsChanged(int);
/**
* Auto selection happend.
*/
void slotAutoSelect();
protected:
void emitExecute( Q3IconViewItem *item, const QPoint &pos );
void updateDragHoldItem( QDropEvent *e );
virtual void focusOutEvent( QFocusEvent *fe );
virtual void leaveEvent( QEvent *e );
virtual void contentsMousePressEvent( QMouseEvent *e );
virtual void contentsMouseDoubleClickEvent ( QMouseEvent * e );
virtual void contentsMouseReleaseEvent( QMouseEvent *e );
virtual void contentsDragEnterEvent( QDragEnterEvent *e );
virtual void contentsDragLeaveEvent( QDragLeaveEvent *e );
virtual void contentsDragMoveEvent( QDragMoveEvent *e );
virtual void contentsDropEvent( QDropEvent* e );
virtual void wheelEvent( QWheelEvent *e );
/**
* This method allows to handle correctly cases where a subclass
* needs the held() signal to not be triggered without calling
* a K3IconView::contentsDrag*Event() method (which have side effects
* because they forward to QIconView).
*/
void cancelPendingHeldSignal();
private Q_SLOTS:
void slotMouseButtonClicked( int btn, Q3IconViewItem *item, const QPoint &pos );
void slotDragHoldTimeout();
private:
/**
* @internal. For use by K3IconViewItem.
*/
QFontMetrics *itemFontMetrics() const;
/**
* @internal. For use by K3IconViewItem.
*/
QPixmap selectedIconPixmap( QPixmap *pix, const QColor &col ) const;
bool m_bUseSingle;
bool m_bChangeCursorOverItem;
Q3IconViewItem* m_pCurrentItem;
QTimer* m_pAutoSelect;
int m_autoSelectDelay;
private:
class K3IconViewPrivate;
K3IconViewPrivate *d;
};
class KWordWrap;
/**
* @short A variant of QIconViewItem that wraps words better.
*
* K3IconViewItem exists to improve the word-wrap functionality of QIconViewItem
* Use K3IconViewItem instead of QIconViewItem for any iconview item you might have :)
*
* @author David Faure <faure@kde.org>
*/
class KDE3SUPPORT_EXPORT K3IconViewItem : public Q3IconViewItem
{
public:
// Need to redefine all the constructors - I want Java !
K3IconViewItem( Q3IconView *parent )
: Q3IconViewItem( parent ) { init(); } // We need to call it because the parent ctor won't call our reimplementation :(((
K3IconViewItem( Q3IconView *parent, Q3IconViewItem *after )
: Q3IconViewItem( parent, after ) { init(); }
K3IconViewItem( Q3IconView *parent, const QString &text )
: Q3IconViewItem( parent, text ) { init(); }
K3IconViewItem( Q3IconView *parent, Q3IconViewItem *after, const QString &text )
: Q3IconViewItem( parent, after, text ) { init(); }
K3IconViewItem( Q3IconView *parent, const QString &text, const QPixmap &icon )
: Q3IconViewItem( parent, text, icon ) { init(); }
K3IconViewItem( Q3IconView *parent, Q3IconViewItem *after, const QString &text, const QPixmap &icon )
: Q3IconViewItem( parent, after, text, icon ) { init(); }
K3IconViewItem( Q3IconView *parent, const QString &text, const QPicture &picture )
: Q3IconViewItem( parent, text, picture ) { init(); }
K3IconViewItem( Q3IconView *parent, Q3IconViewItem *after, const QString &text, const QPicture &picture )
: Q3IconViewItem( parent, after, text, picture ) { init(); }
virtual ~K3IconViewItem();
/**
* Using this function, you can specify a custom size for the pixmap. The
* geometry of the item will be calculated to let a pixmap of the given size
* fit in the iconView without needing an update.
* This may be useful if you want to change the pixmap later without breaking
* the layout. A possible use of this function is to replace a fileItem icon
* by a larger pixmap (preview).
*
* @param size The size to use
*/
void setPixmapSize( const QSize& size );
/**
* @return The size set by setPixmapSize() or QSize( 0, 0 )
*/
QSize pixmapSize() const;
protected:
void init();
virtual void calcRect( const QString& text_ = QString() );
virtual void paintItem( QPainter *p, const QColorGroup &c );
KWordWrap *wordWrap();
void paintPixmap( QPainter *p, const QColorGroup &c );
void paintText( QPainter *p, const QColorGroup &c );
private:
KWordWrap* m_wordWrap;
struct K3IconViewItemPrivate;
K3IconViewItemPrivate *d;
};
#endif
diff --git a/kde3support/kdeui/k3listbox.cpp b/kde3support/kdeui/k3listbox.cpp
index 03d2206494..0a9c488dbf 100644
--- a/kde3support/kdeui/k3listbox.cpp
+++ b/kde3support/kdeui/k3listbox.cpp
@@ -1,259 +1,259 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Reginald Stadlbauer <reggie@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 version 2 as published by the Free Software Foundation.
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 "k3listbox.h"
#include <kglobalsettings.h>
#include <kdebug.h>
#include <QTimer>
#include <QCursor>
#include <QKeyEvent>
#include <QApplication>
-K3ListBox::K3ListBox( QWidget *parent, const char *name, Qt::WFlags f )
+K3ListBox::K3ListBox( QWidget *parent, const char *name, Qt::WindowFlags f )
: Q3ListBox( parent, name, f ), d(0)
{
connect( this, SIGNAL(onViewport()),
this, SLOT(slotOnViewport()) );
connect( this, SIGNAL(onItem(Q3ListBoxItem*)),
this, SLOT(slotOnItem(Q3ListBoxItem*)) );
slotSettingsChanged(KGlobalSettings::SETTINGS_MOUSE);
connect( KGlobalSettings::self(), SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int)) );
m_pCurrentItem = 0L;
m_pAutoSelect = new QTimer( this );
connect( m_pAutoSelect, SIGNAL(timeout()),
this, SLOT(slotAutoSelect()) );
}
void K3ListBox::slotOnItem( Q3ListBoxItem *item )
{
if ( item && m_bChangeCursorOverItem && m_bUseSingle )
viewport()->setCursor(Qt::PointingHandCursor);
if ( item && (m_autoSelectDelay > -1) && m_bUseSingle ) {
m_pAutoSelect->setSingleShot( true );
m_pAutoSelect->start( m_autoSelectDelay );
m_pCurrentItem = item;
}
}
void K3ListBox::slotOnViewport()
{
if ( m_bChangeCursorOverItem )
viewport()->unsetCursor();
m_pAutoSelect->stop();
m_pCurrentItem = 0L;
}
void K3ListBox::slotSettingsChanged(int category)
{
if (category != KGlobalSettings::SETTINGS_MOUSE)
return;
m_bUseSingle = KGlobalSettings::singleClick();
disconnect( this, SIGNAL( mouseButtonClicked( int, Q3ListBoxItem *,
const QPoint & ) ),
this, SLOT( slotMouseButtonClicked( int, Q3ListBoxItem *,
const QPoint & ) ) );
// disconnect( this, SIGNAL( doubleClicked( QListBoxItem *,
// const QPoint & ) ),
// this, SLOT( slotExecute( QListBoxItem *,
// const QPoint & ) ) );
if( m_bUseSingle )
{
connect( this, SIGNAL( mouseButtonClicked( int, Q3ListBoxItem *,
const QPoint & ) ),
this, SLOT( slotMouseButtonClicked( int, Q3ListBoxItem *,
const QPoint & ) ) );
}
else
{
// connect( this, SIGNAL( doubleClicked( QListBoxItem *,
// const QPoint & ) ),
// this, SLOT( slotExecute( QListBoxItem *,
// const QPoint & ) ) );
}
m_bChangeCursorOverItem = KGlobalSettings::changeCursorOverIcon();
m_autoSelectDelay = KGlobalSettings::autoSelectDelay();
if( !m_bUseSingle || !m_bChangeCursorOverItem )
viewport()->unsetCursor();
}
void K3ListBox::slotAutoSelect()
{
// check that the item still exists
if( index( m_pCurrentItem ) == -1 )
return;
//Give this widget the keyboard focus.
if( !hasFocus() )
setFocus();
Qt::KeyboardModifiers keybstate = QApplication::keyboardModifiers();
Q3ListBoxItem* previousItem = item( currentItem() );
setCurrentItem( m_pCurrentItem );
if( m_pCurrentItem ) {
//Shift pressed?
if( (keybstate & Qt::ShiftModifier) ) {
bool block = signalsBlocked();
blockSignals( true );
//No Ctrl? Then clear before!
if( !(keybstate & Qt::ControlModifier) )
clearSelection();
bool select = !m_pCurrentItem->isSelected();
bool update = viewport()->updatesEnabled();
viewport()->setUpdatesEnabled( false );
bool down = index( previousItem ) < index( m_pCurrentItem );
Q3ListBoxItem* it = down ? previousItem : m_pCurrentItem;
for (;it ; it = it->next() ) {
if ( down && it == m_pCurrentItem ) {
setSelected( m_pCurrentItem, select );
break;
}
if ( !down && it == previousItem ) {
setSelected( previousItem, select );
break;
}
setSelected( it, select );
}
blockSignals( block );
viewport()->setUpdatesEnabled( update );
triggerUpdate( false );
emit selectionChanged();
if( selectionMode() == Q3ListBox::Single )
emit selectionChanged( m_pCurrentItem );
}
else if( (keybstate & Qt::ControlModifier) )
setSelected( m_pCurrentItem, !m_pCurrentItem->isSelected() );
else {
bool block = signalsBlocked();
blockSignals( true );
if( !m_pCurrentItem->isSelected() )
clearSelection();
blockSignals( block );
setSelected( m_pCurrentItem, true );
}
}
else
kDebug() << "That's not supposed to happen!!!!";
}
void K3ListBox::emitExecute( Q3ListBoxItem *item, const QPoint &pos )
{
Qt::KeyboardModifiers keybstate = QApplication::keyboardModifiers();
m_pAutoSelect->stop();
//Don't emit executed if in SC mode and Shift or Ctrl are pressed
if( !( m_bUseSingle && ((keybstate & Qt::ShiftModifier) || (keybstate & Qt::ControlModifier)) ) ) {
emit executed( item );
emit executed( item, pos );
}
}
//
// 2000-16-01 Espen Sand
// This widget is used in dialogs. It should ignore
// F1 (and combinations) and Escape since these are used
// to start help or close the dialog. This functionality
// should be done in QListView but it is not (at least now)
//
void K3ListBox::keyPressEvent(QKeyEvent *e)
{
if( e->key() == Qt::Key_Escape )
{
e->ignore();
}
else if( e->key() == Qt::Key_F1 )
{
e->ignore();
}
else
{
Q3ListBox::keyPressEvent(e);
}
}
void K3ListBox::focusOutEvent( QFocusEvent *fe )
{
m_pAutoSelect->stop();
Q3ListBox::focusOutEvent( fe );
}
void K3ListBox::leaveEvent( QEvent *e )
{
m_pAutoSelect->stop();
Q3ListBox::leaveEvent( e );
}
void K3ListBox::contentsMousePressEvent( QMouseEvent *e )
{
if( (selectionMode() == Extended) && (e->modifiers() & Qt::ShiftModifier) && !(e->modifiers() & Qt::ControlModifier) ) {
bool block = signalsBlocked();
blockSignals( true );
clearSelection();
blockSignals( block );
}
Q3ListBox::contentsMousePressEvent( e );
}
void K3ListBox::contentsMouseDoubleClickEvent ( QMouseEvent * e )
{
Q3ListBox::contentsMouseDoubleClickEvent( e );
Q3ListBoxItem* item = itemAt( contentsToViewport( e->pos() ) );
if( item ) {
emit doubleClicked( item, e->globalPos() );
if( (e->button() == Qt::LeftButton) && !m_bUseSingle )
emitExecute( item, e->globalPos() );
}
}
void K3ListBox::slotMouseButtonClicked( int btn, Q3ListBoxItem *item, const QPoint &pos )
{
if( (btn == Qt::LeftButton) && item )
emitExecute( item, pos );
}
#include "moc_k3listbox.cpp"
diff --git a/kde3support/kdeui/k3listbox.h b/kde3support/kdeui/k3listbox.h
index fb06286aa8..0af787c36b 100644
--- a/kde3support/kdeui/k3listbox.h
+++ b/kde3support/kdeui/k3listbox.h
@@ -1,125 +1,125 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Reginald Stadlbauer <reggie@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 version 2 as published by the Free Software Foundation.
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 K3LISTBOX_H
#define K3LISTBOX_H
#include <kde3support_export.h>
#include <Qt3Support/Q3ListBox>
/**
* @short A variant of QListBox that honors KDE's system-wide settings.
*
* Extends the functionality of QListBox to honor the system
* wide settings for Single Click/Double Click mode, Auto Selection and
* Change Cursor over Link.
*
* There is a new signal executed(). It gets connected to either
* QListBox::clicked() or QListBox::doubleClicked()
* depending on the KDE wide Single Click/Double Click settings. It is
* strongly recommended that you use this signal instead of the above
* mentioned. This way you don't need to care about the current
* settings. If you want to get informed when the user selects
* something connect to the QListBox::selectionChanged() signal.
**/
class KDE3SUPPORT_EXPORT K3ListBox : public Q3ListBox
{
Q_OBJECT
public:
- explicit K3ListBox( QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0 );
+ explicit K3ListBox( QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0 );
Q_SIGNALS:
/**
* Emitted whenever the user executes an listbox item.
*
* That means depending on the KDE wide Single Click/Double Click
* setting the user clicked or double clicked on that item.
* @param item is the pointer to the executed listbox item.
*
* Note that you may not delete any QListBoxItem objects in slots
* connected to this signal.
*/
void executed( Q3ListBoxItem *item );
/**
* Emitted whenever the user executes an listbox item.
*
* That means depending on the KDE wide Single Click/Double Click
* setting the user clicked or double clicked on that item.
* @param item is the pointer to the executed listbox item.
* @param pos is the position where the user has clicked
*
* Note that you may not delete any QListBoxItem objects in slots
* connected to this signal.
*/
void executed( Q3ListBoxItem *item, const QPoint &pos );
/**
* This signal gets emitted whenever the user double clicks into the
* listbox.
*
* @param item The pointer to the clicked listbox item.
* @param pos The position where the user has clicked.
*
* Note that you may not delete any QListBoxItem objects in slots
* connected to this signal.
*
* This signal is more or less here for the sake of completeness.
* You should normally not need to use this. In most cases it's better
* to use executed() instead.
*/
void doubleClicked( Q3ListBoxItem *item, const QPoint &pos );
protected Q_SLOTS:
void slotOnItem( Q3ListBoxItem *item );
void slotOnViewport();
void slotSettingsChanged(int);
/**
* Auto selection happend.
*/
void slotAutoSelect();
protected:
void emitExecute( Q3ListBoxItem *item, const QPoint &pos );
virtual void keyPressEvent(QKeyEvent *e);
virtual void focusOutEvent( QFocusEvent *fe );
virtual void leaveEvent( QEvent *e );
virtual void contentsMousePressEvent( QMouseEvent *e );
virtual void contentsMouseDoubleClickEvent ( QMouseEvent *e );
bool m_bUseSingle;
bool m_bChangeCursorOverItem;
Q3ListBoxItem* m_pCurrentItem;
QTimer* m_pAutoSelect;
int m_autoSelectDelay;
private Q_SLOTS:
void slotMouseButtonClicked( int btn, Q3ListBoxItem *item, const QPoint &pos );
private:
class K3ListBoxPrivate;
K3ListBoxPrivate* const d;
};
#endif
diff --git a/kde3support/kdeui/k3spell.cpp b/kde3support/kdeui/k3spell.cpp
index c424d4e235..31331bddfa 100644
--- a/kde3support/kdeui/k3spell.cpp
+++ b/kde3support/kdeui/k3spell.cpp
@@ -1,1701 +1,1701 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 David Sweet <dsweet@kde.org>
Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de>
Copyright (C) 2003 Zack Rusin <zack@kde.org>
Copyright (C) 2007-2008 Kevin Kofler <Kevin@tigcc.ticalc.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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 "k3spell.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h> // atoi
#include <config-kde3support.h>
#if HAVE_STRINGS_H
#include <strings.h>
#endif
#include <QApplication>
#include <QtCore/QTextCodec>
#include <QtCore/QTimer>
#include <kmessagebox.h>
#include <kdebug.h>
#include <klocalizedstring.h>
#include "k3sconfig.h"
#include "k3spelldlg.h"
#include <kprocess.h>
#include <QTextStream>
#define MAXLINELENGTH 10000
#undef IGNORE //fix possible conflict
enum {
GOOD= 0,
IGNORE= 1,
REPLACE= 2,
MISTAKE= 3
};
enum checkMethod { Method1 = 0, Method2 };
struct BufferedWord
{
checkMethod method;
QString word;
bool useDialog;
bool suggest;
};
class K3Spell::K3SpellPrivate
{
public:
bool endOfResponse;
bool m_bIgnoreUpperWords;
bool m_bIgnoreTitleCase;
bool m_bNoMisspellingsEncountered;
SpellerType type;
K3Spell* suggestSpell;
bool checking;
QList<BufferedWord> unchecked;
QTimer *checkNextTimer;
bool aspellV6;
QTextCodec* m_codec;
QString convertQByteArray( const QByteArray& b )
{
QTextCodec* originalCodec = QTextCodec::codecForCStrings();
QTextCodec::setCodecForCStrings( m_codec );
QString s( b );
QTextCodec::setCodecForCStrings( originalCodec );
return s;
}
QByteArray convertQString( const QString& s )
{
QTextCodec* originalCodec = QTextCodec::codecForCStrings();
QTextCodec::setCodecForCStrings( m_codec );
- QByteArray b = s.toAscii();
+ QByteArray b = s.toLatin1();
QTextCodec::setCodecForCStrings( originalCodec );
return b;
}
};
//TODO
//Parse stderr output
//e.g. -- invalid dictionary name
/*
Things to put in K3SpellConfigDlg:
make root/affix combinations that aren't in the dictionary (-m)
don't generate any affix/root combinations (-P)
Report run-together words with missing blanks as spelling errors. (-B)
default dictionary (-d [dictionary])
personal dictionary (-p [dictionary])
path to ispell -- NO: ispell should be in $PATH
*/
// Connects a slot to KProcess's output signal
#define OUTPUT(x) (connect (proc, SIGNAL (readyReadStandardOutput()), this, SLOT (x())))
// Disconnect a slot from...
#define NOOUTPUT(x) (disconnect (proc, SIGNAL (readyReadStandardOutput()), this, SLOT (x())))
K3Spell::K3Spell( QWidget *_parent, const QString &_caption,
QObject *obj, const char *slot, K3SpellConfig *_ksc,
bool _progressbar, bool _modal )
{
initialize( _parent, _caption, obj, slot, _ksc,
_progressbar, _modal, Text );
}
K3Spell::K3Spell( QWidget *_parent, const QString &_caption,
QObject *obj, const char *slot, K3SpellConfig *_ksc,
bool _progressbar, bool _modal, SpellerType type )
{
initialize( _parent, _caption, obj, slot, _ksc,
_progressbar, _modal, type );
}
K3Spell::spellStatus K3Spell::status() const
{
return m_status;
}
void K3Spell::hide() { ksdlg->hide(); }
QStringList K3Spell::suggestions() const
{
return sugg;
}
int K3Spell::dlgResult () const
{
return dlgresult;
}
int K3Spell::heightDlg() const { return ksdlg->height(); }
int K3Spell::widthDlg() const { return ksdlg->width(); }
QString K3Spell::intermediateBuffer() const
{
return K3Spell::newbuffer;
}
// Check if aspell is at least version 0.6
static bool determineASpellV6()
{
QString result;
FILE *fs = popen("aspell -v", "r");
if (fs)
{
// Close textstream before we close fs
{
QTextStream ts(fs, QIODevice::ReadOnly);
result = ts.readAll().trimmed();
}
pclose(fs);
}
QRegExp rx("Aspell (\\d.\\d)");
if (rx.indexIn(result) != -1)
{
float version = rx.cap(1).toFloat();
return (version >= 0.6);
}
return false;
}
void
K3Spell::startIspell()
//trystart = {0,1,2}
{
if ((trystart == 0) && (ksconfig->client() == KS_CLIENT_ASPELL))
d->aspellV6 = determineASpellV6();
kDebug(750) << "Try #" << trystart;
if ( trystart > 0 ) {
proc->reset();
}
switch ( ksconfig->client() )
{
case KS_CLIENT_ISPELL:
*proc << "ispell";
kDebug(750) << "Using ispell";
break;
case KS_CLIENT_ASPELL:
*proc << "aspell";
kDebug(750) << "Using aspell";
break;
case KS_CLIENT_HSPELL:
*proc << "hspell";
kDebug(750) << "Using hspell";
break;
case KS_CLIENT_ZEMBEREK:
*proc << "zpspell";
kDebug(750) << "Using zemberek(zpspell)";
break;
case KS_CLIENT_HUNSPELL:
*proc << "hunspell";
kDebug(750) << "Using hunspell";
break;
}
// Hunspell doesn't need all of these options, but it'll ignore those it doesn't understand.
if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_ASPELL || ksconfig->client() == KS_CLIENT_HUNSPELL )
{
*proc << "-a" << "-S";
switch ( d->type )
{
case HTML:
//Debian uses an ispell version that has the -h option instead.
//Not sure what they did, but the preferred spell checker
//on that platform is aspell anyway, so use -H untill I'll come
//up with something better.
*proc << "-H";
break;
case TeX:
//same for aspell and ispell
*proc << "-t";
break;
case Nroff:
//only ispell and hunspell support
if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_HUNSPELL )
*proc << "-n";
break;
case Text:
default:
//nothing
break;
}
if (ksconfig->noRootAffix())
{
*proc<<"-m";
}
if (ksconfig->runTogether())
{
*proc << "-B";
}
else
{
*proc << "-C";
}
if (trystart<2)
{
if (! ksconfig->dictionary().isEmpty())
{
kDebug(750) << "using dictionary [" << ksconfig->dictionary() << "]";
*proc << "-d";
*proc << ksconfig->dictionary();
}
}
//Note to potential debuggers: -Tlatin2 _is_ being added on the
// _first_ try. But, some versions of ispell will fail with this
// option, so k3spell tries again without it. That's why as 'ps -ax'
// shows "ispell -a -S ..." withou the "-Tlatin2" option.
if ( ksconfig->client() == KS_CLIENT_HUNSPELL && trystart<1 ) {
// Note: This sets I/O encoding. Hunspell correctly handles dictionary encoding != I/O encoding.
// It will be faster if the I/O encoding matches the dictionary encoding, but using UTF-8 is always safe.
switch ( ksconfig->encoding() )
{
case KS_E_LATIN1:
*proc << "-i" << "ISO-8859-1";
break;
case KS_E_LATIN2:
*proc << "-i" << "ISO-8859-2";
break;
case KS_E_LATIN3:
*proc << "-i" << "ISO-8859-3";
break;
case KS_E_LATIN4:
*proc << "-i" << "ISO-8859-4";
break;
case KS_E_LATIN5:
*proc << "-i" << "ISO-8859-5";
break;
case KS_E_LATIN7:
*proc << "-i" << "ISO-8859-7";
break;
case KS_E_LATIN8:
*proc << "-i" << "ISO-8859-8";
break;
case KS_E_LATIN9:
*proc << "-i" << "ISO-8859-9";
break;
case KS_E_LATIN13:
*proc << "-i" << "ISO-8859-13";
break;
case KS_E_LATIN15:
*proc << "-i" << "ISO-8859-15";
break;
case KS_E_UTF8:
*proc << "-i" << "UTF-8";
break;
case KS_E_KOI8R:
*proc << "-i" << "KOI8-R";
break;
case KS_E_KOI8U:
*proc << "-i" << "KOI8-U";
break;
case KS_E_CP1251:
*proc << "-i" << "CP1251";
break;
case KS_E_CP1255:
*proc << "-i" << "CP1255";
break;
default:
break;
}
} else if ( trystart<1 ) {
switch ( ksconfig->encoding() )
{
case KS_E_LATIN1:
*proc << "-Tlatin1";
break;
case KS_E_LATIN2:
*proc << "-Tlatin2";
break;
case KS_E_LATIN3:
*proc << "-Tlatin3";
break;
// add the other charsets here
case KS_E_LATIN4:
case KS_E_LATIN5:
case KS_E_LATIN7:
case KS_E_LATIN8:
case KS_E_LATIN9:
case KS_E_LATIN13:
// will work, if this is the default charset in the dictionary
kError(750) << "charsets ISO-8859-4, -5, -7, -8, -9 and -13 not supported yet" << endl;
break;
case KS_E_LATIN15: // ISO-8859-15 (Latin 9)
if (ksconfig->client() == KS_CLIENT_ISPELL)
{
/*
* As far as I know, there are no ispell dictionary using ISO-8859-15
* but users have the tendency to select this encoding instead of ISO-8859-1
* So put ispell in ISO-8859-1 (Latin 1) mode.
*/
*proc << "-Tlatin1";
}
else
kError(750) << "ISO-8859-15 not supported for aspell yet." << endl;
break;
case KS_E_UTF8:
*proc << "-Tutf8";
if (ksconfig->client() == KS_CLIENT_ASPELL)
*proc << "--encoding=utf-8";
break;
case KS_E_KOI8U:
*proc << "-w'"; // add ' as a word char
break;
default:
break;
}
}
// -a : pipe mode
// -S : sort suggestions by probable correctness
}
else // hspell and Zemberek(zpspell) doesn't need all the rest of the options
*proc << "-a";
if (trystart == 0) //don't connect these multiple times
{
connect( proc, SIGNAL(readyReadStandardError()),
this, SLOT(ispellErrors()) );
connect( proc, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(ispellExit()) );
proc->setOutputChannelMode( KProcess::SeparateChannels );
proc->setNextOpenMode( QIODevice::ReadWrite | QIODevice::Text );
OUTPUT(K3Spell2);
}
proc->start();
if ( !proc->waitForStarted() )
{
m_status = Error;
QTimer::singleShot( 0, this, SLOT(emitDeath()));
}
}
void
K3Spell::ispellErrors( )
{
// buffer[buflen-1] = '\0';
// kDebug(750) << "ispellErrors [" << buffer << "]\n";
}
void K3Spell::K3Spell2( )
{
QString line;
kDebug(750) << "K3Spell::K3Spell2";
trystart = maxtrystart; //We've officially started ispell and don't want
//to try again if it dies.
QByteArray data;
qint64 read = proc->readLine(data.data(),data.count());
if ( read == -1 )
{
QTimer::singleShot( 0, this, SLOT(emitDeath()) );
return;
}
line = d->convertQByteArray( data );
if ( !line.startsWith('@') ) //@ indicates that ispell is working fine
{
QTimer::singleShot( 0, this, SLOT(emitDeath()) );
return;
}
//We want to recognize KDE in any text!
if ( !ignore("kde") )
{
kDebug(750) << "@KDE was false";
QTimer::singleShot( 0, this, SLOT(emitDeath()) );
return;
}
//We want to recognize linux in any text!
if ( !ignore("linux") )
{
kDebug(750) << "@Linux was false";
QTimer::singleShot( 0, this, SLOT(emitDeath()) );
return;
}
NOOUTPUT( K3Spell2 );
m_status = Running;
emit ready( this );
}
void
K3Spell::setUpDialog( bool reallyuseprogressbar )
{
if ( dialogsetup )
return;
//Set up the dialog box
ksdlg = new K3SpellDlg( parent, progressbar && reallyuseprogressbar, modaldlg );
ksdlg->setCaption( caption );
connect( ksdlg, SIGNAL(command(int)),
this, SLOT(slotStopCancel(int)) );
connect( this, SIGNAL(progress(uint)),
ksdlg, SLOT(slotProgress(uint)) );
if ( modaldlg )
ksdlg->setFocus();
dialogsetup = true;
}
bool K3Spell::addPersonal( const QString & word )
{
QString qs = word.simplified();
//we'll let ispell do the work here b/c we can
if ( qs.indexOf(' ') != -1 || qs.isEmpty() ) // make sure it's a _word_
return false;
qs.prepend( "*" );
personaldict = true;
return proc->write( d->convertQString( qs ) );
}
bool K3Spell::writePersonalDictionary()
{
return proc->write( QByteArray( "#" ) );
}
bool K3Spell::ignore( const QString & word )
{
QString qs = word.simplified();
//we'll let ispell do the work here b/c we can
if ( qs.indexOf (' ') != -1 || qs.isEmpty() ) // make sure it's a _word_
return false;
qs.prepend( "@" );
return proc->write( d->convertQString( qs ) );
}
bool
K3Spell::cleanFputsWord( const QString & s )
{
QString qs(s);
bool empty = true;
for( int i = 0; i < qs.length(); i++ )
{
//we need some punctuation for ornaments
if ( (qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-'
&& qs[i].isPunct()) || qs[i].isSpace() )
{
qs.remove(i,1);
i--;
} else {
if ( qs[i].isLetter() )
empty=false;
}
}
// don't check empty words, otherwise synchronization will lost
if (empty)
return false;
return proc->write( d->convertQString( QString('^'+qs+'\n') ) );
}
bool
K3Spell::cleanFputs( const QString & s )
{
QString qs(s);
unsigned l = qs.length();
// some uses of '$' (e.g. "$0") cause ispell to skip all following text
for( unsigned int i = 0; i < l; ++i )
{
if( qs[i] == '$' )
qs[i] = ' ';
}
if ( l<MAXLINELENGTH )
{
if ( qs.isEmpty() )
qs="";
return proc->write( d->convertQString('^'+qs+'\n') );
}
else
return proc->write( d->convertQString( "^\n" ) );
}
bool K3Spell::checkWord( const QString & buffer, bool _usedialog )
{
if (d->checking) { // don't check multiple words simultaneously
BufferedWord bufferedWord;
bufferedWord.method = Method1;
bufferedWord.word = buffer;
bufferedWord.useDialog = _usedialog;
d->unchecked.append( bufferedWord );
return true;
}
d->checking = true;
QString qs = buffer.simplified();
if ( qs.indexOf (' ') != -1 || qs.isEmpty() ) { // make sure it's a _word_
d->checkNextTimer->setInterval(0);
d->checkNextTimer->setSingleShot(true);
d->checkNextTimer->start();
return false;
}
///set the dialog signal handler
dialog3slot = SLOT(checkWord3());
usedialog = _usedialog;
setUpDialog( false );
if ( _usedialog )
{
emitProgress();
}
else
ksdlg->hide();
QByteArray data;
while (proc->readLine( data.data(), data.count() ) != -1 )
; // eat spurious blanks
OUTPUT(checkWord2);
// connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
proc->write( d->convertQString( QString( "%" ) ) ); // turn off terse mode
proc->write( d->convertQString( buffer ) ); // send the word to ispell
return true;
}
bool K3Spell::checkWord( const QString & buffer, bool _usedialog, bool suggest )
{
if (d->checking) { // don't check multiple words simultaneously
BufferedWord bufferedWord;
bufferedWord.method = Method2;
bufferedWord.word = buffer;
bufferedWord.useDialog = _usedialog;
bufferedWord.suggest = suggest;
d->unchecked.append( bufferedWord );
return true;
}
d->checking = true;
QString qs = buffer.simplified();
if ( qs.indexOf (' ') != -1 || qs.isEmpty() ) { // make sure it's a _word_
d->checkNextTimer->setInterval(0);
d->checkNextTimer->setSingleShot(true);
d->checkNextTimer->start();
return false;
}
///set the dialog signal handler
if ( !suggest ) {
dialog3slot = SLOT(checkWord3());
usedialog = _usedialog;
setUpDialog( false );
if ( _usedialog )
{
emitProgress();
}
else
ksdlg->hide();
}
QByteArray data;
while (proc->readLine( data.data(), data.count() ) != -1 ) ; // eat spurious blanks
OUTPUT(checkWord2);
// connect (this, SIGNAL (dialog3()), this, SLOT (checkWord3()));
proc->write( d->convertQString( QString( "%" ) ) ); // turn off terse mode
proc->write( d->convertQString( buffer ) ); // send the word to ispell
return true;
}
void K3Spell::checkWord2( )
{
QString word;
QString line;
line = d->convertQByteArray( proc->readLine() ); //get ispell's response
/* ispell man page: "Each sentence of text input is terminated with an
additional blank line, indicating that ispell has completed processing
the input line."
<sanders>
But there can be multiple lines returned in the case of an error,
in this case we should consume all the output given otherwise spell checking
can get out of sync.
</sanders>
*/
QByteArray data;
while (proc->readLine( data.data(), data.count() ) != -1 ) ; // eat spurious blanks
NOOUTPUT(checkWord2);
bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
if ( mistake && usedialog )
{
cwword = word;
dialog( word, sugg, SLOT(checkWord3()) );
d->checkNextTimer->setInterval(0);
d->checkNextTimer->setSingleShot(true);
d->checkNextTimer->start();
return;
}
else if( mistake )
{
emit misspelling( word, sugg, lastpos );
}
//emits a "corrected" signal _even_ if no change was made
//so that the calling program knows when the check is complete
emit corrected( word, word, 0L );
d->checkNextTimer->setInterval(0);
d->checkNextTimer->setSingleShot(true);
d->checkNextTimer->start();
}
void K3Spell::checkNext()
{
// Queue words to prevent kspell from turning into a fork bomb
d->checking = false;
if (!d->unchecked.empty()) {
BufferedWord buf = d->unchecked.front();
d->unchecked.pop_front();
if (buf.method == Method1)
checkWord( buf.word, buf.useDialog );
else
checkWord( buf.word, buf.useDialog, buf.suggest );
}
}
void K3Spell::suggestWord()
{
QString word;
QString line;
line = d->convertQByteArray( proc->readLine() ); //get ispell's response
/* ispell man page: "Each sentence of text input is terminated with an
additional blank line, indicating that ispell has completed processing
the input line." */
QByteArray data;
while (proc->readLine( data.data(), data.count() ) != -1 ) ; // eat spurious blanks
NOOUTPUT(checkWord2);
bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
if ( mistake && usedialog )
{
cwword=word;
dialog( word, sugg, SLOT(checkWord3()) );
return;
}
}
void K3Spell::checkWord3()
{
disconnect( this, SIGNAL(dialog3()), this, SLOT(checkWord3()) );
emit corrected( cwword, replacement(), 0L );
}
QString K3Spell::funnyWord( const QString & word )
// composes a guess from ispell to a readable word
// e.g. "re+fry-y+ies" -> "refries"
{
QString qs;
for( int i=0; i<word.size(); i++ )
{
if (word [i]=='+')
continue;
if (word [i]=='-')
{
QString shorty;
int j, k;
for( j = i+1; j < word.size() && word[j] != '+' && word[j] != '-'; j++ )
shorty += word[j];
i = j-1;
if ( !( k = qs.lastIndexOf(shorty) ) || k != -1 )
qs.remove( k, shorty.length() );
else
{
qs += '-';
qs += shorty; //it was a hyphen, not a '-' from ispell
}
}
else
qs += word[i];
}
return qs;
}
int K3Spell::parseOneResponse( const QString &buffer, QString &word, QStringList & sugg )
// buffer is checked, word and sugg are filled in
// returns
// GOOD if word is fine
// IGNORE if word is in ignorelist
// REPLACE if word is in replacelist
// MISTAKE if word is misspelled
{
word = "";
posinline=0;
sugg.clear();
if ( buffer[0] == '*' || buffer[0] == '+' || buffer[0] == '-' )
{
return GOOD;
}
if ( buffer[0] == '&' || buffer[0] == '?' || buffer[0] == '#' )
{
int i,j;
word = buffer.mid( 2, buffer.indexOf( ' ', 3 ) -2 );
//check() needs this
orig=word;
if( d->m_bIgnoreTitleCase && word == word.toUpper() )
return IGNORE;
if( d->m_bIgnoreUpperWords && word[0] == word[0].toUpper() )
{
QString text = word[0] + word.right( word.length()-1 ).toLower();
if( text == word )
return IGNORE;
}
/////// Ignore-list stuff //////////
//We don't take advantage of ispell's ignore function because
//we can't interrupt ispell's output (when checking a large
//buffer) to add a word to _it's_ ignore-list.
if ( ignorelist.indexOf( word.toLower() ) != -1 )
return IGNORE;
//// Position in line ///
QString qs2;
if ( buffer.indexOf( ':' ) != -1 )
qs2 = buffer.left( buffer.indexOf(':') );
else
qs2 = buffer;
posinline = qs2.right( qs2.length()-qs2.lastIndexOf(' ') ).toInt()-1;
///// Replace-list stuff ////
QStringList::Iterator it = replacelist.begin();
for( ;it != replacelist.end(); ++it, ++it ) // Skip two entries at a time.
{
if ( word == *it ) // Word matches
{
++it;
word = *it; // Replace it with the next entry
return REPLACE;
}
}
/////// Suggestions //////
if ( buffer[0] != '#' )
{
QString qs = buffer.mid( buffer.indexOf(':')+2, buffer.length() );
qs += ',';
sugg.clear();
i = j = 0;
while( i < qs.length() )
{
QString temp = qs.mid( i, (j=qs.indexOf(',',i)) - i );
sugg.append( funnyWord(temp) );
i=j+2;
}
}
if ( (sugg.count()==1) && (sugg.first() == word) )
return GOOD;
return MISTAKE;
}
if ( buffer.isEmpty() ) {
kDebug(750) << "Got an empty response: ignoring";
return GOOD;
}
kError(750) << "HERE?: [" << buffer << "]" << endl;
kError(750) << "Please report this to zack@kde.org" << endl;
kError(750) << "Thank you!" << endl;
emit done( false );
emit done( K3Spell::origbuffer );
return MISTAKE;
}
bool K3Spell::checkList (QStringList *_wordlist, bool _usedialog)
// prepare check of string list
{
wordlist=_wordlist;
if ((totalpos=wordlist->count())==0)
return false;
wlIt = wordlist->begin();
usedialog=_usedialog;
// prepare the dialog
setUpDialog();
//set the dialog signal handler
dialog3slot = SLOT (checkList4());
proc->write(QByteArray( '%' ) ); // turn off terse mode & check one word at a time
//lastpos now counts which *word number* we are at in checkListReplaceCurrent()
lastpos = -1;
checkList2();
// when checked, KProcess calls checkList3a
OUTPUT(checkList3a);
return true;
}
void K3Spell::checkList2 ()
// send one word from the list to KProcess
// invoked first time by checkList, later by checkListReplaceCurrent and checkList4
{
// send next word
if (wlIt != wordlist->end())
{
kDebug(750) << "KS::cklist2 " << lastpos << ": " << *wlIt;
d->endOfResponse = false;
bool put;
lastpos++; offset=0;
put = cleanFputsWord (*wlIt);
++wlIt;
// when cleanFPutsWord failed (e.g. on empty word)
// try next word; may be this is not good for other
// problems, because this will make read the list up to the end
if (!put) {
checkList2();
}
}
else
// end of word list
{
NOOUTPUT(checkList3a);
ksdlg->hide();
emit done(true);
}
}
void K3Spell::checkList3a ()
// invoked by KProcess, when data from ispell are read
{
//kDebug(750) << "start of checkList3a";
// don't read more data, when dialog is waiting
// for user interaction
if ( dlgon ) {
//kDebug(750) << "dlgon: don't read more data";
return;
}
int e;
qint64 tempe;
QString word;
QString line;
do
{
QByteArray data;
tempe = proc->readLine( data.data(), data.count() ); //get ispell's response
//kDebug(750) << "checkList3a: read bytes [" << tempe << "]";
line = d->convertQByteArray( data );
if ( tempe == 0 ) {
d->endOfResponse = true;
//kDebug(750) << "checkList3a: end of resp";
} else if ( tempe>0 ) {
if ( (e=parseOneResponse( line, word, sugg ) ) == MISTAKE ||
e==REPLACE )
{
dlgresult=-1;
if ( e == REPLACE )
{
QString old = *(--wlIt); ++wlIt;
dlgreplacement = word;
checkListReplaceCurrent();
// inform application
emit corrected( old, *(--wlIt), lastpos ); ++wlIt;
}
else if( usedialog )
{
cwword = word;
dlgon = true;
// show the dialog
dialog( word, sugg, SLOT(checkList4()) );
return;
}
else
{
d->m_bNoMisspellingsEncountered = false;
emit misspelling( word, sugg, lastpos );
}
}
}
emitProgress (); //maybe
// stop when empty line or no more data
} while (tempe > 0);
//kDebug(750) << "checkList3a: exit loop with [" << tempe << "]";
// if we got an empty line, t.e. end of ispell/aspell response
// and the dialog isn't waiting for user interaction, send next word
if (d->endOfResponse && !dlgon) {
//kDebug(750) << "checkList3a: send next word";
checkList2();
}
}
void K3Spell::checkListReplaceCurrent()
{
// go back to misspelled word
wlIt--;
QString s = *wlIt;
s.replace(posinline+offset,orig.length(),replacement());
offset += replacement().length()-orig.length();
wordlist->insert (wlIt, s);
wlIt = wordlist->erase (wlIt);
// wlIt now points to the word after the repalced one
}
void K3Spell::checkList4 ()
// evaluate dialog return, when a button was pressed there
{
dlgon=false;
QString old;
disconnect (this, SIGNAL (dialog3()), this, SLOT (checkList4()));
//others should have been processed by dialog() already
switch (dlgresult)
{
case KS_REPLACE:
case KS_REPLACEALL:
kDebug(750) << "KS: cklist4: lastpos: " << lastpos;
old = *(--wlIt);
++wlIt;
// replace word
checkListReplaceCurrent();
emit corrected( old, *(--wlIt), lastpos );
++wlIt;
break;
case KS_CANCEL:
ksdlg->hide();
emit done( false );
return;
case KS_STOP:
ksdlg->hide();
emit done( true );
return;
case KS_CONFIG:
ksdlg->hide();
emit done( false );
//check( origbuffer.mid( lastpos ), true );
//trystart = 0;
//proc->disconnect();
//proc->kill();
//delete proc;
//proc = new KProcess( codec );
//startIspell();
return;
};
// read more if there is more, otherwise send next word
if (!d->endOfResponse) {
//kDebug(750) << "checkList4: read more from response";
checkList3a();
}
}
bool K3Spell::check( const QString &_buffer, bool _usedialog )
{
QString qs;
usedialog = _usedialog;
setUpDialog();
//set the dialog signal handler
dialog3slot = SLOT(check3());
kDebug(750) << "KS: check";
origbuffer = _buffer;
if ( ( totalpos = origbuffer.length() ) == 0 )
{
emit done( origbuffer );
return false;
}
// Torben: I corrected the \n\n problem directly in the
// origbuffer since I got errors otherwise
if ( !origbuffer.endsWith("\n\n" ) )
{
if (origbuffer.at(origbuffer.length()-1)!='\n')
{
origbuffer+='\n';
origbuffer+='\n'; //shouldn't these be removed at some point?
}
else
origbuffer+='\n';
}
newbuffer = origbuffer;
// KProcess calls check2 when read from ispell
OUTPUT( check2 );
proc->write( QByteArray( "!" ) );
//lastpos is a position in newbuffer (it has offset in it)
offset = lastlastline = lastpos = lastline = 0;
emitProgress();
// send first buffer line
int i = origbuffer.indexOf( '\n', 0 ) + 1;
qs = origbuffer.mid( 0, i );
cleanFputs( qs );
lastline=i; //the character position, not a line number
if ( usedialog )
{
emitProgress();
}
else
ksdlg->hide();
return true;
}
int K3Spell::lastPosition() const
{
return lastpos;
}
void K3Spell::check2()
// invoked by KProcess when read from ispell
{
int e;
qint64 tempe;
QString word;
QString line;
static bool recursive = false;
if (recursive &&
!ksdlg )
{
return;
}
recursive = true;
do
{
QByteArray data;
tempe = proc->readLine( data.data(), data.count() ); //get ispell's response
line = d->convertQByteArray( data );
//kDebug(750) << "K3Spell::check2 (" << tempe << "b)";
if ( tempe>0 )
{
if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE ||
e==REPLACE)
{
dlgresult=-1;
// for multibyte encoding posinline needs correction
if ((ksconfig->encoding() == KS_E_UTF8) && !d->aspellV6) {
// kDebug(750) << "line: " << origbuffer.mid(lastlastline,
// lastline-lastlastline) << endl;
// kDebug(750) << "posinline uncorr: " << posinline;
// convert line to UTF-8, cut at pos, convert back to UCS-2
// and get string length
posinline = (QString::fromUtf8(
origbuffer.mid(lastlastline,lastline-lastlastline).toUtf8(),
posinline)).length();
// kDebug(750) << "posinline corr: " << posinline;
}
lastpos = posinline+lastlastline+offset;
//orig is set by parseOneResponse()
if (e==REPLACE)
{
dlgreplacement=word;
emit corrected( orig, replacement(), lastpos );
offset += replacement().length()-orig.length();
newbuffer.replace( lastpos, orig.length(), word );
}
else //MISTAKE
{
cwword = word;
//kDebug(750) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n";
if ( usedialog ) {
// show the word in the dialog
dialog( word, sugg, SLOT(check3()) );
} else {
// No dialog, just emit misspelling and continue
d->m_bNoMisspellingsEncountered = false;
emit misspelling( word, sugg, lastpos );
dlgresult = KS_IGNORE;
check3();
}
recursive = false;
return;
}
}
}
emitProgress(); //maybe
} while( tempe>0 );
if ( tempe == -1 ) { //we were called, but no data seems to be ready...
// Make sure we don't get called directly again and make sure we do get
// called when new data arrives.
NOOUTPUT( check2 );
// proc->enableReadSignals(true);
OUTPUT( check2 );
recursive = false;
return;
}
// proc->ackRead();
//If there is more to check, then send another line to ISpell.
if ( lastline < origbuffer.length() )
{
int i;
QString qs;
//kDebug(750) << "[EOL](" << tempe << ")[" << temp << "]";
lastpos = (lastlastline=lastline) + offset; //do we really want this?
i = origbuffer.indexOf('\n', lastline) + 1;
qs = origbuffer.mid( lastline, i-lastline );
cleanFputs( qs );
lastline = i;
recursive = false;
return;
}
else
//This is the end of it all
{
ksdlg->hide();
// kDebug(750) << "check2() done";
newbuffer.truncate( newbuffer.length()-2 );
emitProgress();
emit done( newbuffer );
}
recursive = false;
}
void K3Spell::check3 ()
// evaluates the return value of the dialog
{
disconnect (this, SIGNAL (dialog3()), this, SLOT (check3()));
kDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult;
//others should have been processed by dialog() already
switch (dlgresult)
{
case KS_REPLACE:
case KS_REPLACEALL:
offset+=replacement().length()-cwword.length();
newbuffer.replace (lastpos, cwword.length(),
replacement());
emit corrected (dlgorigword, replacement(), lastpos);
break;
case KS_CANCEL:
// kDebug(750) << "canceled\n";
ksdlg->hide();
emit done( origbuffer );
return;
case KS_CONFIG:
ksdlg->hide();
emit done( origbuffer );
KMessageBox::information( 0, i18n("You have to restart the dialog for changes to take effect") );
//check( origbuffer.mid( lastpos ), true );
return;
case KS_STOP:
ksdlg->hide();
//buffer=newbuffer);
emitProgress();
emit done (newbuffer);
return;
};
// proc->ackRead();
}
void
K3Spell::slotStopCancel (int result)
{
if (dialogwillprocess)
return;
kDebug(750) << "K3Spell::slotStopCancel [" << result << "]";
if (result==KS_STOP || result==KS_CANCEL)
if (!dialog3slot.isEmpty())
{
dlgresult=result;
- connect (this, SIGNAL (dialog3()), this, dialog3slot.toAscii().constData());
+ connect (this, SIGNAL (dialog3()), this, dialog3slot.toLatin1().constData());
emit dialog3();
}
}
void K3Spell::dialog( const QString & word, QStringList & sugg, const char *_slot )
{
dlgorigword = word;
dialog3slot = _slot;
dialogwillprocess = true;
connect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
QString tmpBuf = newbuffer;
kDebug(750)<<" position = "<<lastpos;
// extract a context string, replace all characters which might confuse
// the RichText display and highlight the possibly wrong word
QString marker( "_MARKER_" );
tmpBuf.replace( lastpos, word.length(), marker );
QString context = tmpBuf.mid(qMax(lastpos-18,0), 2*18+marker.length());
context.replace( '\n',QLatin1Char(' '));
context.replace( '<', QLatin1String("&lt;") );
context.replace( '>', QLatin1String("&gt;") );
context.replace( marker, QString::fromLatin1("<b>%1</b>").arg( word ) );
context = "<qt>" + context + "</qt>";
ksdlg->init( word, &sugg, context );
d->m_bNoMisspellingsEncountered = false;
emit misspelling( word, sugg, lastpos );
emitProgress();
ksdlg->show();
}
QString K3Spell::replacement () const
{
return dlgreplacement;
}
void K3Spell::dialog2( int result )
{
QString qs;
disconnect( ksdlg, SIGNAL(command(int)), this, SLOT(dialog2(int)) );
dialogwillprocess = false;
dlgresult = result;
ksdlg->standby();
dlgreplacement = ksdlg->replacement();
//process result here
switch ( dlgresult )
{
case KS_IGNORE:
emit ignoreword( dlgorigword );
break;
case KS_IGNOREALL:
// would be better to lower case only words with beginning cap
ignorelist.prepend( dlgorigword.toLower() );
emit ignoreall( dlgorigword );
break;
case KS_ADD:
addPersonal( dlgorigword );
personaldict = true;
emit addword( dlgorigword );
// adding to pesonal dict takes effect at the next line, not the current
ignorelist.prepend( dlgorigword.toLower() );
break;
case KS_REPLACEALL:
{
replacelist.append( dlgorigword );
QString _replacement = replacement();
replacelist.append( _replacement );
emit replaceall( dlgorigword , _replacement );
}
break;
case KS_SUGGEST:
checkWord( ksdlg->replacement(), false, true );
return;
break;
}
- connect( this, SIGNAL(dialog3()), this, dialog3slot.toAscii().constData() );
+ connect( this, SIGNAL(dialog3()), this, dialog3slot.toLatin1().constData() );
emit dialog3();
}
K3Spell::~K3Spell()
{
delete proc;
delete ksconfig;
delete ksdlg;
delete d->checkNextTimer;
delete d;
}
K3SpellConfig K3Spell::ksConfig() const
{
ksconfig->setIgnoreList(ignorelist);
ksconfig->setReplaceAllList(replacelist);
return *ksconfig;
}
void K3Spell::cleanUp()
{
if ( m_status == Cleaning )
return; // Ignore
if ( m_status == Running )
{
if ( personaldict )
writePersonalDictionary();
m_status = Cleaning;
}
proc->closeWriteChannel();
}
void K3Spell::setAutoDelete(bool _autoDelete)
{
autoDelete = _autoDelete;
}
void K3Spell::ispellExit()
{
kDebug() << "K3Spell::ispellExit() " << m_status;
if ( (m_status == Starting) && (trystart < maxtrystart) )
{
trystart++;
startIspell();
return;
}
if ( m_status == Starting )
m_status = Error;
else if (m_status == Cleaning)
m_status = d->m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished;
else if ( m_status == Running )
m_status = Crashed;
else // Error, Finished, Crashed
return; // Dead already
kDebug(750) << "Death";
QTimer::singleShot( 0, this, SLOT(emitDeath()) );
}
// This is always called from the event loop to make
// sure that the receiver can safely delete the
// K3Spell object.
void K3Spell::emitDeath()
{
bool deleteMe = autoDelete; // Can't access object after next call!
emit death();
if ( deleteMe )
deleteLater();
}
void K3Spell::setProgressResolution (unsigned int res)
{
progres=res;
}
void K3Spell::emitProgress ()
{
uint nextprog = (uint) (100.*lastpos/(double)totalpos);
if ( nextprog >= curprog )
{
curprog = nextprog;
emit progress( curprog );
}
}
void K3Spell::moveDlg( int x, int y )
{
QPoint pt( x,y ), pt2;
pt2 = parent->mapToGlobal( pt );
ksdlg->move( pt2.x(),pt2.y() );
}
void K3Spell::setIgnoreUpperWords(bool _ignore)
{
d->m_bIgnoreUpperWords=_ignore;
}
void K3Spell::setIgnoreTitleCase(bool _ignore)
{
d->m_bIgnoreTitleCase=_ignore;
}
// --------------------------------------------------
// Stuff for modal (blocking) spell checking
//
// Written by Torben Weis <weis@kde.org>. So please
// send bug reports regarding the modal stuff to me.
// --------------------------------------------------
int
K3Spell::modalCheck( QString& text )
{
return modalCheck( text,0 );
}
int
K3Spell::modalCheck( QString& text, K3SpellConfig* _kcs )
{
modalreturn = 0;
modaltext = text;
K3Spell* spell = new K3Spell( 0L, i18n("Spell Checker"), 0 ,
0, _kcs, true, true );
while (spell->status()!=Finished)
qApp->processEvents();
text = modaltext;
delete spell;
return modalreturn;
}
void K3Spell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
{
modaltext=modaltext.replace(pos,oldText.length(),newText);
}
void K3Spell::slotModalReady()
{
//kDebug() << qApp->loopLevel();
//kDebug(750) << "MODAL READY------------------";
Q_ASSERT( m_status == Running );
connect( this, SIGNAL(done(QString)),
this, SLOT(slotModalDone(QString)) );
QObject::connect( this, SIGNAL(corrected(QString,QString,uint)),
this, SLOT(slotSpellCheckerCorrected(QString,QString,uint)) );
QObject::connect( this, SIGNAL(death()),
this, SLOT(slotModalSpellCheckerFinished()) );
check( modaltext );
}
void K3Spell::slotModalDone( const QString &/*_buffer*/ )
{
//kDebug(750) << "MODAL DONE " << _buffer;
//modaltext = _buffer;
cleanUp();
//kDebug() << "ABOUT TO EXIT LOOP";
//qApp->exit_loop();
//modalWidgetHack->close(true);
slotModalSpellCheckerFinished();
}
void K3Spell::slotModalSpellCheckerFinished( )
{
modalreturn=(int)this->status();
}
void K3Spell::initialize( QWidget *_parent, const QString &_caption,
QObject *obj, const char *slot, K3SpellConfig *_ksc,
bool _progressbar, bool _modal, SpellerType type )
{
d = new K3SpellPrivate;
d->m_bIgnoreUpperWords =false;
d->m_bIgnoreTitleCase =false;
d->m_bNoMisspellingsEncountered = true;
d->type = type;
d->checking = false;
d->aspellV6 = false;
d->checkNextTimer = new QTimer( this );
connect( d->checkNextTimer, SIGNAL(timeout()),
this, SLOT(checkNext()));
autoDelete = false;
modaldlg = _modal;
progressbar = _progressbar;
proc = 0;
ksconfig = 0;
ksdlg = 0;
lastpos = 0;
//won't be using the dialog in ksconfig, just the option values
if ( _ksc )
ksconfig = new K3SpellConfig( *_ksc );
else
ksconfig = new K3SpellConfig;
d->m_codec = 0;
switch ( ksconfig->encoding() )
{
case KS_E_LATIN1:
d->m_codec = QTextCodec::codecForName("ISO 8859-1");
break;
case KS_E_LATIN2:
d->m_codec = QTextCodec::codecForName("ISO 8859-2");
break;
case KS_E_LATIN3:
d->m_codec = QTextCodec::codecForName("ISO 8859-3");
break;
case KS_E_LATIN4:
d->m_codec = QTextCodec::codecForName("ISO 8859-4");
break;
case KS_E_LATIN5:
d->m_codec = QTextCodec::codecForName("ISO 8859-5");
break;
case KS_E_LATIN7:
d->m_codec = QTextCodec::codecForName("ISO 8859-7");
break;
case KS_E_LATIN8:
d->m_codec = QTextCodec::codecForName("ISO 8859-8-i");
break;
case KS_E_LATIN9:
d->m_codec = QTextCodec::codecForName("ISO 8859-9");
break;
case KS_E_LATIN13:
d->m_codec = QTextCodec::codecForName("ISO 8859-13");
break;
case KS_E_LATIN15:
d->m_codec = QTextCodec::codecForName("ISO 8859-15");
break;
case KS_E_UTF8:
d->m_codec = QTextCodec::codecForName("UTF-8");
break;
case KS_E_KOI8R:
d->m_codec = QTextCodec::codecForName("KOI8-R");
break;
case KS_E_KOI8U:
d->m_codec = QTextCodec::codecForName("KOI8-U");
break;
case KS_E_CP1251:
d->m_codec = QTextCodec::codecForName("CP1251");
break;
case KS_E_CP1255:
d->m_codec = QTextCodec::codecForName("CP1255");
break;
default:
break;
}
kDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (d->m_codec ? d->m_codec->name() : "<default>");
// copy ignore list from ksconfig
ignorelist += ksconfig->ignoreList();
replacelist += ksconfig->replaceAllList();
texmode=dlgon=false;
m_status = Starting;
dialogsetup = false;
progres=10;
curprog=0;
dialogwillprocess = false;
dialog3slot.clear();
personaldict = false;
dlgresult = -1;
caption = _caption;
parent = _parent;
trystart = 0;
maxtrystart = 2;
if ( obj && slot )
// caller wants to know when k3spell is ready
connect( this, SIGNAL(ready(K3Spell*)), obj, slot);
else
// Hack for modal spell checking
connect( this, SIGNAL(ready(K3Spell*)), this, SLOT(slotModalReady()) );
proc = new KProcess();
startIspell();
}
QString K3Spell::modaltext;
int K3Spell::modalreturn = 0;
QWidget* K3Spell::modalWidgetHack = 0;
#include "moc_k3spell.cpp"
diff --git a/kde3support/kdeui/k3wizard.cpp b/kde3support/kdeui/k3wizard.cpp
index 1872ae5adc..59ac6c21e3 100644
--- a/kde3support/kdeui/k3wizard.cpp
+++ b/kde3support/kdeui/k3wizard.cpp
@@ -1,58 +1,58 @@
/* This file is part of the KDE Libraries
Copyright ( C ) 2002 Nadeem Hasan ( nhasan@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.
*/
#include "k3wizard.h"
#include <QPushButton>
#include <QtCore/QCharRef>
#include <kiconloader.h>
#include <kicon.h>
#include <klocalizedstring.h>
#include <kglobalsettings.h>
#include <kguiitem.h>
#include <kstandardguiitem.h>
-K3Wizard::K3Wizard( QWidget *parent, const char *name, bool modal, Qt::WFlags f )
+K3Wizard::K3Wizard( QWidget *parent, const char *name, bool modal, Qt::WindowFlags f )
: Q3Wizard( parent, name, modal, f )
{
bool useIcons = KGlobalSettings::showIconsOnPushButtons();
if ( useIcons )
{
KGuiItem back = KStandardGuiItem::back( KStandardGuiItem::UseRTL );
KGuiItem forward = KStandardGuiItem::forward( KStandardGuiItem::UseRTL );
backButton()->setIcon( back.icon() );
nextButton()->setIcon( forward.icon() );
finishButton()->setIcon( KDE::icon( "dialog-ok-apply" ) );
cancelButton()->setIcon( KDE::icon( "dialog-cancel" ) );
helpButton()->setIcon( KDE::icon( "help-contents" ) );
backButton()->setText( i18n( "&Back" ) );
nextButton()->setText( i18nc( "Opposite to Back","&Next" ) );
}
QFont font = titleFont();
font.setBold( true );
setTitleFont( font );
}
#include "moc_k3wizard.cpp"
diff --git a/kde3support/kdeui/k3wizard.h b/kde3support/kdeui/k3wizard.h
index 9f943f6e0b..d225e21332 100644
--- a/kde3support/kdeui/k3wizard.h
+++ b/kde3support/kdeui/k3wizard.h
@@ -1,61 +1,61 @@
/* This file is part of the KDE Libraries
Copyright (C) 1999 Harri Porten (porten@kde.org)
Replacement for KWizard from KDE 1.x.
Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net)
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 K3WIZARD_H
#define K3WIZARD_H
#include <kde3support_export.h>
#include <Qt3Support/Q3Wizard>
/**
* General-purpose multi-page dialog.
*
* KWizard is a class for a multi-page dialog. The user can navigate trough
* the pages with "Next" and "Back" buttons and is optionally offered "Finish",
* "Cancel" and "Help" buttons. A wizard comes in handy for tutorials or
* configuration dialogs with sequential steps.
*
* KWizard is just a wrapper for Qt's QWizard class. See the
* QWizard documentation for a detailed description of available
* functions. The only added functionality is a KDE conformant translation
* mechanism for the built-in buttons.
*
* @author Harri Porten <porten@kde.org>
* @version 0.3
*/
class KDE3SUPPORT_EXPORT K3Wizard : public Q3Wizard
{
Q_OBJECT
public:
/**
* Constructor
*/
- K3Wizard(QWidget *parent = 0, const char *name = 0, bool modal = false, Qt::WFlags f = 0);
+ K3Wizard(QWidget *parent = 0, const char *name = 0, bool modal = false, Qt::WindowFlags f = 0);
/**
* Destructor
*/
~K3Wizard() {}
};
#endif // K3WIZARD_H
diff --git a/kde3support/kparts/dockmainwindow3.cpp b/kde3support/kparts/dockmainwindow3.cpp
index 98890b0ed0..ae7dcd39ba 100644
--- a/kde3support/kparts/dockmainwindow3.cpp
+++ b/kde3support/kparts/dockmainwindow3.cpp
@@ -1,165 +1,165 @@
/* This file is part of the KDE project
Copyright (C) 2000 Falk Brettschneider <gigafalk@yahoo.com>
(C) 1999 Simon Hausmann <hausmann@kde.org>
(C) 1999 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.
*/
#include "dockmainwindow3.h"
#include <QPointer>
#include <kparts/event.h>
#include <kparts/part.h>
#include <kparts/plugin.h>
#include <kcomponentdata.h>
#include <khelpmenu.h>
#include <kstandarddirs.h>
#include <QApplication>
#include <QStatusBar>
#include <kdebug.h>
#include <kxmlguifactory.h>
#include <assert.h>
using namespace KParts;
namespace KParts
{
class DockMainWindow3Private
{
public:
DockMainWindow3Private()
{
m_activePart = 0;
m_bShellGUIActivated = false;
m_helpMenu = 0;
}
~DockMainWindow3Private()
{
}
QPointer<Part> m_activePart;
bool m_bShellGUIActivated;
KHelpMenu *m_helpMenu;
};
}
-DockMainWindow3::DockMainWindow3( QWidget* parent, const char *name, Qt::WFlags f )
+DockMainWindow3::DockMainWindow3( QWidget* parent, const char *name, Qt::WindowFlags f )
: K3DockMainWindow( parent, name, f )
{
d = new DockMainWindow3Private();
PartBase::setPartObject( this );
setAttribute( Qt::WA_DeleteOnClose );
}
DockMainWindow3::~DockMainWindow3()
{
delete d;
}
void DockMainWindow3::createGUI( Part * part )
{
kDebug(1000) << QString("DockMainWindow3::createGUI for %1").arg(part?part->name():"0L");
KXMLGUIFactory *factory = guiFactory();
setUpdatesEnabled( false );
Q3PtrList<Plugin> plugins;
if ( d->m_activePart )
{
kDebug(1000) << QString("deactivating GUI for %1").arg(d->m_activePart->name());
GUIActivateEvent ev( false );
QApplication::sendEvent( d->m_activePart, &ev );
factory->removeClient( d->m_activePart );
disconnect( d->m_activePart, SIGNAL(setWindowCaption(QString)),
this, SLOT(setCaption(QString)) );
disconnect( d->m_activePart, SIGNAL(setStatusBarText(QString)),
this, SLOT(slotSetStatusBarText(QString)) );
}
if ( !d->m_bShellGUIActivated )
{
loadPlugins( this, this, KGlobal::mainComponent() );
createShellGUI();
d->m_bShellGUIActivated = true;
}
if ( part )
{
// do this before sending the activate event
connect( part, SIGNAL(setWindowCaption(QString)),
this, SLOT(setCaption(QString)) );
connect( part, SIGNAL(setStatusBarText(QString)),
this, SLOT(slotSetStatusBarText(QString)) );
factory->addClient( part );
GUIActivateEvent ev( true );
QApplication::sendEvent( part, &ev );
}
setUpdatesEnabled( true );
d->m_activePart = part;
}
void DockMainWindow3::slotSetStatusBarText( const QString & text )
{
statusBar()->showMessage( text );
}
void DockMainWindow3::createShellGUI( bool create )
{
assert( d->m_bShellGUIActivated != create );
d->m_bShellGUIActivated = create;
if ( create )
{
if ( isHelpMenuEnabled() )
d->m_helpMenu = new KHelpMenu( this, componentData().aboutData(), true, actionCollection() );
QString f = xmlFile();
setXMLFile( KStandardDirs::locate( "config", "ui/ui_standards.rc" ) );
if ( !f.isEmpty() )
setXMLFile( f, true );
else
{
QString auto_file( componentData().componentName() + "ui.rc" );
setXMLFile( auto_file, true );
}
GUIActivateEvent ev( true );
QApplication::sendEvent( this, &ev );
guiFactory()->addClient( this );
}
else
{
GUIActivateEvent ev( false );
QApplication::sendEvent( this, &ev );
guiFactory()->removeClient( this );
}
}
#include "moc_dockmainwindow3.cpp"
diff --git a/kde3support/kparts/dockmainwindow3.h b/kde3support/kparts/dockmainwindow3.h
index c5075e0567..36056fc2db 100644
--- a/kde3support/kparts/dockmainwindow3.h
+++ b/kde3support/kparts/dockmainwindow3.h
@@ -1,88 +1,88 @@
/* This file is part of the KDE project
Copyright (C) 2000 Falk Brettschneider <gigafalk@yahoo.com>
(C) 1999 Simon Hausmann <hausmann@kde.org>
(C) 1999 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 __DOCKMAINWINDOW_H
#define __DOCKMAINWINDOW_H
#include <Qt3Support/Q3PtrList>
#include <kaction.h>
#include <k3dockwidget.h>
#include <kparts/part.h>
class QString;
namespace KParts
{
class DockMainWindow3Private;
/**
* A KPart-aware main window with ability for docking widgets, whose user interface is described in XML.
*
* Inherit your main dock-window from this class
* and don't forget to call setXMLFile() in the inherited constructor.
*
* It implements all internal interfaces in the case of a K3DockMainWindow3 as host:
* the builder and servant interface (for menu merging).
*/
class KDE3SUPPORT_EXPORT DockMainWindow3 : public K3DockMainWindow, virtual public PartBase
{
Q_OBJECT
public:
/**
* Constructor, same signature as K3DockMainWindow3.
*/
- DockMainWindow3( QWidget* parent = 0L, const char *name = 0L, Qt::WFlags f = 0 );
+ DockMainWindow3( QWidget* parent = 0L, const char *name = 0L, Qt::WindowFlags f = 0 );
/**
* Destructor.
*/
virtual ~DockMainWindow3();
protected Q_SLOTS:
/**
* Create the GUI (by merging the host's and the active part's)
*
* Called on startup and whenever the active part changes
* For this you need to connect this slot to the
* KPartManager::activePartChanged() signal
* @param part The active part (set to 0L if no part).
*/
void createGUI( KParts::Part * part );
/**
* Called when the active part wants to change the statusbar message
* Reimplement if your dock-mainwindow has a complex statusbar
* (with several items)
*/
virtual void slotSetStatusBarText( const QString & );
protected:
virtual void createShellGUI( bool create = true );
private:
DockMainWindow3Private *d;
};
}
#endif
diff --git a/kde3support/kunittest/tester.cpp b/kde3support/kunittest/tester.cpp
index 0072bcd48e..133be17dcf 100644
--- a/kde3support/kunittest/tester.cpp
+++ b/kde3support/kunittest/tester.cpp
@@ -1,117 +1,117 @@
/**
* Copyright (C) 2005 Jeroen Wijnhout <Jeroen.Wijnhout@kdemail.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tester.h"
#include <iostream>
using namespace std;
#include <QtCore/QMetaEnum>
#include <QtCore/QRect>
#include <QtCore/QVector>
namespace KUnitTest
{
SlotTester::SlotTester() : Tester()
{
m_total = m_results;
}
SlotTester::~SlotTester()
{
qDeleteAll( m_resultsList );
}
void SlotTester::invokeMember(const QString &str)
{
QString slotname = QString::number(QSLOT_CODE) + str;
- connect(this, SIGNAL(invoke()), this, slotname.toAscii().constData());
+ connect(this, SIGNAL(invoke()), this, slotname.toLatin1().constData());
emit invoke();
- disconnect(this, SIGNAL(invoke()), this, slotname.toAscii().constData());
+ disconnect(this, SIGNAL(invoke()), this, slotname.toLatin1().constData());
}
void SlotTester::allTests()
{
QVector<QByteArray> allSlots;
const int methodCount = metaObject()->methodCount();
const int methodOffset = metaObject()->methodOffset();
allSlots.reserve( methodCount );
for ( int i=0 ; i < methodCount; ++i )
{
QMetaMethod method = metaObject()->method( methodOffset + i );
if ( method.methodType() == QMetaMethod::Slot )
allSlots.append( method.signature() );
}
if ( allSlots.contains("setUp()") )
invokeMember("setUp()");
foreach ( const QByteArray &sl, allSlots )
{
if ( sl.startsWith("test") )
{
m_results = results(sl);
Q_ASSERT( m_results );
m_results->clear();
cout << "KUnitTest_Debug_BeginSlot[" << sl.data() << "]" << endl;
invokeMember(sl);
cout << "KUnitTest_Debug_EndSlot[" << sl.data() << "]" << endl;
}
}
if ( allSlots.contains("tearDown()") )
invokeMember("tearDown()");
m_total->clear();
}
TestResults *SlotTester::results(const char *sl)
{
if ( !m_resultsList.contains(sl) )
m_resultsList.insert(sl, new TestResults());
return m_resultsList[sl];
}
}
QTextStream& operator<<( QTextStream& str, const QRect& r ) {
str << "[" << r.x() << "," << r.y() << " - " << r.width() << "x" << r.height() << "]";
return str;
}
QTextStream& operator<<( QTextStream& str, const QPoint& r ) {
str << "(" << r.x() << "," << r.y() << ")";
return str;
}
QTextStream& operator<<( QTextStream& str, const QSize& r ) {
str << "[" << r.width() << "x" << r.height() << "]";
return str;
}
#include "moc_tester.cpp"
diff --git a/kdecore/io/kdebug.cpp b/kdecore/io/kdebug.cpp
index c0fc4f6c7c..9c0f8c9635 100644
--- a/kdecore/io/kdebug.cpp
+++ b/kdecore/io/kdebug.cpp
@@ -1,952 +1,952 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
2002 Holger Freyther (freyther@kde.org)
2007-2011 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.
*/
#define KDE_EXTENDED_DEBUG_OUTPUT
#ifndef QT_NO_CAST_FROM_ASCII
#define QT_NO_CAST_FROM_ASCII
#endif
#ifndef QT_NO_CAST_TO_ASCII
#define QT_NO_CAST_TO_ASCII
#endif
#ifndef KDE3_SUPPORT
#define KDE3_SUPPORT
#endif
#include "kdebug.h"
#include <config-io.h>
#include <QThreadStorage>
#ifdef Q_OS_WIN
#include <fcntl.h>
#include <windows.h>
#ifndef _WIN32_WCE
#include <wincon.h>
#endif
#else
#include <unistd.h>
#include <stdio.h>
#endif
#ifdef NDEBUG
#undef kDebug
#undef kBacktrace
#endif
//#include <kdefakes.h>
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#if HAVE_TIME_H
#include <time.h>
#endif
#include <kglobal.h> // K_GLOBAL_STATIC
#include "kdatetime.h"
#include <kmessage.h>
#include <klocalizedstring.h>
#include <kconfiggroup.h>
#include <kurl.h>
#include <QtCore/QFile>
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QChar>
#include <QtCore/QCoreApplication>
#include <qstandardpaths.h>
#include <stdlib.h> // abort
#include <unistd.h> // getpid
#include <stdarg.h> // vararg stuff
#include <ctype.h> // isprint
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <kconfig.h>
#ifdef Q_OS_SOLARIS
// For the purposes of KDebug Solaris has a GNU-libc-compatible
// backtrace() function. This isn't detected by the CMake checks
// normally (check_function_exists fails), but we know it's there.
// For better results, we would use walk_context(), but that's
// a little more code -- see also the crash handler in kcrash.cpp .
#define HAVE_BACKTRACE (1)
#endif
#if HAVE_BACKTRACE
#include <execinfo.h>
#ifdef __GNUC__
#define HAVE_BACKTRACE_DEMANGLE
#include <cxxabi.h>
#endif
#endif
#include "kdebugdbusiface_p.h"
#include <QMutex>
KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface = false;
class KNoDebugStream: public QIODevice
{
// Q_OBJECT
public:
KNoDebugStream() { open(WriteOnly); }
bool isSequential() const { return true; }
qint64 readData(char *, qint64) { return 0; /* eof */ }
qint64 readLineData(char *, qint64) { return 0; /* eof */ }
qint64 writeData(const char *, qint64 len) { return len; }
void setContext(const char *debugFile, int line,
const char *funcinfo, const QByteArray& areaName) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
context.file = debugFile;
context.line = line;
context.function = funcinfo;
category = areaName; // for storage
context.category = category.constData();
#endif
}
protected:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
QMessageLogContext context;
QByteArray category;
#endif
};
class KSyslogDebugStream: public KNoDebugStream
{
// Q_OBJECT
public:
qint64 writeData(const char *data, qint64 len)
{
if (len) {
// not using fromRawData because we need a terminating NUL
const QByteArray buf(data, len);
syslog(m_priority, "%s", buf.constData());
}
return len;
}
void setPriority(int priority) { m_priority = priority; }
private:
int m_priority;
};
class KFileDebugStream: public KNoDebugStream
{
// Q_OBJECT
public:
qint64 writeData(const char *data, qint64 len)
{
if (len) {
QFile aOutputFile(m_fileName);
aOutputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered);
QByteArray buf = QByteArray::fromRawData(data, len);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// Apply QT_MESSAGE_PATTERN
extern Q_CORE_EXPORT QString qMessageFormatString(QtMsgType type, const QMessageLogContext &context,
const QString &str);
const QString formatted = qMessageFormatString(QtDebugMsg /*hack*/, context, QString::fromUtf8(buf));
buf = formatted.toUtf8();
#endif
aOutputFile.write(buf.trimmed());
aOutputFile.putChar('\n');
aOutputFile.close();
}
return len;
}
void setFileName(const QString& fn) { m_fileName = fn; }
private:
QString m_fileName;
};
class KMessageBoxDebugStream: public KNoDebugStream
{
// Q_OBJECT
public:
qint64 writeData(const char *data, qint64 len)
{
if (len) {
// Since we are in kdecore here, we cannot use KMsgBox
- QString msg = QString::fromAscii(data, len);
+ QString msg = QString::fromLatin1(data, len);
KMessage::message(KMessage::Information, msg, m_caption);
}
return len;
}
void setCaption(const QString& h) { m_caption = h; }
private:
QString m_caption;
};
class KLineEndStrippingDebugStream: public KNoDebugStream
{
// Q_OBJECT
public:
qint64 writeData(const char *data, qint64 len)
{
QByteArray buf = QByteArray::fromRawData(data, len);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
qt_message_output(QtDebugMsg,
context,
QString::fromLocal8Bit(buf.trimmed()));
#else
qt_message_output(QtDebugMsg,
buf.trimmed().constData());
#endif
return len;
}
};
struct KDebugPrivate
{
enum OutputMode {
FileOutput = 0,
MessageBoxOutput = 1,
QtOutput = 2,
SyslogOutput = 3,
NoOutput = 4,
DefaultOutput = QtOutput, // if you change DefaultOutput, also change the defaults in kdebugdialog!
Unknown = 5
};
struct Area {
inline Area() { clear(); }
void clear(OutputMode set = Unknown)
{
for (int i = 0; i < 4; ++i) {
logFileName[i].clear();
mode[i] = set;
}
}
QByteArray name;
QString logFileName[4];
OutputMode mode[4];
};
typedef QHash<unsigned int, Area> Cache;
KDebugPrivate()
: config(0), kDebugDBusIface(0), m_disableAll(false)
{
Q_ASSERT(int(QtDebugMsg) == 0);
Q_ASSERT(int(QtFatalMsg) == 3);
// Create the D-Bus interface if it has not been created yet
// But only register to D-Bus if we are in a process with a D-Bus event loop,
// otherwise introspection will just hang.
// Examples of processes without a D-Bus event loop: kioslaves and the main kdeinit process.
//
// How to know that we have a real event loop? That's tricky.
// We could delay registration in kDebugDBusIface with a QTimer, but
// it would still get triggered by kioslaves that use enterLoop/exitLoop
// to run kio jobs synchronously.
//
// Solution: we have a bool that is set by KApplication
// (kioslaves should use QCoreApplication but not KApplication).
if (kde_kdebug_enable_dbus_interface) {
kDebugDBusIface = new KDebugDBusIface;
}
for (int i = 0; i < 8; i++) {
m_nullOutputYesNoCache[i] = -1;
}
}
~KDebugPrivate()
{
delete config;
delete kDebugDBusIface;
}
void loadAreaNames()
{
// Don't clear the cache here, that would lose previously registered dynamic areas
//cache.clear();
Area &areaData = cache[0];
areaData.clear();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
areaData.name = qApp ? QCoreApplication::applicationName().toUtf8() : QByteArray("unnamed app");
#else
if (qApp) {
areaData.name = QCoreApplication::applicationName().toUtf8();
if (areaData.name.isEmpty()) {
areaData.name = qAppName().toUtf8();
}
} else {
areaData.name = QByteArray("unnamed app");
}
#endif
//qDebug() << "loadAreaNames: area 0 has name" << areaData.name;
for (int i = 0; i < 8; i++) {
m_nullOutputYesNoCache[i] = -1;
}
QString filename(QStandardPaths::locate(QStandardPaths::ConfigLocation, QLatin1String("kdebug.areas")));
if (!QFile::exists(filename)) {
return;
}
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open %s", filename.toLocal8Bit().constData());
file.close();
return;
}
uint lineNumber=0;
while (!file.atEnd()) {
const QByteArray line = file.readLine().trimmed();
++lineNumber;
if (line.isEmpty())
continue;
int i=0;
unsigned char ch=line[i];
if (ch =='#')
continue; // We have an eof, a comment or an empty line
if (ch < '0' || ch > '9') {
qWarning("Syntax error parsing '%s': no number (line %u)", qPrintable(filename), lineNumber);
continue;
}
do {
ch=line[++i];
} while (ch >= '0' && ch <= '9' && i < line.length());
unsigned int number = line.left(i).toUInt();
while (i < line.length() && line[i] <= ' ')
i++;
Area areaData;
areaData.name = line.mid(i);
cache.insert(number, areaData);
}
file.close();
}
inline int level(QtMsgType type)
{ return int(type) - int(QtDebugMsg); }
QString groupNameForArea(unsigned int area) const
{
QString groupName = QString::number(area);
if (area == 0 || !config->hasGroup(groupName)) {
groupName = QString::fromLocal8Bit(cache.value(area).name);
}
return groupName;
}
OutputMode areaOutputMode(QtMsgType type, unsigned int area, bool enableByDefault)
{
if (!configObject())
return QtOutput;
QString key;
switch (type) {
case QtDebugMsg:
key = QLatin1String( "InfoOutput" );
if (m_disableAll)
return NoOutput;
break;
case QtWarningMsg:
key = QLatin1String( "WarnOutput" );
break;
case QtFatalMsg:
key = QLatin1String( "FatalOutput" );
break;
case QtCriticalMsg:
default:
/* Programmer error, use "Error" as default */
key = QLatin1String( "ErrorOutput" );
break;
}
const KConfigGroup cg(config, groupNameForArea(area));
const int mode = cg.readEntry(key, int(enableByDefault ? DefaultOutput : NoOutput));
return OutputMode(mode);
}
QString logFileName(QtMsgType type, unsigned int area)
{
if (!configObject())
return QString();
const char* aKey;
switch (type)
{
case QtDebugMsg:
aKey = "InfoFilename";
break;
case QtWarningMsg:
aKey = "WarnFilename";
break;
case QtFatalMsg:
aKey = "FatalFilename";
break;
case QtCriticalMsg:
default:
aKey = "ErrorFilename";
break;
}
KConfigGroup cg(config, groupNameForArea(area));
return cg.readPathEntry(aKey, QLatin1String("kdebug.dbg"));
}
KConfig* configObject()
{
if (!config) {
config = new KConfig(QLatin1String("kdebugrc"), KConfig::NoGlobals);
m_disableAll = config->group(QString()).readEntry("DisableAll", false);
}
return config;
}
Cache::Iterator areaData(QtMsgType type, unsigned int num, bool enableByDefault = true)
{
if (!cache.contains(0)) {
//qDebug() << "cache size=" << cache.count() << "loading area names";
loadAreaNames(); // fills 'cache'
Q_ASSERT(cache.contains(0));
}
Cache::Iterator it = cache.find(num);
if (it == cache.end()) {
// unknown area
Q_ASSERT(cache.contains(0));
it = cache.find(0);
num = 0;
}
if (num == 0) { // area 0 is special, it becomes the named area "appname"
static bool s_firstDebugFromApplication = true;
if (s_firstDebugFromApplication && !m_disableAll) {
s_firstDebugFromApplication = false;
//qDebug() << "First debug output from" << it->name << "writing out with default" << enableByDefault;
writeGroupForNamedArea(it->name, enableByDefault);
}
}
const int lev = level(type);
//qDebug() << "in cache for" << num << ":" << it->mode[lev];
if (it->mode[lev] == Unknown)
it->mode[lev] = areaOutputMode(type, num, enableByDefault);
if (it->mode[lev] == FileOutput && it->logFileName[lev].isEmpty())
it->logFileName[lev] = logFileName(type, num);
Q_ASSERT(it->mode[lev] != Unknown);
return it;
}
QDebug setupFileWriter(const QString &fileName)
{
if (!filewriter.hasLocalData())
filewriter.setLocalData(new KFileDebugStream);
filewriter.localData()->setFileName(fileName);
QDebug result(filewriter.localData());
return result;
}
QDebug setupMessageBoxWriter(QtMsgType type, const QByteArray &areaName)
{
if (!messageboxwriter.hasLocalData())
messageboxwriter.setLocalData(new KMessageBoxDebugStream);
QDebug result(messageboxwriter.localData());
QByteArray header;
switch (type) {
case QtDebugMsg:
header = "Info";
break;
case QtWarningMsg:
header = "Warning";
break;
case QtFatalMsg:
header = "Fatal Error";
break;
case QtCriticalMsg:
default:
header = "Error";
break;
}
if (!areaName.isEmpty()) {
header += " (";
header += areaName;
header += ')';
}
- messageboxwriter.localData()->setCaption(QString::fromAscii(header));
+ messageboxwriter.localData()->setCaption(QString::fromLatin1(header));
return result;
}
QDebug setupSyslogWriter(QtMsgType type)
{
if (!syslogwriter.hasLocalData())
syslogwriter.setLocalData(new KSyslogDebugStream);
QDebug result(syslogwriter.localData());
int level = 0;
switch (type) {
case QtDebugMsg:
level = LOG_INFO;
break;
case QtWarningMsg:
level = LOG_WARNING;
break;
case QtFatalMsg:
level = LOG_CRIT;
break;
case QtCriticalMsg:
default:
level = LOG_ERR;
break;
}
syslogwriter.localData()->setPriority(level);
return result;
}
QDebug setupQtWriter(QtMsgType type)
{
if (type == QtWarningMsg) {
// KDE warnings are not the same thing as Qt warnings
// in Qt, warnings indicate bad code, which must be corrected before the release
// in KDE, it's just something that everyone sees (including users)
type = QtDebugMsg;
}
if (type != QtDebugMsg) {
return QDebug(type);
}
return QDebug(&lineendstrippingwriter);
}
QDebug printHeader(QDebug s, bool colored)
{
#ifdef KDE_EXTENDED_DEBUG_OUTPUT
static int printTimeStamp = qgetenv("KDE_DEBUG_TIMESTAMP").toInt();
//s = s.nospace();
if (printTimeStamp > 0) {
if (printTimeStamp >= 2) {
// the extended print: 17:03:24.123
const QString sformat = QString::fromLatin1("hh:mm:ss.zzz");
s << qPrintable(QDateTime::currentDateTime().time().toString(sformat));
} else {
// the default print: 17:03:24
s << qPrintable(QDateTime::currentDateTime().time().toString());
}
//s << ' ';
}
if (m_indentString.hasLocalData()) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
s.setAutoInsertSpaces(false);
#else
s.nospace();
#endif
s << m_indentString.localData()->toLatin1().constData();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
s.setAutoInsertSpaces(true);
#else
s.space();
#endif
}
#if 0 // This is in Qt now, see %{function} in QT_MESSAGE_PATTERN (qlogging.cpp). Only the coloring is missing (TODO Qt-5.1)
if (funcinfo && printMethodName) {
if (colored) {
if (type <= QtDebugMsg)
s << "\033[0;34m"; //blue
else
s << "\033[0;31m"; //red
}
# ifdef Q_CC_GNU
// strip the function info down to the base function name
// note that this throws away the template definitions,
// the parameter types (overloads) and any const/volatile qualifiers
QByteArray info = funcinfo;
int pos = info.indexOf('(');
Q_ASSERT_X(pos != -1, "kDebug",
"Bug in kDebug(): I don't know how to parse this function name");
while (info.at(pos - 1) == ' ')
// that '(' we matched was actually the opening of a function-pointer
pos = info.indexOf('(', pos + 1);
info.truncate(pos);
// gcc 4.1.2 don't put a space between the return type and
// the function name if the function is in an anonymous namespace
int index = 1;
forever {
index = info.indexOf("<unnamed>::", index);
if ( index == -1 )
break;
if ( info.at(index-1) != ':' )
info.insert(index, ' ');
index += strlen("<unnamed>::");
}
pos = info.lastIndexOf(' ');
if (pos != -1) {
int startoftemplate = info.lastIndexOf('<');
if (startoftemplate != -1 && pos > startoftemplate &&
pos < info.lastIndexOf(">::"))
// we matched a space inside this function's template definition
pos = info.lastIndexOf(' ', startoftemplate);
}
if (pos + 1 == info.length())
// something went wrong, so gracefully bail out
s << " " << funcinfo;
else
s << " " << info.constData() + pos + 1;
# else
s << " " << funcinfo;
# endif
if(colored)
s << "\033[0m";
}
s << ":";
s.space();
#endif
#else // KDE_EXTENDED_DEBUG_OUTPUT
Q_UNUSED(funcinfo);
if (!areaName.isEmpty()) {
s.nospace();
s << areaName.constData() << ':';
s.space();
}
#endif
return s;
}
QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line,
const char *funcinfo)
{
static bool env_colored = (!qgetenv("KDE_COLOR_DEBUG").isEmpty());
static bool env_colors_on_any_fd = (!qgetenv("KDE_COLOR_DEBUG_ALWAYS").isEmpty());
Cache::Iterator it = areaData(type, area);
OutputMode mode = it->mode[level(type)];
Q_ASSERT(mode != Unknown);
QString file = it->logFileName[level(type)];
QByteArray areaName = it->name;
//if (areaName.isEmpty())
// areaName = cache.value(0).name;
bool colored=false;
QDebug s(&devnull);
switch (mode) {
case FileOutput:
s = setupFileWriter(file);
filewriter.localData()->setContext(debugFile, line, funcinfo, areaName);
break;
case MessageBoxOutput:
s = setupMessageBoxWriter(type, areaName);
break;
case SyslogOutput:
s = setupSyslogWriter(type);
break;
case NoOutput:
s = QDebug(&devnull);
return s; //no need to take the time to "print header" if we don't want to output anyway
break;
case Unknown: // should not happen
default: // QtOutput
lineendstrippingwriter.setContext(debugFile, line, funcinfo, areaName);
s = setupQtWriter(type);
#ifndef Q_OS_WIN
//only color if the debug goes to a tty, unless env_colors_on_any_fd is set too.
colored = env_colored && (env_colors_on_any_fd || isatty(fileno(stderr)));
#endif
break;
}
return printHeader(s, colored);
}
void writeGroupForNamedArea(const QByteArray& areaName, bool enabled)
{
// Ensure that this area name appears in kdebugrc, so that users (via kdebugdialog)
// can turn it off.
KConfig* cfgObj = configObject();
if (cfgObj) {
KConfigGroup cg(cfgObj, QString::fromUtf8(areaName));
const QString key = QString::fromLatin1("InfoOutput");
if (!cg.hasKey(key)) {
cg.writeEntry(key, int(enabled ? KDebugPrivate::QtOutput : KDebugPrivate::NoOutput));
cg.sync();
}
}
}
QMutex mutex;
KConfig *config;
KDebugDBusIface *kDebugDBusIface;
Cache cache;
bool m_disableAll;
int m_nullOutputYesNoCache[8];
KNoDebugStream devnull;
QThreadStorage<QString*> m_indentString;
QThreadStorage<KSyslogDebugStream*> syslogwriter;
QThreadStorage<KFileDebugStream*> filewriter;
QThreadStorage<KMessageBoxDebugStream*> messageboxwriter;
KLineEndStrippingDebugStream lineendstrippingwriter;
};
// TODO wait for Qt 5.1, uses isDestroyed()
K_GLOBAL_STATIC(KDebugPrivate, kDebug_data)
#if HAVE_BACKTRACE
static QString maybeDemangledName(char *name)
{
#ifdef HAVE_BACKTRACE_DEMANGLE
const int len = strlen(name);
QByteArray in = QByteArray::fromRawData(name, len);
const int mangledNameStart = in.indexOf("(_");
if (mangledNameStart >= 0) {
const int mangledNameEnd = in.indexOf('+', mangledNameStart + 2);
if (mangledNameEnd >= 0) {
int status;
// if we forget about this line and the one that undoes its effect we don't change the
// internal data of the QByteArray::fromRawData() ;)
name[mangledNameEnd] = 0;
char *demangled = abi::__cxa_demangle(name + mangledNameStart + 1, 0, 0, &status);
name[mangledNameEnd] = '+';
if (demangled) {
QString ret = QString::fromLatin1(name, mangledNameStart + 1) +
QString::fromLatin1(demangled) +
QString::fromLatin1(name + mangledNameEnd, len - mangledNameEnd);
free(demangled);
return ret;
}
}
}
#endif
return QString::fromLatin1(name);
}
#endif
QString kRealBacktrace(int levels)
{
QString s;
#if HAVE_BACKTRACE
void* trace[256];
int n = backtrace(trace, 256);
if (!n)
return s;
char** strings = backtrace_symbols (trace, n);
if ( levels != -1 )
n = qMin( n, levels );
s = QLatin1String("[\n");
for (int i = 0; i < n; ++i)
s += QString::number(i) + QLatin1String(": ") +
maybeDemangledName(strings[i]) + QLatin1Char('\n');
s += QLatin1String("]\n");
if (strings)
free (strings);
#endif
return s;
}
QDebug kDebugDevNull()
{
return QDebug(&kDebug_data->devnull);
}
QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
{
if (kDebug_data.isDestroyed()) {
// we don't know what to return now...
qCritical().nospace() << "kDebugStream called after destruction (from "
<< (funcinfo ? funcinfo : "")
<< (file ? " file " : " unknown file")
<< (file ? file :"")
<< " line " << line << ")";
return QDebug(level);
}
QMutexLocker locker(&kDebug_data->mutex);
return kDebug_data->stream(level, area, file, line, funcinfo);
}
QDebug perror(QDebug s, KDebugTag)
{
return s << QString::fromLocal8Bit(strerror(errno));
}
QDebug operator<<(QDebug s, const KDateTime &time)
{
if ( time.isDateOnly() )
s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")";
else
s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")";
return s.space();
}
QDebug operator<<(QDebug s, const KUrl &url)
{
s.nospace() << "KUrl(" << url.prettyUrl() << ")";
return s.space();
}
void kClearDebugConfig()
{
if (!kDebug_data) return;
KDebugPrivate* d = kDebug_data;
QMutexLocker locker(&d->mutex);
delete d->config;
d->config = 0;
KDebugPrivate::Cache::Iterator it = d->cache.begin(),
end = d->cache.end();
for ( ; it != end; ++it)
it->clear();
for (int i = 0; i < 8; i++) {
d->m_nullOutputYesNoCache[i] = -1;
}
}
// static
bool KDebug::hasNullOutput(QtMsgType type,
bool condition,
int area,
bool enableByDefault)
{
if (!condition) {
return true;
}
if (kDebug_data.isDestroyed()) {
// kDebugStream() will generate a warning anyway, so we don't.
return false;
}
KDebugPrivate *const d = kDebug_data;
QMutexLocker locker(&d->mutex);
if (type == QtDebugMsg) {
int *entries = d->m_nullOutputYesNoCache;
for (int i = 0; i < 8; i += 2) {
if (entries[i] == area) {
return entries[i + 1];
}
}
}
KDebugPrivate::Cache::Iterator it = d->areaData(type, area, enableByDefault);
const bool ret = it->mode[d->level(type)] == KDebugPrivate::NoOutput;
// cache result for next time...
if (type == QtDebugMsg) {
int *entries = d->m_nullOutputYesNoCache;
int idx = (qrand() % 4) * 2;
entries[idx] = area;
entries[idx + 1] = ret;
}
return ret;
}
int KDebug::registerArea(const QByteArray& areaName, bool enabled)
{
// TODO for optimization: static int s_lastAreaNumber = 1;
KDebugPrivate* d = kDebug_data;
QMutexLocker locker(&d->mutex);
int areaNumber = 1;
while (d->cache.contains(areaNumber)) {
++areaNumber;
}
KDebugPrivate::Area areaData;
areaData.name = areaName;
//qDebug() << "Assigning area number" << areaNumber << "for name" << areaName;
d->cache.insert(areaNumber, areaData);
d->writeGroupForNamedArea(areaName, enabled);
return areaNumber;
}
#ifndef KDE_NO_DEBUG_OUTPUT
class KDebug::Block::Private
{
public:
QByteArray m_label;
};
KDebug::Block::Block(const char* label, int area)
: m_area(area), d(0)
{
if (hasNullOutputQtDebugMsg(area)) {
d = 0; // remember, for the dtor
} else {
d = new Private;
d->m_label = label;
m_startTime.start();
kDebug(area) << "BEGIN:" << label;
// The indent string is per thread
QThreadStorage<QString*> & indentString = kDebug_data->m_indentString;
if (!indentString.hasLocalData()) {
indentString.setLocalData(new QString);
}
*(indentString.localData()) += QLatin1String(" ");
}
}
KDebug::Block::~Block()
{
if (d) {
const double duration = m_startTime.elapsed() / 1000.0;
QThreadStorage<QString*> & indentString = kDebug_data->m_indentString;
indentString.localData()->chop(2);
// Print timing information, and a special message (DELAY) if the method took longer than 5s
if (duration < 5.0) {
kDebug(m_area)
<< "END__:"
<< d->m_label.constData()
<< qPrintable(QString::fromLatin1("[Took: %3s]").arg(QString::number(duration, 'g', 2)));
} else {
kDebug(m_area)
<< "END__:"
<< d->m_label.constData()
<< qPrintable(QString::fromLatin1("[DELAY Took (quite long) %3s]").arg(QString::number(duration, 'g', 2)));
}
delete d;
}
}
#endif
diff --git a/kdecore/io/kurl.cpp b/kdecore/io/kurl.cpp
index 3f1d617b14..614cfcb6eb 100644
--- a/kdecore/io/kurl.cpp
+++ b/kdecore/io/kurl.cpp
@@ -1,1807 +1,1807 @@
/*
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.
*/
/// KDE4 TODO: maybe we should use QUrl::resolved()
/*
* The currently active RFC for URL/URIs is RFC3986
* Previous (and now deprecated) RFCs are RFC1738 and RFC2396
*/
#include "kurl.h"
#include "kurlmimedata.h"
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QStringList>
#include <QtCore/QRegExp>
#include <QtCore/QMimeData>
#include <QtCore/QTextCodec>
#ifdef DEBUG_KURL
static int kurlDebugArea() { static int s_area = KDebug::registerArea("kdecore (KUrl)"); return s_area; }
#endif
static QString cleanpath( const QString &_path, bool cleanDirSeparator, bool decodeDots )
{
if (_path.isEmpty())
return QString();
if (QFileInfo(_path).isRelative())
return _path; // Don't mangle mailto-style URLs
QString path = _path;
int len = path.length();
if (decodeDots)
{
static const QLatin1String encodedDot("%2e");
if (path.indexOf(encodedDot, 0, Qt::CaseInsensitive) != -1)
{
static const QLatin1String encodedDOT("%2E"); // Uppercase!
path.replace(encodedDot, QString(QLatin1Char('.')));
path.replace(encodedDOT, QString(QLatin1Char('.')));
len = path.length();
}
}
const bool slash = (len && path[len-1] == QLatin1Char('/')) ||
(len > 1 && path[len-2] == QLatin1Char('/') && path[len-1] == QLatin1Char('.'));
// The following code cleans up directory path much like
// QDir::cleanPath() except it can be made to ignore multiple
// directory separators by setting the flag to false. That fixes
// bug# 15044, mail.altavista.com and other similar brain-dead server
// implementations that do not follow what has been specified in
// RFC 2396!! (dA)
QString result;
int cdUp, orig_pos, pos;
cdUp = 0;
pos = orig_pos = len;
while ( pos && (pos = path.lastIndexOf(QLatin1Char('/'),--pos)) != -1 )
{
len = orig_pos - pos - 1;
if ( len == 2 && path[pos+1] == QLatin1Char('.') && path[pos+2] == QLatin1Char('.') )
cdUp++;
else
{
// Ignore any occurrences of '.'
// This includes entries that simply do not make sense like /..../
if ( (len || !cleanDirSeparator) &&
(len != 1 || path[pos+1] != QLatin1Char('.') ) )
{
if ( !cdUp )
result.prepend(path.mid(pos, len+1));
else
cdUp--;
}
}
orig_pos = pos;
}
#ifdef Q_OS_WIN // prepend drive letter if exists (js)
if (orig_pos >= 2 && path[0].isLetter() && path[1] == QLatin1Char(':') ) {
result.prepend(QString(path[0]) + QLatin1Char(':') );
}
#endif
if ( result.isEmpty() )
result = QLatin1Char('/');
else if ( slash && result[result.length()-1] != QLatin1Char('/') )
result.append(QLatin1Char('/'));
return result;
}
#ifdef Q_OS_WIN
// returns true if provided arguments desinate letter+colon or double slash
#define IS_DRIVE_OR_DOUBLESLASH(isletter, char1, char2, colon, slash) \
((isletter && char2 == colon) || (char1 == slash && char2 == slash))
// Removes file:/// or file:// or file:/ or / prefix assuming that str
// is (nonempty) Windows absolute path with a drive letter or double slash.
// If there was file protocol, the path is decoded from percent encoding
static QString removeSlashOrFilePrefix(const QString& str)
{
// FIXME this should maybe be replaced with some (faster?)/nicer logic
const int len = str.length();
if (str[0]==QLatin1Char('f')) {
if ( len > 10 && str.startsWith( QLatin1String( "file:///" ) )
&& IS_DRIVE_OR_DOUBLESLASH(str[8].isLetter(), str[8], str[9], QLatin1Char(':'), QLatin1Char('/')) )
return QUrl::fromPercentEncoding( str.toLatin1() ).mid(8);
else if ( len > 9 && str.startsWith( QLatin1String( "file://" ) )
&& IS_DRIVE_OR_DOUBLESLASH(str[7].isLetter(), str[7], str[8], QLatin1Char(':'), QLatin1Char('/')) )
return QUrl::fromPercentEncoding( str.toLatin1() ).mid(7);
else if ( len > 8 && str.startsWith( QLatin1String( "file:/" ) )
&& IS_DRIVE_OR_DOUBLESLASH(str[6].isLetter(), str[6], str[7], QLatin1Char(':'), QLatin1Char('/')) )
return QUrl::fromPercentEncoding( str.toLatin1() ).mid(6);
}
/* No 'else' here since there can be "f:/" path. */
/* '/' + drive letter or // */
if ( len > 2 && str[0] == QLatin1Char('/')
&& IS_DRIVE_OR_DOUBLESLASH(str[1].isLetter(), str[1], str[2], QLatin1Char(':'), QLatin1Char('/')) )
return str.mid(1);
/* drive letter or // */
else if ( len >= 2 && IS_DRIVE_OR_DOUBLESLASH(str[0].isLetter(), str[0], str[1], QLatin1Char(':'), QLatin1Char('/')) )
return str;
return QString();
}
#endif
bool KUrl::isRelativeUrl(const QString &_url)
{
int len = _url.length();
if (!len) return true; // Very short relative URL.
const QChar *str = _url.unicode();
// Absolute URL must start with alpha-character
if (!isalpha(str[0].toLatin1()))
return true; // Relative URL
for(int i = 1; i < len; i++)
{
char c = str[i].toLatin1(); // Note: non-latin1 chars return 0!
if (c == ':')
return false; // Absolute URL
// Protocol part may only contain alpha, digit, + or -
if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-'))
return true; // Relative URL
}
// URL did not contain ':'
return true; // Relative URL
}
KUrl::List::List(const KUrl &url)
{
append( url );
}
KUrl::List::List(const QList<KUrl> &list)
: QList<KUrl>(list)
{
}
KUrl::List::List(const QList<QUrl> &list)
{
Q_FOREACH(const QUrl& url, list) {
append(KUrl(url));
}
}
KUrl::List::List(const QStringList &list)
{
for (QStringList::ConstIterator it = list.begin();
it != list.end();
++it)
{
append( KUrl(*it) );
}
}
QStringList KUrl::List::toStringList() const
{
return toStringList(KUrl::LeaveTrailingSlash);
}
QStringList KUrl::List::toStringList(KUrl::AdjustPathOption trailing) const
{
QStringList lst;
for(KUrl::List::ConstIterator it = constBegin();
it != constEnd(); ++it) {
lst.append(it->url(trailing));
}
return lst;
}
static void populateMimeDataHelper(const KUrl::List& urls,
QMimeData* mimeData,
const KUrl::MetaDataMap& metaData,
KUrl::MimeDataFlags flags)
{
const QString oldText = mimeData->text();
mimeData->setUrls(urls); // set text/uri-list and text/plain
if ((flags & KUrl::NoTextExport) == 0) {
mimeData->setText(oldText);
}
if (!metaData.isEmpty()) {
KUrlMimeData::setMetaData(metaData, mimeData);
}
}
void KUrl::List::populateMimeData( QMimeData* mimeData,
const KUrl::MetaDataMap& metaData,
MimeDataFlags flags ) const
{
populateMimeDataHelper(*this, mimeData, metaData, flags);
}
void KUrl::List::populateMimeData(const KUrl::List& mostLocalUrls,
QMimeData* mimeData,
const KUrl::MetaDataMap& metaData,
MimeDataFlags flags) const
{
const QString oldText = mimeData->text();
KUrlMimeData::setUrls(*this, mostLocalUrls, mimeData);
if ((flags & KUrl::NoTextExport) == 0) {
mimeData->setText(oldText);
}
if (!metaData.isEmpty()) {
KUrlMimeData::setMetaData(metaData, mimeData);
}
}
bool KUrl::List::canDecode( const QMimeData *mimeData )
{
return mimeData->hasUrls();
}
QStringList KUrl::List::mimeDataTypes()
{
return KUrlMimeData::mimeDataTypes();
}
KUrl::List KUrl::List::fromMimeData(const QMimeData *mimeData,
DecodeOptions decodeOptions,
KUrl::MetaDataMap* metaData)
{
KUrlMimeData::DecodeOptions options = KUrlMimeData::PreferKdeUrls;
if (decodeOptions == PreferLocalUrls)
options = KUrlMimeData::PreferLocalUrls;
return KUrlMimeData::urlsFromMimeData(mimeData, options, metaData);
}
KUrl::List::operator QVariant() const
{
return qVariantFromValue(*this);
}
KUrl::List::operator QList<QUrl>() const
{
QList<QUrl> list;
Q_FOREACH(const KUrl& url, *this) {
list << url;
}
return list;
}
///
KUrl::KUrl()
: QUrl(), d(0)
{
}
KUrl::~KUrl()
{
}
KUrl::KUrl( const QString &str )
: QUrl(), d(0)
{
if ( !str.isEmpty() ) {
#ifdef Q_OS_WIN
#ifdef DEBUG_KURL
- qDebug() << "KUrl::KUrl ( const QString &str = " << str.toAscii().data() << " )";
+ qDebug() << "KUrl::KUrl ( const QString &str = " << str.toLatin1().data() << " )";
#endif
QString pathToSet;
// when it starts with file:// it's a url and must be valid. we don't care if the
// path exist/ is valid or not
if (!str.startsWith(QLatin1String("file://")))
pathToSet = removeSlashOrFilePrefix( QDir::fromNativeSeparators(str) );
if ( !pathToSet.isEmpty() ) {
// we have a prefix indicating this is a local URL
// remember the possible query using _setEncodedUrl(), then set up the correct path without query protocol part
int index = pathToSet.lastIndexOf(QLatin1Char('?'));
if (index == -1)
setPath( pathToSet );
else {
setPath( pathToSet.left( index ) );
_setQuery( pathToSet.mid( index + 1 ) );
}
return;
}
#endif
if ( str[0] == QLatin1Char('/') || str[0] == QLatin1Char('~') )
setPath( str );
else {
_setEncodedUrl( str.toUtf8() );
}
}
}
KUrl::KUrl( const char * str )
: QUrl(), d(0)
{
#ifdef Q_OS_WIN
// true if @a c is letter
#define IS_LETTER(c) \
((c >= QLatin1Char('A') && c <= QLatin1Char('Z')) || (c >= QLatin1Char('a') && c <= QLatin1Char('z')))
// like IS_DRIVE_OR_DOUBLESLASH, but slash is prepended
#define IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 \
( QLatin1Char(str[0]) == QLatin1Char('/') && IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[1])), QLatin1Char(str[1]), QLatin1Char(str[2]), QLatin1Char(':'), QLatin1Char('/')) )
// like IS_DRIVE_OR_DOUBLESLASH, with characters == str[0] and str[1]
#define IS_DRIVE_OR_DOUBLESLASH_0 \
( IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[0])), QLatin1Char(str[0]), QLatin1Char(str[1]), QLatin1Char(':'), QLatin1Char('/')) )
#if defined(DEBUG_KURL)
qDebug() << "KUrl::KUrl " << " " << str;
#endif
if ( str && str[0] && str[1] && str[2] ) {
if ( IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 )
setPath( QString::fromUtf8( str+1 ) );
else if ( IS_DRIVE_OR_DOUBLESLASH_0 )
setPath( QString::fromUtf8( str ) );
}
#endif
if ( str && str[0] ) {
if ( str[0] == '/' || str[0] == '~' )
setPath( QString::fromUtf8( str ) );
else
_setEncodedUrl( str );
}
}
KUrl::KUrl( const QByteArray& str )
: QUrl(), d(0)
{
if ( !str.isEmpty() ) {
#ifdef Q_OS_WIN
#ifdef DEBUG_KURL
qDebug() << "KUrl::KUrl " << " " << str.data();
#endif
if ( IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 )
setPath( QString::fromUtf8( str.mid( 1 ) ) );
else if ( IS_DRIVE_OR_DOUBLESLASH_0 )
setPath( QString::fromUtf8( str ) );
#else
if ( str[0] == '/' || str[0] == '~' )
setPath( QString::fromUtf8( str.data() ) );
#endif
else
_setEncodedUrl( str );
}
}
KUrl::KUrl( const KUrl& _u )
: QUrl( _u ), d(0)
{
#if defined(Q_OS_WIN) && defined(DEBUG_KURL)
qDebug() << "KUrl::KUrl(KUrl) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile();
#endif
}
KUrl::KUrl( const QUrl &u )
: QUrl( u ), d(0)
{
#if defined(Q_OS_WIN) && defined(DEBUG_KURL)
qDebug() << "KUrl::KUrl(Qurl) " << " path " << u.path() << " toLocalFile " << u.toLocalFile();
#endif
}
KUrl::KUrl( const KUrl& _u, const QString& _rel_url )
: QUrl(), d(0)
{
#if defined(Q_OS_WIN) && defined(DEBUG_KURL)
qDebug() << "KUrl::KUrl(KUrl,QString rel_url) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile();
#endif
#if 0
if (_u.hasSubUrl()) // Operate on the last suburl, not the first
{
KUrl::List lst = split( _u );
KUrl u(lst.last(), _rel_url);
lst.erase( --lst.end() );
lst.append( u );
*this = join( lst );
return;
}
#endif
QString rUrl = _rel_url;
// WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS
// http:/index.html AS A VALID SYNTAX FOR RELATIVE
// URLS. ( RFC 2396 section 5.2 item # 3 )
const int len = _u.scheme().length();
if ( !_u.host().isEmpty() && !rUrl.isEmpty() &&
rUrl.indexOf( _u.scheme(), 0, Qt::CaseInsensitive ) == 0 &&
rUrl[len] == QLatin1Char(':') && (rUrl[len+1] != QLatin1Char('/') ||
(rUrl[len+1] == QLatin1Char('/') && rUrl[len+2] != QLatin1Char('/'))) )
{
rUrl.remove( 0, rUrl.indexOf( QLatin1Char(':') ) + 1 );
}
if ( rUrl.isEmpty() )
{
*this = _u;
}
else if ( rUrl[0] == QLatin1Char('#') )
{
*this = _u;
QByteArray strRef_encoded = rUrl.mid(1).toLatin1();
if ( strRef_encoded.isNull() )
strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#'
setEncodedFragment( strRef_encoded );
}
else if ( isRelativeUrl( rUrl ) )
{
*this = _u;
setFragment( QString() );
setEncodedQuery( QByteArray() );
QString strPath = path();
if ( rUrl[0] == QLatin1Char('/') )
{
if ((rUrl.length() > 1) && (rUrl[1] == QLatin1Char('/')))
{
setHost( QString() );
setPort( -1 );
// File protocol returns file:/// without host, strip // from rUrl
if ( _u.isLocalFile() )
rUrl.remove(0, 2);
}
strPath.clear();
}
else if ( rUrl[0] != QLatin1Char('?') )
{
const int pos = strPath.lastIndexOf( QLatin1Char('/') );
if (pos >= 0)
strPath.truncate(pos);
strPath += QLatin1Char('/');
}
else
{
if ( strPath.isEmpty() )
strPath = QLatin1Char('/');
}
setPath( strPath );
//kDebug(kurlDebugArea()) << "url()=" << url() << " rUrl=" << rUrl;
const KUrl tmp( url() + rUrl);
//kDebug(kurlDebugArea()) << "assigning tmp=" << tmp.url();
*this = tmp;
cleanPath(KeepDirSeparators);
}
else
{
const KUrl tmp( rUrl );
//kDebug(kurlDebugArea()) << "not relative; assigning tmp=" << tmp.url();
*this = tmp;
// Preserve userinfo if applicable.
if (!_u.userInfo().isEmpty() && userInfo().isEmpty()
&& (_u.host() == host()) && (_u.scheme() == scheme()))
{
setUserInfo( _u.userInfo() );
}
cleanPath(KeepDirSeparators);
}
}
KUrl& KUrl::operator=( const KUrl& _u )
{
QUrl::operator=( _u );
return *this;
}
bool KUrl::operator==( const KUrl& _u ) const
{
return QUrl::operator==( _u );
}
bool KUrl::operator==( const QString& _u ) const
{
KUrl u( _u );
return ( *this == u );
}
KUrl::operator QVariant() const
{
return qVariantFromValue(*this);
}
#ifndef KDE_NO_DEPRECATED
bool KUrl::cmp( const KUrl &u, bool ignore_trailing ) const
{
return equals( u, ignore_trailing ? CompareWithoutTrailingSlash : EqualsOptions(0) );
}
#endif
bool KUrl::equals( const KUrl &_u, const EqualsOptions& options ) const
{
if ( !isValid() || !_u.isValid() )
return false;
if ( options & CompareWithoutTrailingSlash || options & CompareWithoutFragment )
{
QString path1 = path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash);
QString path2 = _u.path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash);
if (options & AllowEmptyPath) {
if (path1 == QLatin1String("/"))
path1.clear();
if (path2 == QLatin1String("/"))
path2.clear();
}
#ifdef Q_OS_WIN
const bool bLocal1 = isLocalFile();
const bool bLocal2 = _u.isLocalFile();
if ( !bLocal1 && bLocal2 || bLocal1 && !bLocal2 )
return false;
// local files are case insensitive
if ( bLocal1 && bLocal2 && 0 != QString::compare( path1, path2, Qt::CaseInsensitive ) )
return false;
#endif
if ( path1 != path2 )
return false;
if ( scheme() == _u.scheme() &&
authority() == _u.authority() && // user+pass+host+port
encodedQuery() == _u.encodedQuery() &&
(fragment() == _u.fragment() || options & CompareWithoutFragment ) )
return true;
return false;
}
return ( *this == _u );
}
QString KUrl::protocol() const
{
return scheme().toLower();
}
void KUrl::setProtocol( const QString& proto )
{
setScheme( proto );
}
QString KUrl::user() const
{
return userName();
}
void KUrl::setUser( const QString& user )
{
setUserName( user );
}
bool KUrl::hasUser() const
{
return !userName().isEmpty();
}
QString KUrl::pass() const
{
return password();
}
void KUrl::setPass( const QString& pass )
{
setPassword( pass );
}
bool KUrl::hasPass() const
{
return !password().isEmpty();
}
bool KUrl::hasHost() const
{
return !host().isEmpty();
}
bool KUrl::hasPath() const
{
return !path().isEmpty();
}
void KUrl::setFileName( const QString& _txt )
{
setFragment( QString() );
int i = 0;
while( i < _txt.length() && _txt[i] == QLatin1Char('/') )
++i;
QString tmp = i ? _txt.mid( i ) : _txt;
QString path = this->path();
if ( path.isEmpty() )
#ifdef Q_OS_WIN
path = isLocalFile() ? QDir::rootPath() : QLatin1String("/");
#else
path = QDir::rootPath();
#endif
else
{
int lastSlash = path.lastIndexOf( QLatin1Char('/') );
if ( lastSlash == -1)
path.clear(); // there's only the file name, remove it
else if ( !path.endsWith( QLatin1Char('/') ) )
path.truncate( lastSlash+1 ); // keep the "/"
}
path += tmp;
setPath( path );
cleanPath();
}
void KUrl::cleanPath( const CleanPathOption& options )
{
//if (m_iUriMode != URL) return;
const QString newPath = cleanpath(path(), !(options & KeepDirSeparators), false);
if ( path() != newPath )
setPath( newPath );
// WABA: Is this safe when "/../" is encoded with %?
//m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true);
}
static QString trailingSlash( KUrl::AdjustPathOption trailing, const QString &path )
{
if ( trailing == KUrl::LeaveTrailingSlash ) {
return path;
}
QString result = path;
if ( trailing == KUrl::AddTrailingSlash )
{
int len = result.length();
if ( (len == 0) || (result[ len - 1 ] != QLatin1Char('/')) )
result += QLatin1Char('/');
return result;
}
else if ( trailing == KUrl::RemoveTrailingSlash )
{
if ( result == QLatin1String("/") )
return result;
int len = result.length();
while (len > 1 && result[ len - 1 ] == QLatin1Char('/'))
{
len--;
}
result.truncate( len );
return result;
}
else {
assert( 0 );
return result;
}
}
void KUrl::adjustPath( AdjustPathOption trailing )
{
#if 0
if (!m_strPath_encoded.isEmpty())
{
m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded );
}
#endif
const QString newPath = trailingSlash( trailing, path() );
if ( path() != newPath )
setPath( newPath );
}
QString KUrl::encodedPathAndQuery( AdjustPathOption trailing , const EncodedPathAndQueryOptions &options) const
{
QString encodedPath;
#ifdef Q_OS_WIN
// see KUrl::path()
if (isLocalFile()) {
// ### this is probably broken
encodedPath = trailingSlash(trailing, QUrl::toLocalFile());
encodedPath = QString::fromLatin1(QUrl::toPercentEncoding(encodedPath, "!$&'()*+,;=:@/"));
} else {
encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath()));
}
#else
encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath().data()));
#endif
if ((options & AvoidEmptyPath) && encodedPath.isEmpty()) {
encodedPath.append(QLatin1Char('/'));
}
if (hasQuery()) {
return encodedPath + QLatin1Char('?') + QString::fromLatin1(encodedQuery().data());
} else {
return encodedPath;
}
}
void KUrl::setEncodedPathAndQuery( const QString& _txt )
{
const int pos = _txt.indexOf(QLatin1Char('?'));
if ( pos == -1 )
{
setPath( QUrl::fromPercentEncoding( _txt.toLatin1() ) );
setEncodedQuery( QByteArray() );
}
else
{
setPath( QUrl::fromPercentEncoding(_txt.toLatin1().left(pos)) );
_setQuery( _txt.right( _txt.length() - pos - 1 ) );
}
}
QString KUrl::path( AdjustPathOption trailing ) const
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
const QString decodedPath = QUrl::path(QUrl::FullyDecoded);
#else
const QString decodedPath = QUrl::path();
#endif
#ifdef Q_OS_WIN
#ifdef DEBUG_KURL
kWarning() << (isLocalFile() ? "converted to local file - the related call should be converted to toLocalFile()" : "") << QUrl::path();
#endif
return trailingSlash(trailing, isLocalFile() ? QUrl::toLocalFile() : decodedPath);
#else
return trailingSlash(trailing, decodedPath);
#endif
}
QString KUrl::toLocalFile( AdjustPathOption trailing ) const
{
if (hasHost() && isLocalFile()) {
KUrl urlWithoutHost(*this);
urlWithoutHost.setHost(QString());
return trailingSlash(trailing, urlWithoutHost.toLocalFile());
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // QTBUG-20322 is fixed in Qt5, skip the workaround
#ifndef Q_OS_WIN
if (isLocalFile()) {
return trailingSlash(trailing, QUrl::path());
}
#endif
#endif
return trailingSlash(trailing, QUrl::toLocalFile());
}
inline static bool hasSubUrl( const QUrl& url );
static inline bool isLocalFile( const QUrl& url )
{
if ( ( url.scheme() != QLatin1String("file") ) || hasSubUrl( url ) )
return false;
if (url.host().isEmpty() || (url.host() == QLatin1String("localhost")))
return true;
char hostname[ 256 ];
hostname[ 0 ] = '\0';
if (!gethostname( hostname, 255 ))
hostname[sizeof(hostname)-1] = '\0';
for(char *p = hostname; *p; p++)
*p = tolower(*p);
return (url.host() == QString::fromLatin1( hostname ));
}
bool KUrl::isLocalFile() const
{
return ::isLocalFile( *this );
}
void KUrl::setFileEncoding(const QString &encoding)
{
if (!isLocalFile())
return;
QString q = query();
if (!q.isEmpty() && q[0] == QLatin1Char('?'))
q = q.mid(1);
QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts);
for(QStringList::Iterator it = args.begin();
it != args.end();)
{
QString s = QUrl::fromPercentEncoding( (*it).toLatin1() );
if (s.startsWith(QLatin1String("charset=")))
it = args.erase(it);
else
++it;
}
if (!encoding.isEmpty())
args.append(QLatin1String("charset=") + QString::fromLatin1(QUrl::toPercentEncoding(encoding).data()));
if (args.isEmpty())
_setQuery(QString());
else
_setQuery(args.join(QString(QLatin1Char('&'))));
}
QString KUrl::fileEncoding() const
{
if (!isLocalFile())
return QString();
QString q = query();
if (q.isEmpty())
return QString();
if (q[0] == QLatin1Char('?'))
q = q.mid(1);
const QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts);
for(QStringList::ConstIterator it = args.begin();
it != args.end();
++it)
{
QString s = QUrl::fromPercentEncoding((*it).toLatin1());
if (s.startsWith(QLatin1String("charset=")))
return s.mid(8);
}
return QString();
}
inline static bool hasSubUrl( const QUrl& url )
{
// The isValid call triggers QUrlPrivate::validate which needs the full encoded url,
// all this takes too much time for isLocalFile()
const QString scheme = url.scheme();
if ( scheme.isEmpty() /*|| !isValid()*/ )
return false;
const QString ref( url.fragment() );
if (ref.isEmpty())
return false;
switch ( ref.at(0).unicode() ) {
case 'g':
if ( ref.startsWith(QLatin1String("gzip:")) )
return true;
break;
case 'b':
if ( ref.startsWith(QLatin1String("bzip:")) || ref.startsWith(QLatin1String("bzip2:")) )
return true;
break;
case 'l':
if ( ref.startsWith(QLatin1String("lzma:")) )
return true;
break;
case 'x':
if ( ref.startsWith(QLatin1String("xz:")) )
return true;
break;
case 't':
if ( ref.startsWith(QLatin1String("tar:")) )
return true;
break;
case 'a':
if ( ref.startsWith(QLatin1String("ar:")) )
return true;
break;
case 'z':
if ( ref.startsWith(QLatin1String("zip:")) )
return true;
break;
default:
break;
}
if ( scheme == QLatin1String("error") ) // anything that starts with error: has suburls
return true;
return false;
}
bool KUrl::hasSubUrl() const
{
return ::hasSubUrl( *this );
}
QString KUrl::url( AdjustPathOption trailing ) const
{
if (QString::compare(scheme(), QLatin1String("mailto"), Qt::CaseInsensitive) == 0) {
// mailto urls should be prettified, see the url183433 testcase.
return prettyUrl(trailing);
}
if ( trailing == AddTrailingSlash && !path().endsWith( QLatin1Char('/') ) ) {
// -1 and 0 are provided by QUrl, but not +1, so that one is a bit tricky.
// To avoid reimplementing toEncoded() all over again, I just use another QUrl
// Let's hope this is fast, or not called often...
QUrl newUrl( *this );
newUrl.setPath( path() + QLatin1Char('/') );
return QString::fromLatin1(newUrl.toEncoded().data());
}
else if ( trailing == RemoveTrailingSlash) {
const QString cleanedPath = trailingSlash(trailing, path());
if (cleanedPath == QLatin1String("/")) {
if (path() != QLatin1String("/")) {
QUrl fixedUrl = *this;
fixedUrl.setPath(cleanedPath);
return QLatin1String(fixedUrl.toEncoded(None).data());
}
return QLatin1String(toEncoded(None).data());
}
}
return QString::fromLatin1(toEncoded(trailing == RemoveTrailingSlash ? StripTrailingSlash : None).data());
}
static QString toPrettyPercentEncoding(const QString &input, bool forFragment)
{
QString result;
result.reserve(input.length());
for (int i = 0; i < input.length(); ++i) {
const QChar c = input.at(i);
register ushort u = c.unicode();
if (u < 0x20
|| (!forFragment && u == '?') // don't escape '?' in fragments, not needed and wrong (#173101)
|| u == '#' || u == '%'
|| (u == ' ' && (i+1 == input.length() || input.at(i+1).unicode() == ' '))) {
static const char hexdigits[] = "0123456789ABCDEF";
result += QLatin1Char('%');
result += QLatin1Char(hexdigits[(u & 0xf0) >> 4]);
result += QLatin1Char(hexdigits[u & 0xf]);
} else {
result += c;
}
}
return result;
}
QString KUrl::prettyUrl( AdjustPathOption trailing ) const
{
// reconstruct the URL in a "pretty" form
// a "pretty" URL is NOT suitable for data transfer. It's only for showing data to the user.
// however, it must be parseable back to its original state, since
// notably Konqueror displays it in the Location address.
// A pretty URL is the same as a normal URL, except that:
// - the password is removed
// - the hostname is shown in Unicode (as opposed to ACE/Punycode)
// - the pathname and fragment parts are shown in Unicode (as opposed to %-encoding)
QString result = scheme();
if (!result.isEmpty())
{
if (!authority().isEmpty() || result == QLatin1String("file") || path().isEmpty())
result += QLatin1String("://");
else
result += QLatin1Char(':');
}
QString tmp = userName();
if (!tmp.isEmpty()) {
result += QString::fromLatin1(QUrl::toPercentEncoding(tmp).data());
result += QLatin1Char('@');
}
// Check if host is an ipv6 address
tmp = host();
if (tmp.contains(QLatin1Char(':')))
result += QLatin1Char('[') + tmp + QLatin1Char(']');
else
result += tmp;
if (port() != -1) {
result += QLatin1Char(':');
result += QString::number(port());
}
tmp = path();
#ifdef Q_OS_WIN
if (isLocalFile())
tmp.prepend(QLatin1Char('/')); // KUrl::path() returns toLocalFile() on windows so we need to add the / back to create a proper url
#endif
result += toPrettyPercentEncoding(tmp, false);
// adjust the trailing slash, if necessary
if (trailing == AddTrailingSlash && !tmp.endsWith(QLatin1Char('/')))
result += QLatin1Char('/');
else if (trailing == RemoveTrailingSlash && tmp.length() > 1 && tmp.endsWith(QLatin1Char('/')))
result.chop(1);
if (hasQuery()) {
result += QLatin1Char('?');
result += QString::fromLatin1(encodedQuery().data());
}
if (hasFragment()) {
result += QLatin1Char('#');
result += toPrettyPercentEncoding(fragment(), true);
}
return result;
}
#if 0
QString KUrl::prettyUrl( int _trailing, AdjustementFlags _flags) const
{
QString u = prettyUrl(_trailing);
if (_flags & StripFileProtocol && u.startsWith("file://")) {
u.remove(0, 7);
#ifdef Q_OS_WIN
return QDir::convertSeparators(u);
#endif
}
return u;
}
#endif
QString KUrl::pathOrUrl(AdjustPathOption trailing) const
{
if ( isLocalFile() && fragment().isNull() && encodedQuery().isNull() ) {
return toLocalFile(trailing);
} else {
return prettyUrl(trailing);
}
}
// Used for text/uri-list in the mime data
QString KUrl::toMimeDataString() const // don't fold this into populateMimeData, it's also needed by other code like konqdrag
{
if ( isLocalFile() )
{
#if 1
return url();
#else
// According to the XDND spec, file:/ URLs for DND must have
// the hostname part. But in really it just breaks many apps,
// so it's disabled for now.
const QString s = url();
if( !s.startsWith( QLatin1String ( "file://" ) ))
{
char hostname[257];
if ( gethostname( hostname, 255 ) == 0 )
{
hostname[256] = '\0';
return QString( "file://" ) + hostname + s.mid( 5 );
}
}
#endif
}
if (hasPass()) {
KUrl safeUrl(*this);
safeUrl.setPassword(QString());
return safeUrl.url();
}
return url();
}
KUrl::List KUrl::split( const KUrl& _url )
{
QString ref;
bool hasRef;
KUrl::List lst;
KUrl url = _url;
while(true)
{
KUrl u = url;
u.setFragment( QString() );
lst.append(u);
if (url.hasSubUrl())
{
url = KUrl(url.fragment());
}
else
{
ref = url.fragment();
hasRef = url.hasFragment();
break;
}
}
if ( hasRef )
{
// Set HTML ref in all URLs.
KUrl::List::Iterator it;
for( it = lst.begin() ; it != lst.end(); ++it )
{
(*it).setFragment( ref );
}
}
return lst;
}
KUrl::List KUrl::split( const QString& _url )
{
return split(KUrl(_url));
}
KUrl KUrl::join( const KUrl::List & lst )
{
if (lst.isEmpty()) return KUrl();
KUrl tmp;
bool first = true;
QListIterator<KUrl> it(lst);
it.toBack();
while (it.hasPrevious())
{
KUrl u(it.previous());
if (!first) {
u.setEncodedFragment(tmp.url().toLatin1() /* TODO double check encoding */);
}
tmp = u;
first = false;
}
return tmp;
}
QString KUrl::fileName( const DirectoryOptions& options ) const
{
Q_ASSERT( options != 0 ); //Disallow options == false
QString fname;
if (hasSubUrl()) { // If we have a suburl, then return the filename from there
const KUrl::List list = KUrl::split(*this);
return list.last().fileName(options);
}
const QString path = this->path();
int len = path.length();
if ( len == 0 )
return fname;
if (!(options & ObeyTrailingSlash) )
{
while ( len >= 1 && path[ len - 1 ] == QLatin1Char('/') )
len--;
}
else if ( path[ len - 1 ] == QLatin1Char('/') )
return fname;
// Does the path only consist of '/' characters ?
if ( len == 1 && path[ 0 ] == QLatin1Char('/') )
return fname;
// Skip last n slashes
int n = 1;
#if 0
if (!m_strPath_encoded.isEmpty())
{
// This is hairy, we need the last unencoded slash.
// Count in the encoded string how many encoded slashes follow the last
// unencoded one.
int i = m_strPath_encoded.lastIndexOf( QLatin1Char('/'), len - 1 );
QString fileName_encoded = m_strPath_encoded.mid(i+1);
n += fileName_encoded.count("%2f", Qt::CaseInsensitive);
}
#endif
int i = len;
do {
i = path.lastIndexOf( QLatin1Char('/'), i - 1 );
}
while (--n && (i > 0));
// If ( i == -1 ) => the first character is not a '/'
// So it's some URL like file:blah.tgz, return the whole path
if ( i == -1 ) {
if ( len == (int)path.length() )
fname = path;
else
// Might get here if _strip_trailing_slash is true
fname = path.left( len );
}
else
{
fname = path.mid( i + 1, len - i - 1 ); // TO CHECK
}
return fname;
}
void KUrl::addPath( const QString& _txt )
{
if (hasSubUrl())
{
KUrl::List lst = split( *this );
KUrl &u = lst.last();
u.addPath(_txt);
*this = join( lst );
return;
}
//m_strPath_encoded.clear();
if ( _txt.isEmpty() )
return;
QString strPath = path();
int i = 0;
int len = strPath.length();
// Add the trailing '/' if it is missing
if ( _txt[0] != QLatin1Char('/') && ( len == 0 || strPath[ len - 1 ] != QLatin1Char('/') ) )
strPath += QLatin1Char('/');
// No double '/' characters
i = 0;
const int _txtlen = _txt.length();
if ( strPath.endsWith( QLatin1Char('/') ) )
{
while ( ( i < _txtlen ) && ( _txt[i] == QLatin1Char('/') ) )
++i;
}
setPath( strPath + _txt.mid( i ) );
//kDebug(kurlDebugArea())<<"addPath: resultpath="<<path();
}
QString KUrl::directory( const DirectoryOptions& options ) const
{
Q_ASSERT( options != 0 ); //Disallow options == false
QString result = path(); //m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
if ( !(options & ObeyTrailingSlash) )
result = trailingSlash( RemoveTrailingSlash, result );
if ( result.isEmpty() || result == QLatin1String ( "/" ) )
return result;
int i = result.lastIndexOf( QLatin1Char('/') );
// If ( i == -1 ) => the first character is not a '/'
// So it's some URL like file:blah.tgz, with no path
if ( i == -1 )
return QString();
if ( i == 0 )
{
return QString(QLatin1Char('/'));
}
#ifdef Q_OS_WIN
if ( i == 2 && result[1] == QLatin1Char(':') )
{
return result.left(3);
}
#endif
if ( options & AppendTrailingSlash )
result = result.left( i + 1 );
else
result = result.left( i );
//if (!m_strPath_encoded.isEmpty())
// result = decode(result);
return result;
}
bool KUrl::cd( const QString& _dir )
{
if ( _dir.isEmpty() || !isValid() )
return false;
if (hasSubUrl())
{
KUrl::List lst = split( *this );
KUrl &u = lst.last();
u.cd(_dir);
*this = join( lst );
return true;
}
// absolute path ?
#ifdef Q_OS_WIN
if ( !QFileInfo(_dir).isRelative() )
#else
if ( _dir[0] == QLatin1Char('/') )
#endif
{
//m_strPath_encoded.clear();
setPath( _dir );
setHTMLRef( QString() );
setEncodedQuery( QByteArray() );
return true;
}
// Users home directory on the local disk ?
if (_dir[0] == QLatin1Char('~') && scheme() == QLatin1String ("file"))
{
//m_strPath_encoded.clear();
QString strPath = QDir::homePath();
strPath += QLatin1Char('/');
strPath += _dir.right( strPath.length() - 1 );
setPath( strPath );
setHTMLRef( QString() );
setEncodedQuery( QByteArray() );
return true;
}
// relative path
// we always work on the past of the first url.
// Sub URLs are not touched.
// append '/' if necessary
QString p = path(AddTrailingSlash);
p += _dir;
p = cleanpath( p, true, false );
setPath( p );
setHTMLRef( QString() );
setEncodedQuery( QByteArray() );
return true;
}
KUrl KUrl::upUrl( ) const
{
if (!isValid() || isRelative())
return KUrl();
if (!encodedQuery().isEmpty())
{
KUrl u(*this);
u.setEncodedQuery(QByteArray());
return u;
}
if (!hasSubUrl())
{
KUrl u(*this);
u.cd(QLatin1String("../"));
return u;
}
// We have a subURL.
KUrl::List lst = split( *this );
if (lst.isEmpty())
return KUrl(); // Huh?
while (true)
{
KUrl &u = lst.last();
const QString old = u.path();
u.cd(QLatin1String("../"));
if (u.path() != old)
break; // Finished.
if (lst.count() == 1)
break; // Finished.
lst.removeLast();
}
return join( lst );
}
QString KUrl::htmlRef() const
{
if ( !hasSubUrl() )
{
return fragment();
}
const List lst = split( *this );
return (*lst.begin()).fragment();
}
QString KUrl::encodedHtmlRef() const
{
if ( !hasSubUrl() )
{
return ref();
}
const List lst = split( *this );
return (*lst.begin()).ref();
}
void KUrl::setHTMLRef( const QString& _ref )
{
if ( !hasSubUrl() )
{
setFragment( _ref );
return;
}
List lst = split( *this );
(*lst.begin()).setFragment( _ref );
*this = join( lst );
}
bool KUrl::hasHTMLRef() const
{
if ( !hasSubUrl() )
{
return hasRef();
}
const List lst = split( *this );
return (*lst.begin()).hasRef();
}
void KUrl::setDirectory( const QString &dir)
{
if ( dir.endsWith(QLatin1Char('/')))
setPath(dir);
else
setPath(dir + QLatin1Char('/'));
}
void KUrl::setQuery( const QString &_txt )
{
if (!_txt.isEmpty() && _txt[0] == QLatin1Char('?'))
_setQuery( _txt.length() > 1 ? _txt.mid(1) : QString::fromLatin1("") /*empty, not null*/ );
else
_setQuery( _txt );
}
void KUrl::_setQuery( const QString& query )
{
if ( query.isNull() ) {
setEncodedQuery( QByteArray() );
} else if ( query.isEmpty() ) {
setEncodedQuery("");
} else {
setEncodedQuery( query.toLatin1() ); // already percent-escaped, so toLatin1 is ok
}
}
QString KUrl::query() const
{
if (!hasQuery()) {
return QString();
}
return QString(QLatin1Char('?')) + QString::fromLatin1(encodedQuery().data());
}
void KUrl::_setEncodedUrl(const QByteArray& url)
{
setEncodedUrl(url, QUrl::TolerantMode);
if (!isValid()) // see unit tests referring to N183630/task 183874
setUrl(QString::fromUtf8(url.data()), QUrl::TolerantMode);
}
#ifndef KDE_NO_DEPRECATED
bool urlcmp( const QString& _url1, const QString& _url2 )
{
return QUrl( _url1, QUrl::TolerantMode ) == QUrl( _url2, QUrl::TolerantMode );
#if 0
// Both empty ?
if ( _url1.isEmpty() && _url2.isEmpty() )
return true;
// Only one empty ?
if ( _url1.isEmpty() || _url2.isEmpty() )
return false;
KUrl::List list1 = KUrl::split( _url1 );
KUrl::List list2 = KUrl::split( _url2 );
// Malformed ?
if ( list1.isEmpty() || list2.isEmpty() )
return false;
return ( list1 == list2 );
#endif
}
#endif
#ifndef KDE_NO_DEPRECATED
bool urlcmp( const QString& _url1, const QString& _url2, const KUrl::EqualsOptions& _options )
{
// Both empty ?
if (_url1.isEmpty() && _url2.isEmpty())
return true;
// Only one empty ?
if (_url1.isEmpty() || _url2.isEmpty())
return false;
KUrl u1(_url1);
KUrl u2(_url2);
return u1.equals(u2, _options);
#if 0 // kde3 code that supported nested urls
KUrl::List list1 = KUrl::split( _url1 );
KUrl::List list2 = KUrl::split( _url2 );
// Malformed ?
if ( list1.isEmpty() || list2.isEmpty() )
return false;
int size = list1.count();
if ( list2.count() != size )
return false;
if ( _ignore_ref )
{
(*list1.begin()).setRef(QString());
(*list2.begin()).setRef(QString());
}
KUrl::List::Iterator it1 = list1.begin();
KUrl::List::Iterator it2 = list2.begin();
for( ; it1 != list1.end() ; ++it1, ++it2 )
if ( !(*it1).equals( *it2, _ignore_trailing ) )
return false;
return true;
#endif
}
#endif
// static
#ifndef KDE_NO_DEPRECATED
KUrl KUrl::fromPathOrUrl( const QString& text )
{
KUrl url;
if ( !text.isEmpty() )
{
if (!QDir::isRelativePath(text) || text[0] == QLatin1Char('~'))
url.setPath( text );
else
url = KUrl( text );
}
return url;
}
#endif
static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent)
{
QString _base_dir(QDir::cleanPath(base_dir));
QString _path(QDir::cleanPath(path.isEmpty() || (path[0] != QLatin1Char('/')) ? _base_dir+QLatin1Char('/')+path : path));
if (_base_dir.isEmpty())
return _path;
if (_base_dir[_base_dir.length()-1] != QLatin1Char('/'))
_base_dir.append(QLatin1Char('/') );
const QStringList list1 = _base_dir.split(QLatin1Char('/'), QString::SkipEmptyParts);
const QStringList list2 = _path.split(QLatin1Char('/'), QString::SkipEmptyParts);
// Find where they meet
int level = 0;
int maxLevel = qMin(list1.count(), list2.count());
while((level < maxLevel) && (list1[level] == list2[level])) level++;
QString result;
// Need to go down out of the first path to the common branch.
for(int i = level; i < list1.count(); i++)
result.append(QLatin1String("../"));
// Now up up from the common branch to the second path.
for(int i = level; i < list2.count(); i++)
result.append(list2[i]).append(QLatin1Char('/'));
if ((level < list2.count()) && (path[path.length()-1] != QLatin1Char('/')))
result.truncate(result.length()-1);
isParent = (level == list1.count());
return result;
}
QString KUrl::relativePath(const QString &base_dir, const QString &path, bool *isParent)
{
bool parent = false;
QString result = _relativePath(base_dir, path, parent);
if (parent)
result.prepend(QLatin1String("./"));
if (isParent)
*isParent = parent;
return result;
}
QString KUrl::relativeUrl(const KUrl &base_url, const KUrl &url)
{
if ((url.protocol() != base_url.protocol()) ||
(url.host() != base_url.host()) ||
(url.port() && url.port() != base_url.port()) ||
(url.hasUser() && url.user() != base_url.user()) ||
(url.hasPass() && url.pass() != base_url.pass()))
{
return url.url();
}
QString relURL;
if ((base_url.path() != url.path()) || (base_url.query() != url.query()))
{
bool dummy;
QString basePath = base_url.directory(KUrl::ObeyTrailingSlash);
relURL = _relativePath(basePath, url.path(), dummy); // was QUrl::toPercentEncoding() but why?
relURL += url.query();
}
if ( url.hasRef() )
{
relURL += QLatin1Char('#');
relURL += url.ref();
}
if ( relURL.isEmpty() )
return QLatin1String("./");
return relURL;
}
void KUrl::setPath( const QString& _path )
{
#if defined(Q_OS_WIN) && defined(DEBUG_KURL)
- qDebug() << "KUrl::setPath " << " " << _path.toAscii().data();
+ qDebug() << "KUrl::setPath " << " " << _path.toLatin1().data();
#endif
if ( scheme().isEmpty() )
setScheme( QLatin1String( "file" ) );
#pragma message("KDE5 TODO: Remove tildeExpand feature for local paths")
#if 0
QString path = KShell::tildeExpand( _path );
if (path.isEmpty())
#endif
QString path = _path;
#ifdef Q_OS_WIN
const int len = path.length();
if( len == 2 && IS_LETTER(path[0]) && path[1] == QLatin1Char(':') )
path += QLatin1Char('/');
//This is necessary because QUrl has the "path" part including the first slash
//Without this QUrl doesn't understand that this is a path, and some operations fail
//e.g. C:/blah needs to become /C:/blah
else
if( len > 0 && path[0] != QLatin1Char('/') && scheme() == QLatin1String( "file" ) )
path = QLatin1Char('/') + path;
#endif
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QUrl::setPath( path );
#else
QUrl::setPath(path, QUrl::DecodedMode);
#endif
}
#if 0 // this would be if we didn't decode '+' into ' '
QMap< QString, QString > KUrl::queryItems( int options ) const {
QMap< QString, QString > result;
const QList<QPair<QString, QString> > items = QUrl::queryItems();
QPair<QString, QString> item;
Q_FOREACH( item, items ) {
result.insert( options & CaseInsensitiveKeys ? item.first.toLower() : item.first, item.second );
}
return result;
}
#endif
QMap< QString, QString > KUrl::queryItems( const QueryItemsOptions &options ) const
{
const QString strQueryEncoded = QString::fromLatin1(encodedQuery().data());
if ( strQueryEncoded.isEmpty() )
return QMap<QString,QString>();
QMap< QString, QString > result;
const QStringList items = strQueryEncoded.split( QLatin1Char('&'), QString::SkipEmptyParts );
for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
const int equal_pos = (*it).indexOf(QLatin1Char('='));
if ( equal_pos > 0 ) { // = is not the first char...
QString name = (*it).left( equal_pos );
if ( options & CaseInsensitiveKeys )
name = name.toLower();
QString value = (*it).mid( equal_pos + 1 );
if ( value.isEmpty() )
result.insert( name, QString::fromLatin1("") );
else {
// ### why is decoding name not necessary?
value.replace( QLatin1Char('+'), QLatin1Char(' ') ); // + in queries means space
result.insert( name, QUrl::fromPercentEncoding( value.toLatin1() ) );
}
} else if ( equal_pos < 0 ) { // no =
QString name = (*it);
if ( options & CaseInsensitiveKeys )
name = name.toLower();
result.insert( name, QString() );
}
}
return result;
}
QString KUrl::queryItem( const QString& _item ) const
{
const QString strQueryEncoded = QString::fromLatin1(encodedQuery().data());
const QString item = _item + QLatin1Char('=');
if ( strQueryEncoded.length() <= 1 )
return QString();
const QStringList items = strQueryEncoded.split( QString(QLatin1Char('&')), QString::SkipEmptyParts );
const int _len = item.length();
for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it )
{
if ( (*it).startsWith( item ) )
{
if ( (*it).length() > _len )
{
QString str = (*it).mid( _len );
str.replace( QLatin1Char('+'), QLatin1Char(' ') ); // + in queries means space.
return QUrl::fromPercentEncoding( str.toLatin1() );
}
else // empty value
return QString::fromLatin1("");
}
}
return QString();
}
void KUrl::addQueryItem( const QString& _item, const QString& _value )
{
QString item = _item + QLatin1Char('=');
QString value = QString::fromLatin1(QUrl::toPercentEncoding(_value).data());
QString strQueryEncoded = QString::fromLatin1(encodedQuery().data());
if (!strQueryEncoded.isEmpty())
strQueryEncoded += QLatin1Char('&');
strQueryEncoded += item + value;
setEncodedQuery( strQueryEncoded.toLatin1() );
}
void KUrl::populateMimeData( QMimeData* mimeData,
const MetaDataMap& metaData,
MimeDataFlags flags ) const
{
populateMimeDataHelper(KUrl::List(*this), mimeData, metaData, flags);
}
bool KUrl::hasRef() const
{
return hasFragment();
}
void KUrl::setRef( const QString& fragment )
{
if ( fragment.isEmpty() ) // empty or null
setFragment( fragment );
else
setFragment( QUrl::fromPercentEncoding( fragment.toLatin1() ) );
}
QString KUrl::ref() const
{
if ( fragment().isNull() )
return QString();
else
return QString::fromLatin1( QUrl::toPercentEncoding( fragment() ).data() );
}
bool KUrl::isParentOf( const KUrl& u ) const
{
return QUrl::isParentOf( u ) || equals( u, CompareWithoutTrailingSlash );
}
uint qHash(const KUrl& kurl)
{
// qHash(kurl.url()) was the worse implementation possible, since QUrl::toEncoded()
// had to concatenate the bits of the url into the full url every time.
return qHash(kurl.protocol()) ^ qHash(kurl.path()) ^ qHash(kurl.fragment()) ^ qHash(kurl.query());
}
diff --git a/kdecore/kernel/kcmdlineargs.cpp b/kdecore/kernel/kcmdlineargs.cpp
index a95bdca93e..222f2b3052 100644
--- a/kdecore/kernel/kcmdlineargs.cpp
+++ b/kdecore/kernel/kcmdlineargs.cpp
@@ -1,1643 +1,1643 @@
/*
Copyright (C) 1999 Waldo Bastian <bastian@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 version 2 as published by the Free Software Foundation.
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 "kcmdlineargs.h"
#include <kdefakes.h>
#include <config-kernel.h>
#include <sys/param.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QHash>
#include <QtCore/QTextCodec>
#include <QtCore/QUrl>
#include "kaboutdata.h"
// PORTING HACK (KDE5 TODO: clean up)
#define i18nc(a,b) QObject::tr(b, a)
// -----------------------------------------------------------------------------
// Design notes:
//
// These classes deal with a lot of text, some of which needs to be
// marked for translation. Since at the time when these object and calls are
// made the translation catalogs are usually still not initialized, the
// translation has to be delayed. This is achieved by using QLocalizedString
// for translatable strings. QLocalizedStrings are produced by qi18n* calls,
// instead of the more usuall i18n* calls which produce QString by trying to
// translate immediately.
//
// All the non-translatable string arguments to methods are taken QByteArray,
// all the translatable are QLocalizedString. The getter methods always return
// proper QString: the non-translatable strings supplied by the code are
// treated with QString::fromUtf8(), those coming from the outside with
// QTextCodec::toUnicode(), and translatable strings are finalized to QStrings
// at the point of getter calls (i.e. delayed translation).
//
// The code below uses locally defined s->decodeInput(QByteArray) and
// s->encodeOutput(QString) calls to centralize the conversion of raw external
// bytes (instead of QString::to/fromLocal8Bit(), QFile::decodeName, etc.)
// -----------------------------------------------------------------------------
#if HAVE_X11
#define DISPLAY "DISPLAY"
#else
#define DISPLAY "NODISPLAY"
#endif
//
// Helper classes
//
class KCmdLineParsedOptions : public QHash<QByteArray,QByteArray>
{
public:
KCmdLineParsedOptions() { }
};
class KCmdLineParsedArgs : public QList<QByteArray>
{
public:
KCmdLineParsedArgs() { }
};
class KCmdLineArgsList: public QList<KCmdLineArgs*>
{
public:
KCmdLineArgsList() { }
~KCmdLineArgsList() {
while (count())
delete takeFirst();
}
};
//
// KCmdLineOptions
//
class KCmdLineOptionsPrivate {
public:
QList<QByteArray> names;
QList<QLocalizedString> descriptions;
QStringList defaults;
};
KCmdLineOptions::KCmdLineOptions ()
: d(new KCmdLineOptionsPrivate)
{}
KCmdLineOptions::~KCmdLineOptions ()
{
delete d;
}
KCmdLineOptions::KCmdLineOptions (const KCmdLineOptions &options)
: d(new KCmdLineOptionsPrivate(*(options.d)))
{
}
KCmdLineOptions& KCmdLineOptions::operator= (const KCmdLineOptions &options)
{
if (this != &options) {
*d = *(options.d);
}
return *this;
}
KCmdLineOptions &KCmdLineOptions::add (const QByteArray &name,
const QLocalizedString &description,
const QByteArray &defaultValue)
{
d->names.append(name);
d->descriptions.append(description);
d->defaults.append(QString::fromUtf8(defaultValue.data()));
return *this;
}
KCmdLineOptions &KCmdLineOptions::add (const KCmdLineOptions &other)
{
d->names += other.d->names;
d->descriptions += other.d->descriptions;
d->defaults += other.d->defaults;
return *this;
}
//
// KCmdLineArgs static data and methods
//
class KCmdLineArgsStatic {
public:
KCmdLineArgsList *argsList; // All options.
const KAboutData *about;
int all_argc; // The original argc
char **all_argv; // The original argv
char *appName;
bool parsed : 1; // Whether we have parsed the arguments since calling init
bool ignoreUnknown : 1; // Ignore unknown options and arguments
QByteArray mCwd; // Current working directory. Important for KUnqiueApp!
KCmdLineArgs::StdCmdLineArgs mStdargs;
KCmdLineOptions qt_options;
KCmdLineOptions kde_options;
KCmdLineArgsStatic ();
~KCmdLineArgsStatic ();
QTextCodec *codec; // codec for converting raw input to QString
/**
* @internal
* Convertes raw command line argument data to proper QString.
*
* @param rawstr raw text
* @return properly decoded QString
*/
static QString decodeInput(const QByteArray &rawstr);
/**
* @internal
* Convertes QString to raw command line output.
*
* @param str string to be encoded
* @return raw text
*/
static QByteArray encodeOutput(const QString &str);
/**
* @internal
* Shell output with proper decoding.
*/
void printQ(const QString &msg);
/**
* @internal
* Try to match given option in the list of options.
* Returns match status.
*
* @return:
* 0 - option not found.
* 1 - option found // -fork
* 2 - inverse option found ('no') // -nofork
* 3 - option + arg found // -fork now
*
* +4 - no more options follow // !fork
*/
static int findOption(const KCmdLineOptions &options, QByteArray &opt,
QByteArray &opt_name, QString &def, bool &enabled);
/**
* @internal
*
* Checks what to do with a single option
*/
static void findOption(const QByteArray &optv, const QByteArray &_opt,
int &i, bool _enabled, bool &moreOptions);
/**
* @internal
*
* Parse all arguments, verify correct syntax and put all arguments
* where they belong.
*/
static void parseAllArgs();
/**
* @internal
*
* Remove named options.
*
* @param id The name of the options to be removed.
*/
static void removeArgs(const QByteArray &id);
/**
* @internal
*
* Convert &, ", ', <, > characters into XML entities
* &amp;, &lt;, &gt;, &apos;, &quot;, respectively.
*/
static QString escape(const QString &text);
};
Q_GLOBAL_STATIC(KCmdLineArgsStatic, staticObj)
KCmdLineArgsStatic::KCmdLineArgsStatic () {
// Global data
argsList = 0;
all_argc = 0;
all_argv = 0;
appName = 0;
mCwd.clear();
about = 0;
parsed = false;
ignoreUnknown = false;
mStdargs = 0;
// Text codec.
codec = QTextCodec::codecForLocale();
// Qt options
//FIXME: Check if other options are specific to Qt/X11
#if HAVE_X11
qt_options.add("display <displayname>", qi18n("Use the X-server display 'displayname'"));
#else
#endif
qt_options.add("session <sessionId>", qi18n("Restore the application for the given 'sessionId'"));
qt_options.add("cmap", qi18n("Causes the application to install a private color\nmap on an 8-bit display"));
qt_options.add("ncols <count>", qi18n("Limits the number of colors allocated in the color\ncube on an 8-bit display, if the application is\nusing the QApplication::ManyColor color\nspecification"));
qt_options.add("nograb", qi18n("tells Qt to never grab the mouse or the keyboard"));
qt_options.add("dograb", qi18n("running under a debugger can cause an implicit\n-nograb, use -dograb to override"));
qt_options.add("sync", qi18n("switches to synchronous mode for debugging"));
qt_options.add("fn");
qt_options.add("font <fontname>", qi18n("defines the application font"));
qt_options.add("bg");
qt_options.add("background <color>", qi18n("sets the default background color and an\napplication palette (light and dark shades are\ncalculated)"));
qt_options.add("fg");
qt_options.add("foreground <color>", qi18n("sets the default foreground color"));
qt_options.add("btn");
qt_options.add("button <color>", qi18n("sets the default button color"));
qt_options.add("name <name>", qi18n("sets the application name"));
qt_options.add("title <title>", qi18n("sets the application title (caption)"));
qt_options.add("testability", qi18n("load the testability framework"));
#if HAVE_X11
qt_options.add("visual TrueColor", qi18n("forces the application to use a TrueColor visual on\nan 8-bit display"));
qt_options.add("inputstyle <inputstyle>", qi18n("sets XIM (X Input Method) input style. Possible\nvalues are onthespot, overthespot, offthespot and\nroot"));
qt_options.add("im <XIM server>", qi18n("set XIM server"));
qt_options.add("noxim", qi18n("disable XIM"));
#endif
qt_options.add("reverse", qi18n("mirrors the whole layout of widgets"));
qt_options.add("stylesheet <file.qss>", qi18n("applies the Qt stylesheet to the application widgets"));
qt_options.add("graphicssystem <system>", qi18n("use a different graphics system instead of the default one, options are raster and opengl (experimental)"));
qt_options.add("qmljsdebugger <port>", qi18n("QML JS debugger information. Application must be\nbuilt with -DQT_DECLARATIVE_DEBUG for the debugger to be\nenabled"));
// KDE options
kde_options.add("caption <caption>", qi18n("Use 'caption' as name in the titlebar"));
kde_options.add("icon <icon>", qi18n("Use 'icon' as the application icon"));
kde_options.add("config <filename>", qi18n("Use alternative configuration file"));
kde_options.add("nocrashhandler", qi18n("Disable crash handler, to get core dumps"));
#if HAVE_X11
kde_options.add("waitforwm", qi18n("Waits for a WM_NET compatible windowmanager"));
#endif
kde_options.add("style <style>", qi18n("sets the application GUI style"));
kde_options.add("geometry <geometry>", qi18n("sets the client geometry of the main widget - see man X for the argument format (usually WidthxHeight+XPos+YPos)"));
#ifndef Q_OS_WIN
kde_options.add("smkey <sessionKey>"); // this option is obsolete and exists only to allow smooth upgrades from sessions
#endif
}
KCmdLineArgsStatic::~KCmdLineArgsStatic ()
{
delete argsList;
// KAboutData object is deleted by ~KCleanUpGlobalStatic.
//delete about;
}
//
// KCmdLineArgs private data and methods
//
class KCmdLineArgsPrivate
{
friend class KCmdLineArgsStatic;
public:
KCmdLineArgsPrivate(const KCmdLineOptions &_options, const QLocalizedString &_name, const QByteArray &_id)
: options(_options)
, name(_name)
, id(_id)
, parsedOptionList(0)
, parsedArgList(0)
, isQt(id == "qt")
{
}
~KCmdLineArgsPrivate()
{
delete parsedOptionList;
delete parsedArgList;
}
const KCmdLineOptions options;
const QLocalizedString name;
const QByteArray id;
KCmdLineParsedOptions *parsedOptionList;
KCmdLineParsedArgs *parsedArgList;
bool isQt;
/**
* @internal
*
* Set a boolean option
*/
void setOption(const QByteArray &option, bool enabled);
/**
* @internal
*
* Set a string option
*/
void setOption(const QByteArray &option, const QByteArray &value);
/**
* @internal
*
* Add an argument
*/
void addArgument(const QByteArray &argument);
/**
* @internal
*
* Save to a stream.
*/
void save( QDataStream &) const;
/**
* @internal
*
* Restore from a stream.
*/
void load( QDataStream &);
};
//
// Static functions
//
QString
KCmdLineArgsStatic::decodeInput(const QByteArray &rawstr)
{
return staticObj()->codec->toUnicode(rawstr);
}
QByteArray
KCmdLineArgsStatic::encodeOutput(const QString &str)
{
return staticObj()->codec->fromUnicode(str);
}
void
KCmdLineArgsStatic::printQ(const QString &msg)
{
fprintf(stdout, "%s", encodeOutput(msg).data());
}
void
KCmdLineArgs::init(int _argc, char **_argv,
const QByteArray &_appname,
const QByteArray &_catalog,
const QLocalizedString &_programName,
const QByteArray &_version,
const QLocalizedString &_description,
StdCmdLineArgs stdargs)
{
init(_argc, _argv,
new KAboutData(_appname, _catalog, _programName, _version, _description),
stdargs);
}
void
KCmdLineArgs::initIgnore(int _argc, char **_argv, const QByteArray &_appname )
{
init(_argc, _argv,
new KAboutData(_appname, 0, qi18n(_appname.data()), "unknown", qi18n("KDE Application")));
staticObj()->ignoreUnknown = true;
}
void
KCmdLineArgs::init(const KAboutData* ab)
{
char **_argv = (char **) malloc(sizeof(char *));
_argv[0] = (char *) staticObj()->encodeOutput(ab->appName()).data();
init(1,_argv,ab, CmdLineArgNone);
}
void
KCmdLineArgs::init(int _argc, char **_argv, const KAboutData *_about, StdCmdLineArgs stdargs)
{
staticObj()->all_argc = _argc;
staticObj()->all_argv = _argv;
if (!staticObj()->all_argv)
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
fprintf(stderr, "Passing null-pointer to 'argv' is not allowed.\n\n");
assert( 0 );
exit(255);
}
// Strip path from argv[0]
if (staticObj()->all_argc) {
- char *p = strrchr(staticObj()->all_argv[0], QDir::separator().toAscii());
+ char *p = strrchr(staticObj()->all_argv[0], QDir::separator().toLatin1());
if (p)
staticObj()->appName = p+1;
else
staticObj()->appName = staticObj()->all_argv[0];
}
staticObj()->about = _about;
staticObj()->parsed = false;
staticObj()->mCwd = QDir::currentPath().toLocal8Bit(); //currentPath() uses fromLocal8Bit internally apparently
addStdCmdLineOptions(stdargs);
}
QString KCmdLineArgs::cwd()
{
return QString::fromLocal8Bit(staticObj()->mCwd.data());
}
QString KCmdLineArgs::appName()
{
if (!staticObj()->appName) return QString();
return staticObj()->decodeInput(staticObj()->appName);
}
/**
* Add Qt and KDE command line options to KCmdLineArgs.
*/
void KCmdLineArgs::addStdCmdLineOptions(StdCmdLineArgs stdargs) {
if (stdargs & KCmdLineArgs::CmdLineArgQt) {
KCmdLineArgs::addCmdLineOptions(staticObj()->qt_options, qi18n("Qt"), "qt");
}
if (stdargs & KCmdLineArgs::CmdLineArgKDE) {
KCmdLineArgs::addCmdLineOptions(staticObj()->kde_options, qi18n("KDE"), "kde");
}
staticObj()->mStdargs = stdargs;
}
void
KCmdLineArgs::addCmdLineOptions( const KCmdLineOptions &options, const QLocalizedString &name,
const QByteArray &id, const QByteArray &afterId)
{
if (!staticObj()->argsList)
staticObj()->argsList = new KCmdLineArgsList;
int pos = staticObj()->argsList->count();
// To make sure that the named options come before unnamed.
if (pos > 0 && !id.isEmpty() && staticObj()->argsList->last()->d->name.isEmpty())
pos--;
KCmdLineArgsList::Iterator args;
int i = 0;
for(args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args, i++)
{
if (id == (*args)->d->id) {
return; // Options already present.
}
// Only check for afterId if it has been given non-empty, as the
// unnamed option group should come after all named groups.
if (!afterId.isEmpty() && afterId == (*args)->d->id)
pos = i+1;
}
Q_ASSERT( staticObj()->parsed == false ); // You must add _ALL_ cmd line options
// before accessing the arguments!
staticObj()->argsList->insert(pos, new KCmdLineArgs(options, name, id));
}
void
KCmdLineArgs::saveAppArgs( QDataStream &ds)
{
if (!staticObj()->parsed)
staticObj()->parseAllArgs();
// Remove Qt and KDE options.
staticObj()->removeArgs("qt");
staticObj()->removeArgs("kde");
staticObj()->removeArgs("kuniqueapp");
ds << staticObj()->mCwd;
uint count = staticObj()->argsList ? staticObj()->argsList->count() : 0;
ds << count;
if (!count) return;
KCmdLineArgsList::Iterator args;
for(args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args)
{
ds << (*args)->d->id;
(*args)->d->save(ds);
}
}
void
KCmdLineArgs::loadAppArgs( QDataStream &ds)
{
staticObj()->parsed = true; // don't reparse argc/argv!
// Remove Qt and KDE options.
staticObj()->removeArgs("qt");
staticObj()->removeArgs("kde");
staticObj()->removeArgs("kuniqueapp");
KCmdLineArgsList::Iterator args;
if ( staticObj()->argsList ) {
for(args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args)
{
(*args)->clear();
}
}
if (ds.atEnd())
return;
QByteArray qCwd;
ds >> qCwd;
staticObj()->mCwd = qCwd;
uint count;
ds >> count;
while(count--)
{
QByteArray id;
ds >> id;
Q_ASSERT( staticObj()->argsList );
bool found = false;
for(args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args)
{
if ((*args)->d->id == id)
{
(*args)->d->load(ds);
found = true;
break;
}
}
if (!found) {
qWarning() << "Argument definitions for" << id << "not found!";
// The next ds >> id will do nonsensical things...
}
}
staticObj()->parsed = true;
}
KCmdLineArgs *KCmdLineArgs::parsedArgs(const QByteArray &id)
{
if (!staticObj()->argsList)
return 0;
KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
while(args != staticObj()->argsList->end())
{
if ((*args)->d->id == id)
{
if (!staticObj()->parsed)
staticObj()->parseAllArgs();
return *args;
}
++args;
}
return 0;
}
void KCmdLineArgsStatic::removeArgs(const QByteArray &id)
{
if (!staticObj()->argsList)
return;
KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
while(args != staticObj()->argsList->end())
{
if ((*args)->d->id == id)
{
if (!staticObj()->parsed)
staticObj()->parseAllArgs();
break;
}
++args;
}
if (args != staticObj()->argsList->end()) {
KCmdLineArgs *a = *args;
staticObj()->argsList->erase(args);
delete a;
}
}
#pragma message("KDE5 TODO: Remove this method once it is in Qt5 and import it to libinqt5")
QString KCmdLineArgsStatic::escape(const QString &text)
{
int tlen = text.length();
QString ntext;
ntext.reserve(tlen);
for (int i = 0; i < tlen; ++i) {
QChar c = text[i];
if (c == QLatin1Char('&')) {
ntext += QLatin1String("&amp;");
} else if (c == QLatin1Char('<')) {
ntext += QLatin1String("&lt;");
} else if (c == QLatin1Char('>')) {
ntext += QLatin1String("&gt;");
} else if (c == QLatin1Char('\'')) {
ntext += QLatin1String("&apos;"); // not handled by Qt::escape
} else if (c == QLatin1Char('"')) {
ntext += QLatin1String("&quot;");
} else {
ntext += c;
}
}
return ntext;
}
int
KCmdLineArgsStatic::findOption(const KCmdLineOptions &options, QByteArray &opt,
QByteArray &opt_name, QString &def, bool &enabled)
{
int result;
bool inverse;
for (int i = 0; i < options.d->names.size(); i++)
{
result = 0;
inverse = false;
opt_name = options.d->names[i];
if (opt_name.startsWith(':') || opt_name.isEmpty())
{
continue;
}
if (opt_name.startsWith('!'))
{
opt_name = opt_name.mid(1);
result = 4;
}
if (opt_name.startsWith("no") && !opt_name.contains('<')) // krazy:exclude=strings
{
opt_name = opt_name.mid(2);
inverse = true;
}
int len = opt.length();
if (opt == opt_name.left(len))
{
opt_name = opt_name.mid(len);
if (opt_name.isEmpty())
{
if (inverse)
return result+2;
if (options.d->descriptions[i].isEmpty())
{
i++;
if (i >= options.d->names.size())
return result+0;
QByteArray nextOption = options.d->names[i];
int p = nextOption.indexOf(' ');
if (p > 0)
nextOption = nextOption.left(p);
if (nextOption.startsWith('!'))
nextOption = nextOption.mid(1);
if (nextOption.startsWith("no") && !nextOption.contains('<')) // krazy:exclude=strings
{
nextOption = nextOption.mid(2);
enabled = !enabled;
}
result = findOption(options, nextOption, opt_name, def, enabled);
Q_ASSERT(result);
opt = nextOption;
return result;
}
return 1;
}
if (opt_name.startsWith(' '))
{
opt_name = opt_name.mid(1);
def = options.d->defaults[i];
return result+3;
}
}
}
return 0;
}
void
KCmdLineArgsStatic::findOption(const QByteArray &optv, const QByteArray &_opt,
int &i, bool _enabled, bool &moreOptions)
{
KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
QByteArray opt = _opt;
QByteArray opt_name;
QString def;
QByteArray argument;
int j = opt.indexOf('=');
if (j != -1)
{
argument = opt.mid(j+1);
opt = opt.left(j);
}
bool enabled = true;
int result = 0;
while (args != staticObj()->argsList->end())
{
enabled = _enabled;
result = findOption((*args)->d->options, opt, opt_name, def, enabled);
if (result) break;
++args;
}
if ((args == staticObj()->argsList->end()) &&
(optv.startsWith('-') && !optv.startsWith("--"))) // krazy:exclude=strings
{
// Option not found check if it is a valid option
// in the style of -Pprinter1 or ps -aux
int p = 1;
while (true)
{
QByteArray singleCharOption = " "; // krazy:exclude=doublequote_chars
singleCharOption[0] = optv[p];
args = staticObj()->argsList->begin();
while (args != staticObj()->argsList->end())
{
enabled = _enabled;
result = findOption((*args)->d->options, singleCharOption,
opt_name, def, enabled);
if (result) break;
++args;
}
if (args == staticObj()->argsList->end())
break; // Unknown argument
p++;
if (result == 1) // Single option
{
(*args)->d->setOption(singleCharOption, enabled);
if (p < optv.length())
continue; // Next option
else
return; // Finished
}
else if (result == 3) // This option takes an argument
{
if (argument.isEmpty())
{
argument = optv.mid(p);
}
(*args)->d->setOption(singleCharOption, argument);
return;
}
break; // Unknown argument
}
args = staticObj()->argsList->end();
result = 0;
}
if (args == staticObj()->argsList->end() || !result)
{
if (staticObj()->ignoreUnknown)
return;
KCmdLineArgs::enable_i18n();
KCmdLineArgs::usageError(QObject::tr("Unknown option '%1'.").arg(QString::fromLocal8Bit(_opt.data())));
}
if ((result & 4) != 0)
{
result &= ~4;
moreOptions = false;
}
if (result == 3) // This option takes an argument
{
if (!enabled)
{
if (staticObj()->ignoreUnknown)
return;
KCmdLineArgs::enable_i18n();
KCmdLineArgs::usageError(QObject::tr("Unknown option '%1'.").arg(QString::fromLocal8Bit(_opt.data())));
}
if (argument.isEmpty())
{
i++;
if (i >= staticObj()->all_argc)
{
KCmdLineArgs::enable_i18n();
KCmdLineArgs::usageError(i18nc("@info:shell %1 is cmdoption name","'%1' missing.").arg(QString::fromLocal8Bit(opt_name.data())));
}
argument = staticObj()->all_argv[i];
}
(*args)->d->setOption(opt, argument);
}
else
{
(*args)->d->setOption(opt, enabled);
}
}
void
KCmdLineArgsStatic::parseAllArgs()
{
bool allowArgs = false;
bool inOptions = true;
bool everythingAfterArgIsArgs = false;
KCmdLineArgs *appOptions = staticObj()->argsList->last();
if (appOptions->d->id.isEmpty())
{
Q_FOREACH(const QByteArray& name, appOptions->d->options.d->names)
{
everythingAfterArgIsArgs = everythingAfterArgIsArgs || name.startsWith("!+");
allowArgs = allowArgs || name.startsWith('+') || everythingAfterArgIsArgs;
}
}
for(int i = 1; i < staticObj()->all_argc; i++)
{
if (!staticObj()->all_argv[i])
continue;
if ((staticObj()->all_argv[i][0] == '-') && staticObj()->all_argv[i][1] && inOptions)
{
bool enabled = true;
QByteArray orig = staticObj()->all_argv[i];
QByteArray option = orig.mid(1);
if (option.startsWith('-'))
{
option = option.mid(1);
if (option.isEmpty())
{
inOptions = false;
continue;
}
}
if (option == "help")
{
KCmdLineArgs::usage();
}
else if (option.startsWith("help-")) // krazy:exclude=strings
{
KCmdLineArgs::usage(option.mid(5));
}
#ifdef Q_OS_MAC
// skip the finder -psn_* hint
else if (option.startsWith("psn_")) // krazy:exclude=strings
{
continue;
}
#endif
else if ((option == "version") || (option == "v"))
{
KCmdLineArgs::enable_i18n();
#pragma message("KDE5 TODO: use kcoreaddons version number")
staticObj()->printQ(i18nc("@info:shell message on appcmd --version; do not translate 'Development Platform'"
"%3 application name, other %n version strings",
"Qt: %1\n"
"KDE Development Platform: %2\n"
"%3: %4\n").arg(QString::fromLatin1(qVersion()),
QString::fromLatin1("TODO" /*KDE_VERSION_STRING*/),
staticObj()->about->programName(), staticObj()->about->version()));
exit(0);
} else if (option == "license")
{
KCmdLineArgs::enable_i18n();
staticObj()->printQ(staticObj()->about->license());
staticObj()->printQ(QString::fromLatin1("\n"));
exit(0);
} else if (option == "author") {
KCmdLineArgs::enable_i18n();
if ( staticObj()->about ) {
const QList<KAboutPerson> authors = staticObj()->about->authors();
if ( !authors.isEmpty() ) {
QString authorlist;
for (QList<KAboutPerson>::ConstIterator it = authors.begin(); it != authors.end(); ++it ) {
QString email;
if ( !(*it).emailAddress().isEmpty() )
email = QString::fromLatin1(" &lt;") + (*it).emailAddress() + QLatin1String("&gt;");
authorlist += QString::fromLatin1(" ") + (*it).name() + email + QLatin1Char('\n');
}
staticObj()->printQ(i18nc("the 2nd argument is a list of name+address, one on each line","%1 was written by\n%2").arg(QString(staticObj()->about->programName()), authorlist));
}
} else {
staticObj()->printQ(QObject::tr("This application was written by somebody who wants to remain anonymous."));
}
if (staticObj()->about)
{
if (!staticObj()->about->customAuthorTextEnabled ())
{
if (staticObj()->about->bugAddress().isEmpty() || staticObj()->about->bugAddress() == QLatin1String("submit@bugs.kde.org") )
staticObj()->printQ(QObject::tr( "Please use http://bugs.kde.org to report bugs.\n"));
else
staticObj()->printQ(QObject::tr( "Please report bugs to %1.\n").arg(staticObj()->about->bugAddress()));
}
else
{
staticObj()->printQ(staticObj()->about->customAuthorPlainText()+QLatin1Char('\n'));
}
}
exit(0);
} else {
if (option.startsWith("no")) // krazy:exclude=strings
{
bool noHasParameter=false;
Q_FOREACH(const QByteArray& name, appOptions->d->options.d->names)
{
if (name.contains(option + QByteArray(" ")) && name.contains('<'))
{
noHasParameter=true;
break;
}
}
if (!noHasParameter)
{
option = option.mid(2);
enabled = false;
}
}
staticObj()->findOption(orig, option, i, enabled, inOptions);
}
}
else
{
// Check whether appOptions allows these arguments
if (!allowArgs)
{
if (staticObj()->ignoreUnknown)
continue;
KCmdLineArgs::enable_i18n();
KCmdLineArgs::usageError(QObject::tr("Unexpected argument '%1'.").arg(staticObj()->escape(staticObj()->decodeInput(staticObj()->all_argv[i]))));
}
else
{
appOptions->d->addArgument(staticObj()->all_argv[i]);
if (everythingAfterArgIsArgs)
inOptions = false;
}
}
}
staticObj()->parsed = true;
}
int & KCmdLineArgs::qtArgc()
{
if (!staticObj()->argsList)
addStdCmdLineOptions(CmdLineArgKDE|CmdLineArgQt); // Lazy bastards!
static int qt_argc = -1;
if( qt_argc != -1 )
return qt_argc;
if (!(staticObj()->mStdargs & KCmdLineArgs::CmdLineArgQt))
{
qt_argc = 2;
return qt_argc;
}
KCmdLineArgs *args = parsedArgs("qt");
Q_ASSERT(args); // No qt options have been added!
if (!staticObj()->all_argv)
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
fprintf(stderr, "Application has not called KCmdLineArgs::init(...).\n\n");
assert( 0 );
exit(255);
}
Q_ASSERT(staticObj()->all_argc >= (args->count()+1));
qt_argc = args->count() +1;
return qt_argc;
}
static char** s_qt_argv;
char **
KCmdLineArgs::qtArgv()
{
if (!staticObj()->argsList)
addStdCmdLineOptions(CmdLineArgKDE|CmdLineArgQt); // Lazy bastards!
if( s_qt_argv != NULL )
return s_qt_argv;
if (!(staticObj()->mStdargs & KCmdLineArgs::CmdLineArgQt))
{
s_qt_argv = new char*[2];
s_qt_argv[0] = qstrdup(staticObj()->all_argc?staticObj()->all_argv[0]:"");
s_qt_argv[1] = 0;
return s_qt_argv;
}
KCmdLineArgs *args = parsedArgs("qt");
if (!args)
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
fprintf(stderr, "The \"qt\" options have not be added to KCmdLineArgs!\n\n");
assert( 0 );
exit(255);
}
if (!staticObj()->all_argv)
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
fprintf(stderr, "Application has not called KCmdLineArgs::init(...).\n\n");
assert( 0 );
exit(255);
}
int count=args->count();
s_qt_argv = new char*[ count + 2 ];
s_qt_argv[0] = qstrdup(staticObj()->all_argc?staticObj()->all_argv[0]:"");
int i = 0;
for(; i < count; i++)
{
s_qt_argv[i+1] = qstrdup(args->d->parsedArgList->at(i).data());
}
s_qt_argv[i+1] = 0;
return s_qt_argv;
}
const KAboutData *
KCmdLineArgs::aboutData()
{
return staticObj()->about;
}
void
KCmdLineArgs::enable_i18n()
{
#pragma message ("KDE5 NOTE: What about this method ?")
}
void
KCmdLineArgs::usageError(const QString &error)
{
QByteArray localError = staticObj()->encodeOutput(error);
if (localError.endsWith('\n'))
localError.chop(1);
fprintf(stderr, "%s: %s\n", staticObj()->appName, localError.data());
QString tmp = QObject::tr("Use --help to get a list of available command line options.");
localError = staticObj()->encodeOutput(tmp);
fprintf(stderr, "%s: %s\n", staticObj()->appName, localError.data());
exit(254);
}
void
KCmdLineArgs::usage(const QByteArray &id)
{
enable_i18n();
Q_ASSERT(staticObj()->argsList != 0); // It's an error to call usage(...) without
// having done addCmdLineOptions first!
QString optionFormatString = QString::fromLatin1(" %1 %2\n");
QString optionFormatStringDef = QString::fromLatin1(" %1 %2 [%3]\n");
QString tmp;
QString usage;
KCmdLineArgsList::Iterator args = --(staticObj()->argsList->end());
if ((*args)->d->id.isEmpty() && ((*args)->d->options.d->names.size() > 0) &&
!(*args)->d->options.d->names[0].startsWith('+'))
{
usage = QObject::tr("[options] ")+usage;
}
while(true)
{
if (!(*args)->d->name.isEmpty())
{
usage = QObject::tr("[%1-options]").arg((*args)->d->name.toString())+QLatin1Char(' ')+usage;
}
if (args == staticObj()->argsList->begin())
break;
--args;
}
KCmdLineArgs *appOptions = staticObj()->argsList->last();
if (appOptions->d->id.isEmpty())
{
const KCmdLineOptions &option = appOptions->d->options;
for (int i = 0; i < option.d->names.size(); i++)
{
QByteArray opt_name = option.d->names[i];
if (opt_name.startsWith('+'))
usage += QString::fromLatin1(opt_name.mid(1).data()) + QLatin1Char(' ');
else if ( opt_name.startsWith("!+") )
usage += QString::fromLatin1(opt_name.mid(2).data()) + QLatin1Char(' ');
}
}
staticObj()->printQ(QObject::tr("Usage: %1 %2\n").arg(QString::fromLocal8Bit(staticObj()->appName)).arg(staticObj()->escape(usage)));
staticObj()->printQ(QLatin1Char('\n')+staticObj()->about->shortDescription()+QLatin1Char('\n'));
staticObj()->printQ(QObject::tr("\nGeneric options:\n"));
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--help"), -25)
.arg(QObject::tr("Show help about options")));
args = staticObj()->argsList->begin();
while(args != staticObj()->argsList->end())
{
if (!(*args)->d->name.isEmpty() && !(*args)->d->id.isEmpty())
{
QString option = QString::fromLatin1("--help-%1").arg(QString::fromLatin1((*args)->d->id.data()));
QString desc = QObject::tr("Show %1 specific options").arg((*args)->d->name.toString());
staticObj()->printQ(optionFormatString.arg(option, -25).arg(desc));
}
++args;
}
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--help-all"),-25).arg(QObject::tr("Show all options")));
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--author"),-25).arg(QObject::tr("Show author information")));
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("-v, --version"),-25).arg(QObject::tr("Show version information")));
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--license"),-25).arg(QObject::tr("Show license information")));
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--"), -25).arg(QObject::tr("End of options")));
args = staticObj()->argsList->begin(); // Sets current to 1st.
bool showAll = (id == "all");
if (!showAll)
{
while(args != staticObj()->argsList->end())
{
if (id == (*args)->d->id) break;
++args;
}
}
while(args != staticObj()->argsList->end())
{
bool hasArgs = false;
bool hasOptions = false;
QString optionsHeader;
if (!(*args)->d->name.isEmpty())
optionsHeader = QObject::tr("\n%1 options:\n").arg((*args)->d->name.toString());
else
optionsHeader = QObject::tr("\nOptions:\n");
while (args != staticObj()->argsList->end())
{
const KCmdLineOptions &option = (*args)->d->options;
QByteArray opt;
for (int i = 0; i < option.d->names.size(); i++)
{
QString description;
QStringList dl;
QString descriptionFull;
if (!option.d->descriptions[i].isEmpty()) {
descriptionFull = option.d->descriptions[i].toString();
}
// Option header
if (option.d->names[i].startsWith(':'))
{
if (!descriptionFull.isEmpty())
{
optionsHeader = QLatin1Char('\n')+descriptionFull;
if (!optionsHeader.endsWith(QLatin1Char('\n')))
optionsHeader.append(QLatin1Char('\n'));
hasOptions = false;
}
continue;
}
// Free-form comment
if (option.d->names[i].isEmpty())
{
if (!descriptionFull.isEmpty())
{
tmp = QLatin1Char('\n')+descriptionFull;
if (!tmp.endsWith(QLatin1Char('\n')))
tmp.append(QLatin1Char('\n'));
staticObj()->printQ(tmp);
}
continue;
}
// Options
if (!descriptionFull.isEmpty())
{
dl = descriptionFull.split(QLatin1Char('\n'), QString::KeepEmptyParts);
description = dl.first();
dl.erase( dl.begin() );
}
QByteArray name = option.d->names[i];
if (name.startsWith('!'))
name = name.mid(1);
if (name.startsWith('+'))
{
if (!hasArgs)
{
staticObj()->printQ(QObject::tr("\nArguments:\n"));
hasArgs = true;
}
name = name.mid(1);
if (name.startsWith('[') && name.endsWith(']'))
name = name.mid(1, name.length()-2);
staticObj()->printQ(optionFormatString.arg(QString::fromLocal8Bit(name.data()), -25).arg(description));
}
else
{
if (!hasOptions)
{
staticObj()->printQ(optionsHeader);
hasOptions = true;
}
if ((name.length() == 1) || (name[1] == ' '))
name = '-'+name;
else
name = "--"+name;
if (descriptionFull.isEmpty())
{
opt = name + ", ";
}
else
{
opt = opt + name;
if (option.d->defaults[i].isEmpty())
{
staticObj()->printQ(optionFormatString.arg(QString::fromLatin1(opt.data()), -25).arg(description));
}
else
{
staticObj()->printQ(optionFormatStringDef.arg(QString::fromLatin1(opt.data()), -25)
.arg(description, option.d->defaults[i]));
}
opt.clear();
}
}
for(QStringList::Iterator it = dl.begin();
it != dl.end();
++it)
{
staticObj()->printQ(optionFormatString.arg(QString(), -25).arg(*it));
}
}
++args;
if (args == staticObj()->argsList->end() || !(*args)->d->name.isEmpty() || (*args)->d->id.isEmpty())
break;
}
if (!showAll) break;
}
exit(0);
}
//
// Member functions
//
/**
* Constructor.
*
* The given arguments are assumed to be constants.
*/
KCmdLineArgs::KCmdLineArgs( const KCmdLineOptions &_options,
const QLocalizedString &_name,
const QByteArray &_id)
: d(new KCmdLineArgsPrivate(_options, _name, _id))
{
}
/**
* Destructor.
*/
KCmdLineArgs::~KCmdLineArgs()
{
#pragma message("Qt5 TODO: use new Q_GLOBAL_STATIC isDestroyed")
if (/* Qt5 TODO !staticObj()->isDestroyed() &&*/ staticObj()->argsList)
staticObj()->argsList->removeAll(this);
delete d;
}
void
KCmdLineArgs::setCwd( const QByteArray &cwd )
{
staticObj()->mCwd = cwd;
}
void
KCmdLineArgs::clear()
{
delete d->parsedArgList; d->parsedArgList = 0;
delete d->parsedOptionList; d->parsedOptionList = 0;
}
void
KCmdLineArgs::reset()
{
delete staticObj()->argsList; staticObj()->argsList = 0;
staticObj()->parsed = false;
}
void
KCmdLineArgsPrivate::save( QDataStream &ds) const
{
if (parsedOptionList)
ds << (*(parsedOptionList));
else
ds << quint32(0);
if (parsedArgList)
ds << (*(parsedArgList));
else
ds << quint32(0);
}
void
KCmdLineArgsPrivate::load( QDataStream &ds)
{
if (!parsedOptionList) parsedOptionList = new KCmdLineParsedOptions;
if (!parsedArgList) parsedArgList = new KCmdLineParsedArgs;
ds >> (*(parsedOptionList));
ds >> (*(parsedArgList));
if (parsedOptionList->count() == 0)
{
delete parsedOptionList; parsedOptionList = 0;
}
if (parsedArgList->count() == 0)
{
delete parsedArgList; parsedArgList = 0;
}
}
void
KCmdLineArgsPrivate::setOption(const QByteArray &opt, bool enabled)
{
if (isQt)
{
// Qt does it own parsing.
QByteArray argString = "-"; // krazy:exclude=doublequote_chars
if( !enabled )
argString += "no";
argString += opt;
addArgument(argString);
}
if (!parsedOptionList) {
parsedOptionList = new KCmdLineParsedOptions;
}
if (enabled)
parsedOptionList->insert( opt, "t" ); // krazy:exclude=doublequote_chars
else
parsedOptionList->insert( opt, "f" ); // krazy:exclude=doublequote_chars
}
void
KCmdLineArgsPrivate::setOption(const QByteArray &opt, const QByteArray &value)
{
if (isQt)
{
// Qt does it's own parsing.
QByteArray argString = "-"; // krazy:exclude=doublequote_chars
argString += opt;
if (opt == "qmljsdebugger") {
// hack: Qt expects the value of the "qmljsdebugger" option to be
// passed using a '=' separator rather than a space, so we recreate it
// correctly.
// See code of QCoreApplicationPrivate::processCommandLineArguments()
addArgument(argString + "=" + value);
} else {
addArgument(argString);
addArgument(value);
}
#if HAVE_X11
// Hack coming up!
if (argString == "-display")
{
qputenv(DISPLAY, value);
}
#endif
}
if (!parsedOptionList) {
parsedOptionList = new KCmdLineParsedOptions;
}
parsedOptionList->insertMulti( opt, value );
}
QString
KCmdLineArgs::getOption(const QByteArray &_opt) const
{
QByteArray opt = _opt;
QByteArray value;
if (d->parsedOptionList)
{
value = d->parsedOptionList->value(opt);
}
if (!value.isEmpty())
return QString::fromLocal8Bit(value.data());
// Look up the default.
QByteArray opt_name;
QString def;
bool dummy = true;
int result = staticObj()->findOption( d->options, opt, opt_name, def, dummy) & ~4;
if (result != 3)
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
fprintf(stderr, "Application requests for getOption(\"%s\") but the \"%s\" option\n",
opt.data(), opt.data());
fprintf(stderr, "has never been specified via addCmdLineOptions( ... )\n\n");
Q_ASSERT( 0 );
exit(255);
}
return def;
}
QStringList
KCmdLineArgs::getOptionList(const QByteArray &opt) const
{
QStringList result;
if (!d->parsedOptionList)
return result;
while(true)
{
QByteArray value = d->parsedOptionList->take(opt);
if (value.isEmpty())
break;
result.prepend(QString::fromLocal8Bit(value.data()));
}
// Reinsert items in dictionary
// WABA: This is rather silly, but I don't want to add restrictions
// to the API like "you can only call this function once".
// I can't access all items without taking them out of the list.
// So taking them out and then putting them back is the only way.
Q_FOREACH(const QString &str, result)
{
d->parsedOptionList->insertMulti(opt, str.toLocal8Bit());
}
return result;
}
bool
KCmdLineArgs::isSet(const QByteArray &_opt) const
{
// Look up the default.
QByteArray opt = _opt;
QByteArray opt_name;
QString def;
int result = 0;
KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
while (args != staticObj()->argsList->end())
{
bool dummy = true;
result = staticObj()->findOption((*args)->d->options, opt, opt_name, def, dummy) & ~4;
if (result) break;
++args;
}
if (result == 0)
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
fprintf(stderr, "Application requests for isSet(\"%s\") but the \"%s\" option\n",
opt.data(), opt.data());
fprintf(stderr, "has never been specified via addCmdLineOptions( ... )\n\n");
Q_ASSERT( 0 );
exit(255);
}
QByteArray value;
if (d->parsedOptionList)
{
value = d->parsedOptionList->value(opt);
}
if (!value.isEmpty())
{
if (result == 3)
return true;
else
return (value.at(0) == 't');
}
if (result == 3)
return false; // String option has 'false' as default.
// We return 'true' as default if the option was listed as '-nofork'
// We return 'false' as default if the option was listed as '-fork'
return (result == 2);
}
int
KCmdLineArgs::count() const
{
return d->parsedArgList?d->parsedArgList->count():0;
}
QString
KCmdLineArgs::arg(int n) const
{
if (!d->parsedArgList || (n >= (int) d->parsedArgList->count()))
{
fprintf(stderr, "\n\nFAILURE (KCmdLineArgs): Argument out of bounds\n");
fprintf(stderr, "Application requests for arg(%d) without checking count() first.\n",
n);
Q_ASSERT( 0 );
exit(255);
}
return QString::fromLocal8Bit(d->parsedArgList->at(n).data());
}
QUrl
KCmdLineArgs::url(int n) const
{
return makeURL( arg(n).toUtf8() );
}
QUrl KCmdLineArgs::makeURL(const QByteArray &_urlArg)
{
const QString urlArg = QString::fromUtf8(_urlArg.data());
QFileInfo fileInfo(urlArg);
if (!fileInfo.isRelative()) { // i.e. starts with '/', on unix
QUrl result = QUrl::fromLocalFile(QDir::fromNativeSeparators(urlArg));
return result; // Absolute path.
}
QUrl qurl(urlArg);
if ( qurl.isRelative() || fileInfo.exists() ) {
QUrl result = QUrl::fromLocalFile(cwd()+QLatin1Char('/')+urlArg);
#if 0 //Qt5 TODO: QUrlInfo::cleanPath
result.cleanPath(); //This did use KUrl::cleanPath()
#endif
return result; // Relative path
}
return QUrl(urlArg); // Argument is a URL
}
void
KCmdLineArgsPrivate::addArgument(const QByteArray &argument)
{
if (!parsedArgList)
parsedArgList = new KCmdLineParsedArgs;
parsedArgList->append(argument);
}
void
KCmdLineArgs::addTempFileOption()
{
KCmdLineOptions tmpopt;
tmpopt.add( "tempfile", qi18n("The files/URLs opened by the application will be deleted after use") );
KCmdLineArgs::addCmdLineOptions( tmpopt, qi18n("KDE-tempfile"), "kde-tempfile" );
}
bool KCmdLineArgs::isTempFileSet()
{
KCmdLineArgs* args = KCmdLineArgs::parsedArgs( "kde-tempfile" );
return args && args->isSet( "tempfile" );
}
QStringList KCmdLineArgs::allArguments()
{
QStringList lst;
for(int i = 0; i < staticObj()->all_argc; i++) {
char* arg = staticObj()->all_argv[i];
if (!arg)
continue;
lst.append(QString::fromLocal8Bit(arg));
}
return lst;
}
diff --git a/kdecore/kernel/kkernel_mac.cpp b/kdecore/kernel/kkernel_mac.cpp
index ccf127440f..1450b63f3e 100644
--- a/kdecore/kernel/kkernel_mac.cpp
+++ b/kdecore/kernel/kkernel_mac.cpp
@@ -1,211 +1,211 @@
/*
This file is part of the KDE libraries
Copyright (C) 2008 Benjamin Reed <rangerrick@befunk.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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 "kkernel_mac.h"
#ifdef Q_OS_MACX
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <crt_externs.h>
#include <mach-o/dyld.h>
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFURL.h>
#include <QtCore/QFile>
#include <QtCore/QProcess>
#include <QtCore/QStringList>
#include <QtCore/qvarlengtharray.h>
#include <ksharedconfig.h>
#include <kconfig.h>
#include <kdebug.h>
int timeout = 3000; // msec
bool dbus_initialized = false;
/**
qAppFileName() is not public in qt4/mac, so we need to redo it here
*/
QString convert_CFString_to_QString(CFStringRef str) {
CFIndex length = CFStringGetLength(str);
const UniChar *chars = CFStringGetCharactersPtr(str);
if (chars)
return QString(reinterpret_cast<const QChar *>(chars), length);
QVarLengthArray<UniChar> buffer(length);
CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
return QString(reinterpret_cast<const QChar *>(buffer.constData()), length);
}
/**
Calling CoreFoundation APIs (which is unavoidable in Qt/Mac) has always had issues
on Mac OS X, but as of 10.5 is explicitly disallowed with an exception. As a
result, in the case where we would normally fork and then dlopen code, or continue
to run other code, we must now fork-and-exec.
See "CoreFoundation and fork()" at http://developer.apple.com/releasenotes/CoreFoundation/CoreFoundation.html
*/
void
mac_fork_and_reexec_self()
{
int argc = *_NSGetArgc();
char ** argv = *_NSGetArgv();
char * newargv[argc+2];
char progname[PATH_MAX];
uint32_t buflen = PATH_MAX;
_NSGetExecutablePath(progname, &buflen);
bool found_psn = false;
for (int i = 0; i < argc; i++) {
newargv[i] = argv[i];
}
newargv[argc] = "--nofork";
newargv[argc+1] = NULL;
int x_fork_result = fork();
switch(x_fork_result) {
case -1:
#ifndef NDEBUG
fprintf(stderr, "Mac OS X workaround fork() failed!\n");
#endif
::_exit(255);
break;
case 0:
// Child
execvp(progname, newargv);
break;
default:
// Parent
_exit(0);
break;
}
}
/**
Set the D-Bus environment based on session bus socket
*/
bool mac_set_dbus_address(QString value)
{
if (!value.isEmpty() && QFile::exists(value) && (QFile::permissions(value) & QFile::WriteUser)) {
value = QLatin1String("unix:path=") + value;
qputenv("DBUS_SESSION_BUS_ADDRESS", value.toLocal8Bit());
kDebug() << "set session bus address to" << value;
return true;
}
return false;
}
/**
Make sure D-Bus is initialized, by any means necessary.
*/
void mac_initialize_dbus()
{
if (dbus_initialized)
return;
QString dbusVar = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
if (!dbusVar.isEmpty()) {
dbus_initialized = true;
return;
}
dbusVar = QFile::decodeName(qgetenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET"));
if (mac_set_dbus_address(dbusVar)) {
dbus_initialized = true;
return;
}
QString externalProc;
QStringList path = QFile::decodeName(qgetenv("KDEDIRS")).split(QLatin1Char(':')).replaceInStrings(QRegExp(QLatin1String("$")), QLatin1String("/bin"));
path << QFile::decodeName(qgetenv("PATH")).split(QLatin1Char(':')) << QLatin1String("/usr/local/bin");
for (int i = 0; i < path.size(); ++i) {
QString testLaunchctl = QString(path.at(i)).append(QLatin1String("/launchctl"));
if (QFile(testLaunchctl).exists()) {
externalProc = testLaunchctl;
break;
}
}
if (!externalProc.isEmpty()) {
QProcess qp;
qp.setTextModeEnabled(true);
qp.start(externalProc, QStringList() << QLatin1String("getenv") << QLatin1String("DBUS_LAUNCHD_SESSION_BUS_SOCKET"));
if (!qp.waitForFinished(timeout)) {
kDebug() << "error running" << externalProc << qp.errorString();
return;
}
if (qp.exitCode() != 0) {
kDebug() << externalProc << "unsuccessful:" << qp.readAllStandardError();
return;
}
- QString line = QString::fromAscii(qp.readLine()).trimmed(); // read the first line
+ QString line = QString::fromLatin1(qp.readLine()).trimmed(); // read the first line
if (mac_set_dbus_address(line))
dbus_initialized = true; // hooray
}
if (dbus_initialized == false) {
kDebug() << "warning: unable to initialize D-Bus environment!";
}
}
QString mac_app_filename() {
static QString appFileName;
if (appFileName.isEmpty()) {
CFURLRef bundleURL = NULL;
CFBundleRef bundle = NULL;
CFStringRef bundlePath = NULL;
bundle = CFBundleGetMainBundle();
if (bundle) {
bundleURL = CFBundleCopyBundleURL(bundle);
bundlePath = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
if (bundleURL) {
CFRelease(bundleURL);
}
if (bundlePath) {
appFileName = convert_CFString_to_QString(bundlePath);
CFRelease(bundlePath);
}
}
}
return appFileName;
}
#endif
diff --git a/kdecore/kernel/kkernel_win.cpp b/kdecore/kernel/kkernel_win.cpp
index 27eb0101bd..cf8c4c9718 100644
--- a/kdecore/kernel/kkernel_win.cpp
+++ b/kdecore/kernel/kkernel_win.cpp
@@ -1,537 +1,537 @@
/*
This file is part of the KDE libraries
Copyright (C) 2004 Jarosław Staniek <staniek@kde.org>
Copyright (C) 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de>
Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de>
Copyright (C) 2008-2009 Ralf Habacker <ralf.habacker@freenet.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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 "kkernel_win.h"
#include <kdefakes.h>
#include <QtCore/QTextCodec>
#ifdef Q_OS_WIN
#include <klocalizedstring.h>
#include <QtCore/QDir>
#include <QtCore/QString>
#include <QtCore/QLibrary>
#include <windows.h>
#include <shellapi.h>
#include <process.h>
// console related includes
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>
#ifndef _USE_OLD_IOSTREAMS
using namespace std;
#endif
#if defined(__MINGW32__)
# define WIN32_CAST_CHAR (const WCHAR*)
#else
# define WIN32_CAST_CHAR (LPCWSTR)
#endif
#ifndef _WIN32_WCE
static HINSTANCE kdecoreDllInstance = NULL;
#else
static HANDLE kdecoreDllInstance = NULL;
#endif
#ifdef KDELIBS_STATIC_LIBS
static bool kde4prefixInitialized = false;
#endif
static wchar_t kde4prefixUtf16[MAX_PATH + 2] = L"";
static QString *kde4Prefix = NULL;
void initKde4prefixUtf16()
{
//the path is C:\some\path\kde4\bin\kdecore.dll
#ifndef _WIN32_WCE
GetModuleFileNameW(kdecoreDllInstance, kde4prefixUtf16, MAX_PATH + 1);
#else
GetModuleFileNameW((HMODULE)kdecoreDllInstance, kde4prefixUtf16, MAX_PATH + 1);
#endif
int bs1 = 0, bs2 = 0;
//we convert \ to / and remove \bin\kdecore.dll from the string
int pos;
for (pos = 0; pos < MAX_PATH + 1 && kde4prefixUtf16[pos] != 0; ++pos) {
if (kde4prefixUtf16[pos] == '\\') {
bs1 = bs2;
bs2 = pos;
kde4prefixUtf16[pos] = '/';
}
}
Q_ASSERT(bs1);
Q_ASSERT(pos < MAX_PATH + 1);
kde4prefixUtf16[bs1] = '/';
kde4prefixUtf16[bs1+1] = 0;
}
// can't use QCoreApplication::applicationDirPath() because sometimes we
// don't have an instantiated QCoreApplication
QString getKde4Prefix()
{
#ifdef _WIN32_WCE
if (kde4prefixInitialized)
return QString::fromUtf16((ushort*) kde4prefixUtf16);
QDir kde4prefixDir(QString::fromUtf16((ushort*) STATIC_INSTALL_PATH));
if (kde4prefixDir.exists()){
wcscpy(kde4prefixUtf16, STATIC_INSTALL_PATH);
kde4prefixUtf16[wcslen(kde4prefixUtf16)] = 0;
kde4prefixInitialized = true;
return QString::fromUtf16((ushort*) kde4prefixUtf16);
} else {
bool ok;
QString retval = getWin32RegistryValue(HKEY_LOCAL_MACHINE, "Software\\kde", "KDEDIRS", &ok);
if (!ok){
return QString();
} else {
retval = QDir::fromNativeSeparators(retval);
wcscpy(kde4prefixUtf16, retval.utf16());
kde4prefixUtf16[wcslen(kde4prefixUtf16)] = 0;
kde4prefixInitialized = true;
return retval;
}
}
#else
// we can get called after DLL_PROCESS_DETACH!
return kde4Prefix ? *kde4Prefix : QString::fromUtf16((ushort*) kde4prefixUtf16);
#endif
}
#ifndef KDELIBS_STATIC_LIBS
/**
* The dll entry point - get the instance handle for GetModuleFleNameW
* Maybe also some special initialization / cleanup can be done here
**/
extern "C"
#ifndef _WIN32_WCE
BOOL WINAPI DllMain ( HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)
#else
BOOL WINAPI DllMain ( HANDLE hinstDLL,DWORD fdwReason,LPVOID lpReserved)
#endif
{
switch ( fdwReason ) {
case DLL_PROCESS_ATTACH:
kdecoreDllInstance = hinstDLL;
initKde4prefixUtf16();
kde4Prefix = new QString( QString::fromUtf16((ushort*) kde4prefixUtf16) );
break;
case DLL_PROCESS_DETACH:
/* msdn:
When handling DLL_PROCESS_DETACH, a DLL should free resources such
as heap memory only if the DLL is being unloaded dynamically (the
lpReserved parameter is NULL). If the process is terminating (the
lpvReserved parameter is non-NULL), all threads in the process except
the current thread either have exited already or have been explicitly
terminated by a call to the ExitProcess function, which might leave
some process resources such as heaps in an inconsistent state. In this
case, it is not safe for the DLL to clean up the resources. Instead,
the DLL should allow the operating system to reclaim the memory.
*/
if( lpReserved == NULL )
delete kde4Prefix;
kde4Prefix = 0;
break;
default:
break;
}
return true;
}
#endif
/**
\return a value from MS Windows native registry.
@param key is usually one of HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE
constants defined in WinReg.h.
@param subKey is a registry subkey defined as a path to a registry folder, eg.
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
('\' delimiter must be used)
@param item is an item inside subKey or "" if default folder's value should be returned
@param ok if not null, will be set to true on success and false on failure
*/
QString getWin32RegistryValue ( HKEY key, const QString& subKey, const QString& item, bool *ok )
{
#define FAILURE \
{ if (ok) \
*ok = false; \
return QString(); }
if ( subKey.isEmpty() )
FAILURE;
HKEY hKey;
TCHAR *lszValue;
DWORD dwType=REG_SZ;
DWORD dwSize;
if ( ERROR_SUCCESS!=RegOpenKeyExW ( key, WIN32_CAST_CHAR subKey.utf16(), 0, KEY_READ, &hKey ) )
FAILURE;
if ( ERROR_SUCCESS!=RegQueryValueExW ( hKey, WIN32_CAST_CHAR item.utf16(), NULL, NULL, NULL, &dwSize ) )
FAILURE;
lszValue = new TCHAR[dwSize];
if ( ERROR_SUCCESS!=RegQueryValueExW ( hKey, WIN32_CAST_CHAR item.utf16(), NULL, &dwType, ( LPBYTE ) lszValue, &dwSize ) ) {
delete [] lszValue;
FAILURE;
}
RegCloseKey ( hKey );
QString res = QString::fromUtf16 ( ( const ushort* ) lszValue );
delete [] lszValue;
if (ok)
*ok = true;
return res;
}
bool showWin32FilePropertyDialog ( const QString& fileName )
{
QString path_ = QDir::convertSeparators ( QFileInfo ( fileName ).absoluteFilePath() );
#ifndef _WIN32_WCE
SHELLEXECUTEINFOW execInfo;
#else
SHELLEXECUTEINFO execInfo;
#endif
memset ( &execInfo,0,sizeof ( execInfo ) );
execInfo.cbSize = sizeof ( execInfo );
#ifndef _WIN32_WCE
execInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
#else
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
#endif
const QString verb ( QLatin1String ( "properties" ) );
execInfo.lpVerb = WIN32_CAST_CHAR verb.utf16();
execInfo.lpFile = WIN32_CAST_CHAR path_.utf16();
#ifndef _WIN32_WCE
return ShellExecuteExW ( &execInfo );
#else
return ShellExecuteEx ( &execInfo );
//There is no native file property dialog in wince
// return false;
#endif
}
// note: QLocale().name().left(2).toLatin1() returns the same
QByteArray getWin32LocaleName()
{
bool ok;
QString localeNumber = getWin32RegistryValue ( HKEY_CURRENT_USER,
QLatin1String("Control Panel\\International"),
QLatin1String("Locale"), &ok );
if ( !ok )
return QByteArray();
QString localeName = getWin32RegistryValue ( HKEY_LOCAL_MACHINE,
QLatin1String("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout\\DosKeybCodes"),
localeNumber, &ok );
if ( !ok )
return QByteArray();
return localeName.toLatin1();
}
/**
\return a value from MS Windows native registry for shell folder \a folder.
*/
QString getWin32ShellFoldersPath ( const QString& folder )
{
return getWin32RegistryValue ( HKEY_CURRENT_USER,
QLatin1String("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"),
folder );
}
/**
kde and qt debug message printer using windows debug message port
*/
static void kMessageOutputDebugString(QtMsgType type, const char *msg)
{
int BUFSIZE=4096;
char *buf = new char[BUFSIZE];
switch (type) {
case QtDebugMsg:
strlcpy(buf,"Debug:",BUFSIZE);
strlcat(buf,msg,BUFSIZE);
break;
case QtWarningMsg:
strlcpy(buf,"Warning:",BUFSIZE);
strlcat(buf,msg,BUFSIZE);
break;
case QtCriticalMsg:
strlcpy(buf,"Critical:",BUFSIZE);
strlcat(buf,msg,BUFSIZE);
break;
case QtFatalMsg:
strlcpy(buf,"Fatal:",BUFSIZE);
strlcat(buf,msg,BUFSIZE);
//abort();
break;
}
strlcat(buf,"\n",BUFSIZE);
- OutputDebugStringW( (WCHAR*)QString::fromAscii(buf).utf16());
+ OutputDebugStringW( (WCHAR*)QString::fromLatin1(buf).utf16());
delete[] buf;
}
/**
kde and qt debug message printer using FILE pointer based output
*/
static void kMessageOutputFileIO(QtMsgType type, const char *msg)
{
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s\n", msg);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s\n", msg);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s\n", msg);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s\n", msg);
//abort();
}
}
/**
try to attach to the parents console
\return true if console has been attached, false otherwise
*/
typedef BOOL (WINAPI*attachConsolePtr)(DWORD dwProcessId);
static attachConsolePtr attachConsole = 0;
static bool attachConsoleResolved = false;
static bool attachToConsole()
{
if(!attachConsoleResolved) {
attachConsoleResolved = true;
attachConsole = (attachConsolePtr)QLibrary::resolve(QLatin1String("kernel32"), "AttachConsole");
}
return attachConsole ? attachConsole(~0U) != 0 : false;
}
/**
redirect stdout, stderr and
cout, wcout, cin, wcin, wcerr, cerr, wclog and clog to console
*/
static void redirectToConsole()
{
//FIXME: for wince we cannot set stdio buffers
#ifndef _WIN32_WCE
int hCrt;
FILE *hf;
int i;
hCrt = _open_osfhandle((intptr_t) GetStdHandle(STD_INPUT_HANDLE),_O_TEXT);
if(hCrt != -1) {
hf = _fdopen( hCrt, "r" );
*stdin = *hf;
i = setvbuf( stdin, NULL, _IONBF, 0 );
}
hCrt = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE),_O_TEXT);
if(hCrt != -1) {
hf = _fdopen( hCrt, "w" );
*stdout = *hf;
i = setvbuf( stdout, NULL, _IONBF, 0 );
}
hCrt = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE),_O_TEXT);
if(hCrt != -1) {
hf = _fdopen( hCrt, "w" );
*stderr = *hf;
i = setvbuf( stderr, NULL, _IONBF, 0 );
}
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
// point to console as well
ios::sync_with_stdio();
#endif
}
#include <streambuf>
/**
ios related debug message printer for win32
*/
class debug_streambuf: public std::streambuf
{
public:
debug_streambuf(const char *prefix)
{
strcpy(buf,prefix);
index = rindex = strlen(buf);
}
protected:
virtual int overflow(int c = EOF)
{
if (c != EOF)
{
char cc = traits_type::to_char_type(c);
// @TODO: buffer size checking
buf[index++] = cc;
if (cc == '\n')
{
buf[index] = '\0';
- OutputDebugStringW((WCHAR*)QString::fromAscii(buf).utf16());
+ OutputDebugStringW((WCHAR*)QString::fromLatin1(buf).utf16());
index = rindex;
}
}
return traits_type::not_eof(c);
}
private:
char buf[4096];
int index, rindex;
};
/**
retrieve type of win32 subsystem from the executable header
\return type of win32 subsystem - the subsystem types are defined at http://msdn.microsoft.com/en-us/library/ms680339(VS.85).aspx
*/
static int subSystem()
{
#ifdef _WIN32_WCE
// there is only one subsystem on Windows CE
return IMAGE_SUBSYSTEM_WINDOWS_CE_GUI;
#else
static int subSystem = -1;
if (subSystem > -1)
return subSystem;
// get base address of memory mapped executable
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS) ((char *)dosHeader + dosHeader->e_lfanew);
if (ntHeader->Signature != 0x00004550)
{
subSystem = IMAGE_SUBSYSTEM_UNKNOWN;
return subSystem;
}
subSystem = ntHeader->OptionalHeader.Subsystem;
return subSystem;
#endif
}
/**
win32 debug and console output handling
source type of output
1. kde/qt debug system - kDebug(), kWarning(), kFatal(), kError(), qDebug(), qWarning(), qFatal()
2. ios - cout, wcout, wcerr, cerr, wclog and clog
3. FILE * - stdout,stderr
application console ------------------ output -----------------
type available qt/kde-debug ios FILE *
cui yes console console console
cui no win32debug win32debug no output[1]
gui yes win32debug console console
gui no win32debug win32debug win32debug
win-ce no win32debug win32debug win32debug
[1]no redirect solution for FILE * based output yet
TODO: report events to the windows event log system
http://msdn.microsoft.com/en-us/library/aa363680(VS.85).aspx
*/
/**
setup up debug output
*/
static class kMessageOutputInstaller {
public:
kMessageOutputInstaller() : stdoutBuffer("stdout:"), stderrBuffer("stderr:"), oldStdoutBuffer(0), oldStderrBuffer(0)
{
if (subSystem() == IMAGE_SUBSYSTEM_WINDOWS_CUI) {
if (attachToConsole()) {
// setup kde and qt level
qInstallMsgHandler(kMessageOutputFileIO);
// redirect ios and file io to console
redirectToConsole();
}
else {
// setup kde and qt level
qInstallMsgHandler(kMessageOutputDebugString);
// redirect ios to debug message port
oldStdoutBuffer = std::cout.rdbuf(&stdoutBuffer);
oldStderrBuffer = std::cerr.rdbuf(&stderrBuffer);
}
}
else if (subSystem() == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
// setup kde and qt level
qInstallMsgHandler(kMessageOutputDebugString);
// try to get a console
if (attachToConsole()) {
redirectToConsole();
}
else {
// redirect ios to debug message port
oldStdoutBuffer = std::cout.rdbuf(&stdoutBuffer);
oldStderrBuffer = std::cerr.rdbuf(&stderrBuffer);
// TODO: redirect FILE * level to console, no idea how to do yet
}
} else if (subSystem() == IMAGE_SUBSYSTEM_WINDOWS_CE_GUI) {
// do not try to get a console on WinCE systems
qInstallMsgHandler(kMessageOutputDebugString);
oldStdoutBuffer = std::cout.rdbuf(&stdoutBuffer);
oldStderrBuffer = std::cerr.rdbuf(&stderrBuffer);
}
else
qWarning("unknown subsystem %d detected, could not setup qt message handler",subSystem());
}
~kMessageOutputInstaller()
{
if (oldStdoutBuffer)
std::cout.rdbuf(oldStdoutBuffer);
if (oldStderrBuffer)
std::cerr.rdbuf(oldStderrBuffer);
}
private:
debug_streambuf stdoutBuffer;
debug_streambuf stderrBuffer;
std::streambuf* oldStdoutBuffer;
std::streambuf* oldStderrBuffer;
} kMessageOutputInstallerInstance;
bool isExecutable(const QString &file)
{
return ( file.endsWith( QLatin1String( ".exe" ) ) ||
file.endsWith( QLatin1String( ".com" ) ) ||
file.endsWith( QLatin1String( ".bat" ) ) ||
file.endsWith( QLatin1String( ".sln" ) ) ||
file.endsWith( QLatin1String( ".lnk" ) ) );
}
#endif // Q_OS_WIN
diff --git a/kdecore/kernel/kstandarddirs.cpp b/kdecore/kernel/kstandarddirs.cpp
index 8e9ced4ef1..d09651d6a1 100644
--- a/kdecore/kernel/kstandarddirs.cpp
+++ b/kdecore/kernel/kstandarddirs.cpp
@@ -1,2181 +1,2181 @@
/* This file is part of the KDE libraries
Copyright (C) 1999 Sirtaj Singh Kang <taj@kde.org>
Copyright (C) 1999,2007 Stephan Kulow <coolo@kde.org>
Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
Copyright (C) 2009 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 version 2 as published by the Free Software Foundation.
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.
*/
/*
* Author: Stephan Kulow <coolo@kde.org> and Sirtaj Singh Kang <taj@kde.org>
* Generated: Thu Mar 5 16:05:28 EST 1998
*/
#include "kstandarddirs.h"
#include "kconfig.h"
#include "kconfiggroup.h"
#include "kdebug.h"
#include "kshell.h"
#include "kuser.h"
#include "kde_file.h"
#include "kkernel_win.h"
#include "kkernel_mac.h"
#include "klocalizedstring.h"
#include <kservice_export.h>
#include <config-prefix.h>
#include <config-kstandarddirs.h>
#include <config-kernel.h>
#include <kdefakes.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#ifdef Q_OS_WIN
#include <windows.h>
#ifdef _WIN32_WCE
#include <basetyps.h>
#endif
#ifdef Q_OS_WIN64
// FIXME: did not find a reliable way to fix with kdewin mingw header
#define interface struct
#endif
#include <shlobj.h>
#include <QtCore/QVarLengthArray>
#endif
#include <QtCore/QMutex>
#include <QtCore/QRegExp>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
#include <QCoreApplication>
class KStandardDirs::KStandardDirsPrivate
{
public:
KStandardDirsPrivate(KStandardDirs* qq)
: m_restrictionsActive(false),
m_checkRestrictions(true),
m_cacheMutex(QMutex::Recursive), // resourceDirs is recursive
q(qq)
{ }
bool hasDataRestrictions(const QString &relPath) const;
QStringList resourceDirs(const char* type, const QString& subdirForRestrictions);
bool m_restrictionsActive : 1;
bool m_checkRestrictions : 1;
QMap<QByteArray, bool> m_restrictions;
QStringList xdgdata_prefixes;
QStringList xdgconf_prefixes;
QStringList m_prefixes;
// Directory dictionaries
QMap<QByteArray, QStringList> m_absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global
QMap<QByteArray, QStringList> m_relatives; // Same with relative paths
// The search path is "all relative paths" < "all absolute paths", from most priority to least priority.
// Caches (protected by mutex in const methods, cf ctor docu)
QMap<QByteArray, QStringList> m_dircache;
QMap<QByteArray, QString> m_savelocations;
QMutex m_cacheMutex;
KStandardDirs* q;
};
/* If you add a new resource type here, make sure to
* 1) regenerate using "kdesdk/scripts/generate_string_table.pl types < tmpfile" with the data below
* in tmpfile (no empty line at the beginning or at the end!)
* 2) update the KStandardDirs class documentation
* 3) update the list in kde-config.cpp
* 4) update the installPath() function below
data
.
html
doc/HTML
icon
icons
config
config
pixmap
pixmaps
sound
sounds
locale
locale
services
kde5/services
servicetypes
kde5/servicetypes
wallpaper
wallpapers
templates
templates
exe
bin
module
%lib/plugins/kf5
qtplugins
%lib/plugins
kcfg
config.kcfg
emoticons
emoticons
xdgdata
.
xdgdata-apps
applications
xdgdata-icon
icons
xdgdata-pixmap
pixmaps
xdgdata-dirs
desktop-directories
xdgdata-mime
mime
xdgconf
.
xdgconf-menu
menus
xdgconf-autostart
autostart
*/
static const char types_string[] =
"data\0"
".\0"
"html\0"
"doc/HTML\0"
"icon\0"
"icons\0"
"config\0"
"pixmap\0"
"pixmaps\0"
"sound\0"
"sounds\0"
"locale\0"
"services\0"
"kde5/services\0"
"servicetypes\0"
"kde5/servicetypes\0"
"wallpaper\0"
"wallpapers\0"
"templates\0"
"exe\0"
"bin\0"
"module\0"
"%lib/kde5\0"
"qtplugins\0"
"%lib/kde5/plugins\0"
"kcfg\0"
"config.kcfg\0"
"emoticons\0"
"xdgdata\0"
"xdgdata-apps\0"
"applications\0"
"xdgdata-icon\0"
"xdgdata-pixmap\0"
"xdgdata-dirs\0"
"desktop-directories\0"
"xdgdata-mime\0"
"mime\0"
"xdgconf\0"
"xdgconf-menu\0"
"menus\0"
"xdgconf-autostart\0"
"autostart\0"
"\0";
static const int types_indices[] = {
0, 5, 7, 12, 21, 26, 32, 32,
39, 46, 54, 60, 67, 67, 74, 83,
97, 110, 128, 138, 149, 149, 159, 163,
167, 174, 184, 194, 212, 217, 229, 229,
239, 5, 247, 260, 273, 26, 286, 46,
301, 314, 334, 347, 352, 5, 360, 373,
379, 397, -1
};
static void tokenize(QStringList& token, const QString& str,
const QString& delim);
enum BasePrefix { XdgConf, XdgData, KdePrefixes };
static BasePrefix basePrefixForResource(const char* type)
{
// KF5: We now use xdgdata_prefixes for every resource in share/*,
// i.e. everything except exe, lib, config and xdgconf...
// TODO: exe: replaced with $PATH
// lib: unused as is, right?
// module: based on lib, but mostly replaced with QT_PLUGIN_PATH (+"/kf5")
const QByteArray typeBa(type);
if (typeBa.startsWith("xdgconf") || typeBa == "config") {
return XdgConf;
} else if (typeBa == "exe" || typeBa == "lib") {
return KdePrefixes;
} else { // was: if (typeBa.startsWith("xdgdata") || typeBa == "data")
return XdgData;
}
}
#ifdef Q_OS_WIN
QString getKde4Prefix();
#endif
static QString relativeInstallPath(const char *type)
{
Q_ASSERT(type != NULL);
switch (type[0]) {
case 'c':
if (strcmp("config", type) == 0)
return QFile::decodeName(CONFIG_INSTALL_DIR "/");
break;
case 'k':
if (strcmp("kcfg", type) == 0)
return QFile::decodeName(KCFG_INSTALL_DIR "/");
if (strcmp("kdedir", type) == 0)
return QString::fromLatin1(""); // not null!
break;
case 'd':
if (strcmp("data", type) == 0)
return QFile::decodeName(DATA_INSTALL_DIR "/");
break;
case 'e':
if (strcmp("exe", type) == 0)
return QFile::decodeName(BIN_INSTALL_DIR "/");
break;
case 'h':
if (strcmp("html", type) == 0)
return QFile::decodeName(HTML_INSTALL_DIR "/");
break;
case 'i':
if (strcmp("icon", type) == 0)
return QFile::decodeName(ICON_INSTALL_DIR "/");
if (strcmp("include", type) == 0)
return QFile::decodeName(INCLUDE_INSTALL_DIR "/");
break;
case 'l':
if (strcmp("lib", type) == 0)
return QFile::decodeName(LIB_INSTALL_DIR "/");
if (strcmp("libexec", type) == 0)
return QFile::decodeName("lib" KDELIBSUFF "/kde5/libexec/");
if (strcmp("locale", type) == 0)
return QFile::decodeName(LOCALE_INSTALL_DIR "/");
break;
case 'm':
if (strcmp("module", type) == 0)
return QFile::decodeName(PLUGIN_INSTALL_DIR "/");
break;
case 'q':
if (strcmp("qtplugins", type) == 0)
return QFile::decodeName(PLUGIN_INSTALL_DIR "/plugins/");
break;
case 's':
if (strcmp("services", type) == 0)
return QFile::decodeName(SERVICES_INSTALL_DIR "/");
if (strcmp("servicetypes", type) == 0)
return QFile::decodeName(SERVICETYPES_INSTALL_DIR "/");
if (strcmp("sound", type) == 0)
return QFile::decodeName(SOUND_INSTALL_DIR "/");
break;
case 't':
if (strcmp("templates", type) == 0)
return QFile::decodeName(TEMPLATES_INSTALL_DIR "/");
break;
case 'w':
if (strcmp("wallpaper", type) == 0)
return QFile::decodeName(WALLPAPER_INSTALL_DIR "/");
break;
case 'x':
if (strcmp("xdgconf-menu", type) == 0)
return QFile::decodeName(SYSCONF_INSTALL_DIR "/xdg/menus/");
if (strcmp("xdgdata-apps", type) == 0)
return QFile::decodeName(XDG_APPS_INSTALL_DIR "/");
if (strcmp("xdgdata-dirs", type) == 0)
return QFile::decodeName(XDG_DIRECTORY_INSTALL_DIR "/");
break;
}
return QString();
}
QString KStandardDirs::installPath(const char *type)
{
const QString relPath = relativeInstallPath(type);
if (relPath.isNull()) {
return QString();
} else if (QDir::isAbsolutePath(relPath)) {
return relPath;
} else {
#ifdef Q_OS_WIN
return getKde4Prefix() + relPath;
#else
return QFile::decodeName(CMAKE_INSTALL_PREFIX "/") + relPath;
#endif
}
}
KStandardDirs::KStandardDirs()
: d(new KStandardDirsPrivate(this))
{
addKDEDefaults();
}
KStandardDirs::~KStandardDirs()
{
delete d;
}
bool KStandardDirs::isRestrictedResource(const char *type, const QString& relPath) const
{
if (!d->m_restrictionsActive)
return false;
if (d->m_restrictions.value(type, false))
return true;
if (strcmp(type, "data")==0 && d->hasDataRestrictions(relPath))
return true;
return false;
}
bool KStandardDirs::KStandardDirsPrivate::hasDataRestrictions(const QString &relPath) const
{
QString key;
const int i = relPath.indexOf(QLatin1Char('/'));
if (i != -1)
key = QString::fromLatin1("data_") + relPath.left(i);
else
key = QString::fromLatin1("data_") + relPath;
return m_restrictions.value(key.toLatin1(), false);
}
QStringList KStandardDirs::allTypes() const
{
QStringList list;
for (int i = 0; types_indices[i] != -1; i += 2)
list.append(QLatin1String(types_string + types_indices[i]));
// Those are added manually by addKDEDefaults
list.append(QString::fromLatin1("lib"));
//list.append(QString::fromLatin1("home")); // undocumented on purpose, said Waldo in r113855.
// Those are handled by resourceDirs() itself
list.append(QString::fromLatin1("socket"));
list.append(QString::fromLatin1("tmp"));
list.append(QString::fromLatin1("cache"));
// Those are handled by installPath()
list.append(QString::fromLatin1("include"));
// If you add anything here, make sure kde-config.cpp has a description for it.
return list;
}
static void priorityAdd(QStringList &prefixes, const QString& dir, bool priority)
{
if (priority && !prefixes.isEmpty())
{
// Add in front but behind the most-local prefix
QStringList::iterator it = prefixes.begin();
++it;
prefixes.insert(it, dir);
}
else
{
prefixes.append(dir);
}
}
void KStandardDirs::addPrefix( const QString& _dir )
{
addPrefix(_dir, false);
}
void KStandardDirs::addPrefix( const QString& _dir, bool priority )
{
if (_dir.isEmpty())
return;
QString dir = _dir;
if (dir.at(dir.length() - 1) != QLatin1Char('/'))
dir += QLatin1Char('/');
if (!d->m_prefixes.contains(dir)) {
priorityAdd(d->m_prefixes, dir, priority);
d->m_dircache.clear();
}
}
void KStandardDirs::addXdgConfigPrefix( const QString& _dir )
{
addXdgConfigPrefix(_dir, false);
}
void KStandardDirs::addXdgConfigPrefix( const QString& _dir, bool priority )
{
if (_dir.isEmpty())
return;
QString dir = _dir;
if (dir.at(dir.length() - 1) != QLatin1Char('/'))
dir += QLatin1Char('/');
if (!d->xdgconf_prefixes.contains(dir)) {
priorityAdd(d->xdgconf_prefixes, dir, priority);
d->m_dircache.clear();
}
}
void KStandardDirs::addXdgDataPrefix( const QString& _dir )
{
addXdgDataPrefix(_dir, false);
}
void KStandardDirs::addXdgDataPrefix( const QString& _dir, bool priority )
{
if (_dir.isEmpty())
return;
QString dir = _dir;
if (dir.at(dir.length() - 1) != QLatin1Char('/'))
dir += QLatin1Char('/');
if (!d->xdgdata_prefixes.contains(dir)) {
priorityAdd(d->xdgdata_prefixes, dir, priority);
d->m_dircache.clear();
}
}
#ifndef KDE_NO_DEPRECATED
QString KStandardDirs::kfsstnd_prefixes()
{
return d->m_prefixes.join(QString(QLatin1Char(':')));
}
bool KStandardDirs::addResourceType( const char *type,
const QString& relativename,
bool priority )
{
return addResourceType( type, 0, relativename, priority);
}
#endif
bool KStandardDirs::addResourceType( const char *type,
const char *basetype,
const QString& relativename,
bool priority )
{
if (relativename.isEmpty())
return false;
QString copy = relativename;
if (basetype) {
copy = QLatin1Char('%') + QString::fromLatin1(basetype) + QLatin1Char('/');
if (relativename != QLatin1String("/"))
copy += relativename;
}
if (!copy.endsWith(QLatin1Char('/')))
copy += QLatin1Char('/');
QByteArray typeBa = type;
QStringList& rels = d->m_relatives[typeBa]; // find or insert
if (!rels.contains(copy)) {
if (priority)
rels.prepend(copy);
else
rels.append(copy);
// clean the caches
d->m_dircache.remove(typeBa);
d->m_savelocations.remove(typeBa);
return true;
}
return false;
}
bool KStandardDirs::addResourceDir( const char *type,
const QString& absdir,
bool priority)
{
if (absdir.isEmpty() || !type)
return false;
// find or insert entry in the map
QString copy = absdir;
if (copy.at(copy.length() - 1) != QLatin1Char('/'))
copy += QLatin1Char('/');
QByteArray typeBa = type;
QStringList &paths = d->m_absolutes[typeBa];
if (!paths.contains(copy)) {
if (priority)
paths.prepend(copy);
else
paths.append(copy);
// clean the caches
d->m_dircache.remove(typeBa);
d->m_savelocations.remove(typeBa);
return true;
}
return false;
}
QString KStandardDirs::findResource( const char *type,
const QString& _filename ) const
{
if (!QDir::isRelativePath(_filename)) {
// absolute dirs are absolute dirs, right? :-/
return KLocalizedString::localizedFilePath(_filename); // -- almost.
}
#if 0
kDebug(180) << "Find resource: " << type;
for (QStringList::ConstIterator pit = m_prefixes.begin();
pit != m_prefixes.end();
++pit)
{
kDebug(180) << "Prefix: " << *pit;
}
#endif
QString filename(_filename);
#ifdef Q_OS_WIN
if(strcmp(type, "exe") == 0) {
if(!filename.endsWith(QLatin1String(".exe")))
filename += QLatin1String(".exe");
}
#endif
const QString dir = findResourceDir(type, filename);
if (dir.isEmpty())
return dir;
else
return KLocalizedString::localizedFilePath(dir + filename);
}
#ifndef KDE_NO_DEPRECATED
static quint32 updateHash(const QString &file, quint32 hash)
{
KDE_struct_stat buff;
if ((KDE::access(file, R_OK) == 0) && (KDE::stat(file, &buff) == 0) && (S_ISREG(buff.st_mode))) {
hash = hash + static_cast<quint32>(buff.st_ctime);
}
return hash;
}
quint32 KStandardDirs::calcResourceHash( const char *type,
const QString& filename,
SearchOptions options ) const
{
quint32 hash = 0;
if (!QDir::isRelativePath(filename))
{
// absolute dirs are absolute dirs, right? :-/
return updateHash(filename, hash);
}
QStringList candidates = d->resourceDirs(type, filename);
foreach ( const QString& candidate, candidates )
{
hash = updateHash(candidate + filename, hash);
if ( !( options & Recursive ) && hash ) {
return hash;
}
}
return hash;
}
#endif
QStringList KStandardDirs::findDirs( const char *type,
const QString& reldir ) const
{
QDir testdir;
QStringList list;
if (!QDir::isRelativePath(reldir))
{
testdir.setPath(reldir);
if (testdir.exists())
{
if (reldir.endsWith(QLatin1Char('/')))
list.append(reldir);
else
list.append(reldir+QLatin1Char('/'));
}
return list;
}
const QStringList candidates = d->resourceDirs(type, reldir);
for (QStringList::ConstIterator it = candidates.begin();
it != candidates.end(); ++it) {
testdir.setPath(*it + reldir);
if (testdir.exists())
list.append(testdir.absolutePath() + QLatin1Char('/'));
}
return list;
}
QString KStandardDirs::findResourceDir( const char *type,
const QString& _filename) const
{
#ifndef NDEBUG
if (_filename.isEmpty()) {
kWarning() << "filename for type " << type << " in KStandardDirs::findResourceDir is not supposed to be empty!!";
return QString();
}
#endif
QString filename(_filename);
#ifdef Q_OS_WIN
if(strcmp(type, "exe") == 0) {
if(!filename.endsWith(QLatin1String(".exe")))
filename += QLatin1String(".exe");
}
#endif
const QStringList candidates = d->resourceDirs(type, filename);
for (QStringList::ConstIterator it = candidates.begin();
it != candidates.end(); ++it) {
if (exists(*it + filename)) {
return *it;
}
}
#ifndef NDEBUG
if(false && strcmp(type, "locale"))
kDebug(180) << "KStdDirs::findResDir(): can't find \"" << filename << "\" in type \"" << type << "\".";
#endif
return QString();
}
bool KStandardDirs::exists(const QString &fullPath)
{
#ifdef Q_OS_WIN
// access() and stat() give a stupid error message to the user
// if the path is not accessible at all (e.g. no disk in A:/ and
// we do stat("A:/.directory")
if (fullPath.endsWith(QLatin1Char('/')))
return QDir(fullPath).exists();
return QFileInfo(fullPath).exists();
#else
KDE_struct_stat buff;
QByteArray cFullPath = QFile::encodeName(fullPath);
if (access(cFullPath, R_OK) == 0 && KDE_stat( cFullPath, &buff ) == 0) {
if (!fullPath.endsWith(QLatin1Char('/'))) {
if (S_ISREG( buff.st_mode ))
return true;
} else
if (S_ISDIR( buff.st_mode ))
return true;
}
return false;
#endif
}
static void lookupDirectory(const QString& path, const QString &relPart,
const QRegExp &regexp,
QStringList& list,
QStringList& relList,
bool recursive, bool unique)
{
const QString pattern = regexp.pattern();
if (recursive || pattern.contains(QLatin1Char('?')) || pattern.contains(QLatin1Char('*')))
{
if (path.isEmpty()) //for sanity
return;
#ifdef Q_OS_WIN
QString path_ = path + QLatin1String( "*.*" );
WIN32_FIND_DATA findData;
HANDLE hFile = FindFirstFile( (LPWSTR)path_.utf16(), &findData );
if( hFile == INVALID_HANDLE_VALUE )
return;
do {
const int len = wcslen( findData.cFileName );
if (!( findData.cFileName[0] == '.' &&
findData.cFileName[1] == '\0' ) &&
!( findData.cFileName[0] == '.' &&
findData.cFileName[1] == '.' &&
findData.cFileName[2] == '\0' ) &&
( findData.cFileName[len-1] != '~' ) ) {
QString fn = QString::fromUtf16( (const unsigned short*)findData.cFileName );
if (!recursive && !regexp.exactMatch(fn))
continue; // No match
QString pathfn = path + fn;
bool bIsDir = ( ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY );
if ( recursive ) {
if ( bIsDir ) {
lookupDirectory(pathfn + QLatin1Char('/'),
relPart + fn + QLatin1Char('/'),
regexp, list, relList, recursive, unique);
}
if (!regexp.exactMatch(fn))
continue; // No match
}
if ( !bIsDir )
{
if ( !unique || !relList.contains(relPart + fn) )
{
list.append( pathfn );
relList.append( relPart + fn );
}
}
}
} while( FindNextFile( hFile, &findData ) != 0 );
FindClose( hFile );
#else
// We look for a set of files.
DIR *dp = opendir( QFile::encodeName(path));
if (!dp)
return;
assert(path.endsWith(QLatin1Char('/')));
struct dirent *ep;
while( ( ep = readdir( dp ) ) != 0L )
{
QString fn( QFile::decodeName(ep->d_name));
if (fn == QString::fromLatin1(".") || fn == QString::fromLatin1("..") || fn.at(fn.length() - 1) == QLatin1Char('~'))
continue;
if (!recursive && !regexp.exactMatch(fn))
continue; // No match
bool isDir;
bool isReg;
QString pathfn = path + fn;
#if HAVE_DIRENT_D_TYPE
isDir = ep->d_type == DT_DIR;
isReg = ep->d_type == DT_REG;
if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
#endif
{
KDE_struct_stat buff;
if ( KDE::stat( pathfn, &buff ) != 0 ) {
kDebug(180) << "Error stat'ing " << pathfn << " : " << perror;
continue; // Couldn't stat (e.g. no read permissions)
}
isReg = S_ISREG (buff.st_mode);
isDir = S_ISDIR (buff.st_mode);
}
if ( recursive ) {
if ( isDir ) {
lookupDirectory(pathfn + QLatin1Char('/'), relPart + fn + QLatin1Char('/'), regexp, list, relList, recursive, unique);
}
if (!regexp.exactMatch(fn))
continue; // No match
}
if ( isReg )
{
if (!unique || !relList.contains(relPart + fn))
{
list.append( pathfn );
relList.append( relPart + fn );
}
}
}
closedir( dp );
#endif
}
else
{
// We look for a single file.
QString fn = pattern;
QString pathfn = path + fn;
KDE_struct_stat buff;
if ( KDE::stat( pathfn, &buff ) != 0 )
return; // File not found
if ( S_ISREG( buff.st_mode))
{
if (!unique || !relList.contains(relPart + fn))
{
list.append( pathfn );
relList.append( relPart + fn );
}
}
}
}
static void lookupPrefix(const QString& prefix, const QString& relpath,
const QString& relPart,
const QRegExp &regexp,
QStringList& list,
QStringList& relList,
bool recursive, bool unique)
{
if (relpath.isEmpty()) {
if (recursive)
Q_ASSERT(prefix != QLatin1String("/")); // we don't want to recursively list the whole disk!
lookupDirectory(prefix, relPart, regexp, list,
relList, recursive, unique);
return;
}
QString path;
QString rest;
int slash = relpath.indexOf(QLatin1Char('/'));
if (slash < 0)
rest = relpath.left(relpath.length() - 1);
else {
path = relpath.left(slash);
rest = relpath.mid(slash + 1);
}
if (prefix.isEmpty()) //for sanity
return;
#ifndef Q_OS_WIN
// what does this assert check ?
assert(prefix.endsWith(QLatin1Char('/')));
#endif
if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?'))) {
QRegExp pathExp(path, Qt::CaseSensitive, QRegExp::Wildcard);
#ifdef Q_OS_WIN
QString prefix_ = prefix + QLatin1String( "*.*" );
WIN32_FIND_DATA findData;
HANDLE hFile = FindFirstFile( (LPWSTR)prefix_.utf16(), &findData );
if( hFile == INVALID_HANDLE_VALUE )
return;
do {
const int len = wcslen( findData.cFileName );
if (!( findData.cFileName[0] == '.' &&
findData.cFileName[1] == '\0' ) &&
!( findData.cFileName[0] == '.' &&
findData.cFileName[1] == '.' &&
findData.cFileName[2] == '\0' ) &&
( findData.cFileName[len-1] != '~' ) ) {
const QString fn = QString::fromUtf16( (const unsigned short*)findData.cFileName );
if ( !pathExp.exactMatch(fn) )
continue; // No match
if ( ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY )
lookupPrefix(prefix + fn + QLatin1Char('/'),
rest, relPart + fn + QLatin1Char('/'),
regexp, list, relList, recursive, unique);
}
} while( FindNextFile( hFile, &findData ) != 0 );
FindClose( hFile );
#else
DIR *dp = opendir( QFile::encodeName(prefix) );
if (!dp) {
return;
}
struct dirent *ep;
while( ( ep = readdir( dp ) ) != 0L )
{
QString fn( QFile::decodeName(ep->d_name));
if (fn == QLatin1String(".") || fn == QLatin1String("..") || fn.at(fn.length() - 1) == QLatin1Char('~'))
continue;
if ( !pathExp.exactMatch(fn) )
continue; // No match
QString rfn = relPart+fn;
fn = prefix + fn;
bool isDir;
#if HAVE_DIRENT_D_TYPE
isDir = ep->d_type == DT_DIR;
if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
#endif
{
QString pathfn = path + fn;
KDE_struct_stat buff;
if ( KDE::stat( fn, &buff ) != 0 ) {
kDebug(180) << "Error stat'ing " << fn << " : " << perror;
continue; // Couldn't stat (e.g. no read permissions)
}
isDir = S_ISDIR (buff.st_mode);
}
if ( isDir )
lookupPrefix(fn + QLatin1Char('/'), rest, rfn + QLatin1Char('/'), regexp, list, relList, recursive, unique);
}
closedir( dp );
#endif
} else {
// Don't stat, if the dir doesn't exist we will find out
// when we try to open it.
lookupPrefix(prefix + path + QLatin1Char('/'), rest,
relPart + path + QLatin1Char('/'), regexp, list,
relList, recursive, unique);
}
}
QStringList
KStandardDirs::findAllResources( const char *type,
const QString& filter,
SearchOptions options,
QStringList &relList) const
{
QString filterPath;
QString filterFile;
if ( !filter.isEmpty() )
{
int slash = filter.lastIndexOf(QLatin1Char('/'));
if (slash < 0) {
filterFile = filter;
} else {
filterPath = filter.left(slash + 1);
filterFile = filter.mid(slash + 1);
}
}
QStringList candidates;
if ( !QDir::isRelativePath(filter) ) // absolute path
{
#ifdef Q_OS_WIN
candidates << filterPath.left(3); //e.g. "C:\"
filterPath = filterPath.mid(3);
#else
candidates << QString::fromLatin1("/");
filterPath = filterPath.mid(1);
#endif
}
else
{
candidates = d->resourceDirs(type, filter);
}
if (filterFile.isEmpty()) {
filterFile = QString(QLatin1Char('*'));
}
QRegExp regExp(filterFile, Qt::CaseSensitive, QRegExp::Wildcard);
QStringList list;
foreach ( const QString& candidate, candidates )
{
lookupPrefix(candidate, filterPath, QString(), regExp, list,
relList, options & Recursive, options & NoDuplicates);
}
return list;
}
QStringList
KStandardDirs::findAllResources( const char *type,
const QString& filter,
SearchOptions options ) const
{
QStringList relList;
return findAllResources(type, filter, options, relList);
}
// ####### KDE4: should this be removed, in favor of QDir::canonicalPath()?
// aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist
// and this method is often used with the expectation for it to work
// even if the directory doesn't exist. so ... no, we can't drop this
// yet
QString
KStandardDirs::realPath(const QString &dirname)
{
#ifdef Q_OS_WIN
const QString strRet = realFilePath(dirname);
if (!strRet.endsWith(QLatin1Char('/')))
return strRet + QLatin1Char('/');
return strRet;
#else
if (dirname.isEmpty() || (dirname.size() == 1 && dirname.at(0) == QLatin1Char('/')))
return dirname;
if (dirname.at(0) != QLatin1Char('/')) {
qWarning("realPath called with a relative path '%s', please fix", qPrintable(dirname));
return dirname;
}
char realpath_buffer[MAXPATHLEN + 1];
memset(realpath_buffer, 0, MAXPATHLEN + 1);
/* If the path contains symlinks, get the real name */
if (realpath( QFile::encodeName(dirname).constData(), realpath_buffer) != 0) {
// success, use result from realpath
int len = strlen(realpath_buffer);
realpath_buffer[len] = '/';
realpath_buffer[len+1] = 0;
return QFile::decodeName(realpath_buffer);
}
// Does not exist yet; resolve symlinks in parent dirs then.
// This ensures that once the directory exists, it will still be resolved
// the same way, so that the general rule that KStandardDirs always returns
// canonical paths stays true, and app code can compare paths more easily.
QString dir = dirname;
if (!dir.endsWith(QLatin1Char('/')))
dir += QLatin1Char('/');
QString relative;
while (!KStandardDirs::exists(dir)) {
//qDebug() << "does not exist:" << dir;
const int pos = dir.lastIndexOf(QLatin1Char('/'), -2);
Q_ASSERT(pos >= 0); // what? even "/" doesn't exist?
relative.prepend(dir.mid(pos+1)); // keep "subdir/"
dir = dir.left(pos+1);
Q_ASSERT(dir.endsWith(QLatin1Char('/')));
}
Q_ASSERT(!relative.isEmpty()); // infinite recursion ahead
if (!relative.isEmpty()) {
//qDebug() << "done, resolving" << dir << "and adding" << relative;
dir = realPath(dir) + relative;
}
return dir;
#endif
}
// ####### KDE4: should this be removed, in favor of QFileInfo::canonicalFilePath()?
// aseigo: QDir::canonicalPath returns QString() if the dir doesn't exist
// and this method is often used with the expectation for it to work
// even if the directory doesn't exist. so ... no, we can't drop this
// yet
QString
KStandardDirs::realFilePath(const QString &filename)
{
#ifdef Q_OS_WIN
LPCWSTR lpIn = (LPCWSTR)filename.utf16();
QVarLengthArray<WCHAR, MAX_PATH> buf(MAX_PATH);
DWORD len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL);
if (len > (DWORD)buf.size()) {
buf.resize(len);
len = GetFullPathNameW(lpIn, buf.size(), buf.data(), NULL);
}
if (len == 0)
return QString();
return QString::fromUtf16((const unsigned short*)buf.data()).replace(QLatin1Char('\\'),QLatin1Char('/')).toLower();
#else
char realpath_buffer[MAXPATHLEN + 1];
memset(realpath_buffer, 0, MAXPATHLEN + 1);
/* If the path contains symlinks, get the real name */
if (realpath( QFile::encodeName(filename).constData(), realpath_buffer) != 0) {
// success, use result from realpath
return QFile::decodeName(realpath_buffer);
}
return filename;
#endif
}
QStringList KStandardDirs::resourceDirs(const char *type) const
{
return d->resourceDirs(type, QString());
}
QStringList KStandardDirs::KStandardDirsPrivate::resourceDirs(const char* type, const QString& subdirForRestrictions)
{
QMutexLocker lock(&m_cacheMutex);
const bool dataRestrictionActive = m_restrictionsActive
&& (strcmp(type, "data") == 0)
&& hasDataRestrictions(subdirForRestrictions);
QMap<QByteArray, QStringList>::const_iterator dirCacheIt = m_dircache.constFind(type);
QStringList candidates;
if (dirCacheIt != m_dircache.constEnd() && !dataRestrictionActive) {
//qDebug() << this << "resourceDirs(" << type << "), in cache already";
candidates = *dirCacheIt;
}
else // filling cache
{
//qDebug() << this << "resourceDirs(" << type << "), not in cache";
QDir testdir;
bool restrictionActive = false;
if (m_restrictionsActive) {
if (dataRestrictionActive)
restrictionActive = true;
if (m_restrictions.value("all", false))
restrictionActive = true;
else if (m_restrictions.value(type, false))
restrictionActive = true;
}
const QStringList dirs = m_relatives.value(type);
const QString typeInstallPath = installPath(type); // could be empty
#ifdef Q_OS_WIN
const QString installdir = typeInstallPath.isEmpty() ? QString() : realPath(typeInstallPath).toLower();
const QString installprefix = installPath("kdedir").toLower();
#else
const QString installdir = typeInstallPath.isEmpty() ? QString() : realPath(typeInstallPath);
const QString installprefix = installPath("kdedir");
#endif
if (!dirs.isEmpty())
{
bool local = true;
for (QStringList::ConstIterator it = dirs.constBegin();
it != dirs.constEnd(); ++it)
{
if ((*it).startsWith(QLatin1Char('%'))) {
// grab the "data" from "%data/apps"
const int pos = (*it).indexOf(QLatin1Char('/'));
QString rel = (*it).mid(1, pos - 1);
QString rest = (*it).mid(pos + 1);
const QStringList basedirs = resourceDirs(rel.toUtf8().constData(), subdirForRestrictions);
for (QStringList::ConstIterator it2 = basedirs.begin();
it2 != basedirs.end(); ++it2)
{
#ifdef Q_OS_WIN
const QString path = realPath( *it2 + rest ).toLower();
#else
const QString path = realPath( *it2 + rest );
#endif
testdir.setPath(path);
if ((local || testdir.exists()) && !candidates.contains(path))
candidates.append(path);
local = false;
}
}
}
const QStringList *prefixList = 0;
const BasePrefix basePrefix = basePrefixForResource(type);
if (basePrefix == XdgConf) {
prefixList = &(xdgconf_prefixes);
} else if (basePrefix == XdgData) {
prefixList = &(xdgdata_prefixes);
} else if (basePrefix == KdePrefixes) {
prefixList = &m_prefixes;
}
for (QStringList::ConstIterator pit = prefixList->begin();
pit != prefixList->end();
++pit)
{
if((*pit)!=installprefix||installdir.isEmpty())
{
for (QStringList::ConstIterator it = dirs.constBegin();
it != dirs.constEnd(); ++it)
{
if ((*it).startsWith(QLatin1Char('%')))
continue;
#ifdef Q_OS_WIN
const QString path = realPath( *pit + *it ).toLower();
#else
const QString path = realPath( *pit + *it );
#endif
testdir.setPath(path);
if (local && restrictionActive)
continue;
if ((local || testdir.exists()) && !candidates.contains(path))
candidates.append(path);
}
local = false;
}
else
{
// we have a custom install path, so use this instead of <installprefix>/<relative dir>
testdir.setPath(installdir);
if(testdir.exists() && ! candidates.contains(installdir))
candidates.append(installdir);
}
}
}
// make sure we find the path where it's installed
if (!installdir.isEmpty()) {
bool ok = true;
foreach (const QString &s, candidates) {
if (installdir.startsWith(s)) {
ok = false;
break;
}
}
if (ok)
candidates.append(installdir);
}
const QStringList absDirs = m_absolutes.value(type);
for (QStringList::ConstIterator it = absDirs.constBegin();
it != absDirs.constEnd(); ++it)
{
testdir.setPath(*it);
if (testdir.exists()) {
#ifdef Q_OS_WIN
const QString filename = realPath( *it ).toLower();
#else
const QString filename = realPath( *it );
#endif
if (!candidates.contains(filename)) {
candidates.append(filename);
}
}
}
// Insert result into the cache for next time.
// Exception: data_subdir restrictions are per-subdir, so we can't store such results
if (!dataRestrictionActive) {
//qDebug() << this << "Inserting" << type << candidates << "into dircache";
m_dircache.insert(type, candidates);
}
}
#if 0
kDebug(180) << "found dirs for resource" << type << ":" << candidates;
#endif
return candidates;
}
#ifdef Q_OS_WIN
static QStringList executableExtensions()
{
QStringList ret = QString::fromLocal8Bit(qgetenv("PATHEXT")).split(QLatin1Char(';'));
if (!ret.contains(QLatin1String(".exe"), Qt::CaseInsensitive)) {
// If %PATHEXT% does not contain .exe, it is either empty, malformed, or distorted in ways that we cannot support, anyway.
ret.clear();
ret << QLatin1String(".exe")
<< QLatin1String(".com")
<< QLatin1String(".bat")
<< QLatin1String(".cmd");
}
return ret;
}
#endif
QStringList KStandardDirs::systemPaths( const QString& pstr )
{
QStringList tokens;
QString p = pstr;
if( p.isEmpty() )
{
p = QString::fromLocal8Bit( qgetenv( "PATH" ) );
}
QString delimiters(QLatin1Char(KPATH_SEPARATOR));
delimiters += QLatin1Char('\b');
tokenize( tokens, p, delimiters );
QStringList exePaths;
// split path using : or \b as delimiters
for( int i = 0; i < tokens.count(); i++ )
{
exePaths << KShell::tildeExpand( tokens[ i ] );
}
return exePaths;
}
#ifdef Q_OS_MAC
static QString getBundle( const QString& path, bool ignore )
{
//kDebug(180) << "getBundle(" << path << ", " << ignore << ") called";
QFileInfo info;
QString bundle = path;
bundle += QLatin1String(".app/Contents/MacOS/") + bundle.section(QLatin1Char('/'), -1);
info.setFile( bundle );
FILE *file;
if (file = fopen(info.absoluteFilePath().toUtf8().constData(), "r")) {
fclose(file);
struct stat _stat;
if ((stat(info.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) {
return QString();
}
if ( ignore || (_stat.st_mode & S_IXUSR) ) {
if ( ((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK) ) {
//kDebug(180) << "getBundle(): returning " << bundle;
return bundle;
}
}
}
return QString();
}
#endif
static QString checkExecutable( const QString& path, bool ignoreExecBit )
{
#ifdef Q_OS_MAC
QString bundle = getBundle( path, ignoreExecBit );
if ( !bundle.isEmpty() ) {
//kDebug(180) << "findExe(): returning " << bundle;
return bundle;
}
#endif
QFileInfo info( path );
QFileInfo orig = info;
#if defined(Q_OS_DARWIN) || defined(Q_OS_MAC)
FILE *file;
if (file = fopen(orig.absoluteFilePath().toUtf8().constData(), "r")) {
fclose(file);
struct stat _stat;
if ((stat(orig.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) {
return QString();
}
if ( ignoreExecBit || (_stat.st_mode & S_IXUSR) ) {
if ( ((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK) ) {
orig.makeAbsolute();
return orig.filePath();
}
}
}
return QString();
#else
if( info.exists() && info.isSymLink() )
info = QFileInfo( info.canonicalFilePath() );
if( info.exists() && ( ignoreExecBit || info.isExecutable() ) && info.isFile() ) {
// return absolute path, but without symlinks resolved in order to prevent
// problems with executables that work differently depending on name they are
// run as (for example gunzip)
orig.makeAbsolute();
return orig.filePath();
}
//kDebug(180) << "checkExecutable(): failed, returning empty string";
return QString();
#endif
}
// KDE5 TODO: remove IgnoreExecBit almost unused
QString KStandardDirs::findExe( const QString& appname,
const QString& pstr,
SearchOptions options )
{
//kDebug(180) << "findExe(" << appname << ", pstr, " << ignoreExecBit << ") called";
#ifdef Q_OS_WIN
QStringList executable_extensions = executableExtensions();
if (!executable_extensions.contains(appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep), Qt::CaseInsensitive)) {
QString found_exe;
foreach (const QString& extension, executable_extensions) {
found_exe = findExe(appname + extension, pstr, options);
if (!found_exe.isEmpty()) {
return found_exe;
}
}
return QString();
}
#endif
QFileInfo info;
// absolute or relative path?
if (appname.contains(QDir::separator()))
{
//kDebug(180) << "findExe(): absolute path given";
QString path = checkExecutable(appname, options & IgnoreExecBit);
return path;
}
//kDebug(180) << "findExe(): relative path given";
QString p = installPath("libexec") + appname;
QString result = checkExecutable(p, options & IgnoreExecBit);
if (!result.isEmpty()) {
//kDebug(180) << "findExe(): returning " << result;
return result;
}
//kDebug(180) << "findExe(): checking system paths";
const QStringList exePaths = systemPaths( pstr );
for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it)
{
p = (*it) + QLatin1Char('/');
p += appname;
// Check for executable in this tokenized path
result = checkExecutable(p, options & IgnoreExecBit);
if (!result.isEmpty()) {
//kDebug(180) << "findExe(): returning " << result;
return result;
}
}
// Not found in PATH, look into the KDE-specific bin dir ("exe" resource)
p = installPath("exe");
p += appname;
result = checkExecutable(p, options & IgnoreExecBit);
if (!result.isEmpty()) {
//kDebug(180) << "findExe(): returning " << result;
return result;
}
// If we reach here, the executable wasn't found.
// So return empty string.
//kDebug(180) << "findExe(): failed, nothing matched";
return QString();
}
// TODO: very rarely used. Only known example is kdevelop looking for all qmakes/cmakes in the path.
int KStandardDirs::findAllExe( QStringList& list, const QString& appname,
const QString& pstr, SearchOptions options )
{
#ifdef Q_OS_WIN
QStringList executable_extensions = executableExtensions();
if (!executable_extensions.contains(appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep), Qt::CaseInsensitive)) {
int total = 0;
foreach (const QString& extension, executable_extensions) {
total += findAllExe (list, appname + extension, pstr, options);
}
return total;
}
#endif
QFileInfo info;
QString p;
list.clear();
const QStringList exePaths = systemPaths( pstr );
for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it)
{
p = (*it) + QLatin1Char('/');
p += appname;
#ifdef Q_OS_MAC
QString bundle = getBundle( p, (options & IgnoreExecBit) );
if ( !bundle.isEmpty() ) {
//kDebug(180) << "findExe(): returning " << bundle;
list.append( bundle );
}
#endif
info.setFile( p );
if( info.exists() && ( ( options & IgnoreExecBit ) || info.isExecutable())
&& info.isFile() ) {
list.append( p );
}
}
return list.count();
}
static inline QString equalizePath(QString &str)
{
#ifdef Q_OS_WIN
// filter pathes through QFileInfo to have always
// the same case for drive letters
QFileInfo f(str);
if (f.isAbsolute())
return f.absoluteFilePath();
else
#endif
return str;
}
static void tokenize(QStringList& tokens, const QString& str,
const QString& delim)
{
const int len = str.length();
QString token;
for(int index = 0; index < len; index++) {
if (delim.contains(str[index])) {
tokens.append(equalizePath(token));
token.clear();
} else {
token += str[index];
}
}
if (!token.isEmpty()) {
tokens.append(equalizePath(token));
}
}
#ifndef KDE_NO_DEPRECATED
QString KStandardDirs::kde_default(const char *type)
{
return QString(QLatin1Char('%')) + QString::fromLatin1(type) + QLatin1Char('/');
}
#endif
QString KStandardDirs::saveLocation(const char *type,
const QString& suffix,
bool create) const
{
QMutexLocker lock(&d->m_cacheMutex);
QString path = d->m_savelocations.value(type);
if (path.isEmpty())
{
QStringList dirs = d->m_relatives.value(type);
if (dirs.isEmpty() && (
(strcmp(type, "socket") == 0) ||
(strcmp(type, "tmp") == 0) ||
(strcmp(type, "cache") == 0) ))
{
(void) resourceDirs(type); // Generate socket|tmp|cache resource.
dirs = d->m_relatives.value(type); // Search again.
}
if (!dirs.isEmpty())
{
path = dirs.first();
if (path.startsWith(QLatin1Char('%'))) {
// grab the "data" from "%data/apps"
const int pos = path.indexOf(QLatin1Char('/'));
QString rel = path.mid(1, pos - 1);
QString rest = path.mid(pos + 1);
QString basepath = saveLocation(rel.toUtf8().constData(), QString(), create);
path = basepath + rest;
} else {
if (path == QLatin1String("./")) {
path.clear();
}
// Check for existence of typed directory + suffix
const BasePrefix basePrefix = basePrefixForResource(type);
if (basePrefix == XdgConf) {
path = realPath(localxdgconfdir() + path);
} else if (basePrefix == XdgData) {
path = realPath(localxdgdatadir() + path);
} else {
path = realPath(localkdedir() + path);
}
}
}
else {
dirs = d->m_absolutes.value(type);
if (dirs.isEmpty()) {
qFatal("KStandardDirs: The resource type %s is not registered", type);
} else {
path = realPath(dirs.first());
}
}
d->m_savelocations.insert(type, path.endsWith(QLatin1Char('/')) ? path : path + QLatin1Char('/'));
}
QString fullPath = path + suffix;
KDE_struct_stat st;
if (KDE::stat(fullPath, &st) != 0 || !(S_ISDIR(st.st_mode))) {
if(!create) {
#ifndef NDEBUG
// Too much noise from kbuildsycoca4 -- it's fine if this happens from KConfig
// when parsing global files without a local equivalent.
//kDebug(180) << QString("save location %1 doesn't exist").arg(fullPath);
#endif
return fullPath;
}
if(!makeDir(fullPath, 0700)) {
return fullPath;
}
d->m_dircache.remove(type);
}
if (!fullPath.endsWith(QLatin1Char('/')))
fullPath += QLatin1Char('/');
return fullPath;
}
QString KStandardDirs::relativeLocation(const char *type, const QString &absPath) const
{
QString fullPath = absPath;
int i = absPath.lastIndexOf(QLatin1Char('/'));
if (i != -1) {
fullPath = realFilePath(absPath); // Normalize
}
const QStringList candidates = resourceDirs(type);
for (QStringList::ConstIterator it = candidates.begin();
it != candidates.end(); ++it) {
if (fullPath.startsWith(*it)) {
return fullPath.mid((*it).length());
}
}
return absPath;
}
bool KStandardDirs::makeDir(const QString& dir, int mode)
{
// we want an absolute path
if (QDir::isRelativePath(dir))
return false;
#ifdef Q_OS_WIN
return QDir().mkpath(dir);
#else
QString target = dir;
uint len = target.length();
// append trailing slash if missing
if (dir.at(len - 1) != QLatin1Char('/'))
target += QLatin1Char('/');
QString base;
uint i = 1;
while( i < len )
{
KDE_struct_stat st;
int pos = target.indexOf(QLatin1Char('/'), i);
base += target.mid(i - 1, pos - i + 1);
QByteArray baseEncoded = QFile::encodeName(base);
// bail out if we encountered a problem
if (KDE_stat(baseEncoded, &st) != 0)
{
// Directory does not exist....
// Or maybe a dangling symlink ?
if (KDE_lstat(baseEncoded, &st) == 0)
(void)unlink(baseEncoded); // try removing
if (KDE_mkdir(baseEncoded, static_cast<mode_t>(mode)) != 0) {
baseEncoded.prepend( "trying to create local folder " );
perror(baseEncoded.constData());
return false; // Couldn't create it :-(
}
}
i = pos + 1;
}
return true;
#endif
}
static QString readEnvPath(const char *env)
{
QByteArray c_path;
#ifndef _WIN32_WCE
c_path = qgetenv(env);
if (c_path.isEmpty())
return QString();
#else
bool ok;
QString retval = getWin32RegistryValue(HKEY_LOCAL_MACHINE, "Software\\kde", "KDEDIRS", &ok);
if (!ok){
return QString();
} else {
- c_path = retval.toAscii();
+ c_path = retval.toLatin1();
}
#endif
return QDir::fromNativeSeparators(QFile::decodeName(c_path));
}
#ifdef __linux__
static QString executablePrefix()
{
char path_buffer[MAXPATHLEN + 1];
path_buffer[MAXPATHLEN] = 0;
int length = readlink ("/proc/self/exe", path_buffer, MAXPATHLEN);
if (length == -1)
return QString();
path_buffer[length] = '\0';
QString path = QFile::decodeName(path_buffer);
if(path.isEmpty())
return QString();
int pos = path.lastIndexOf(QLatin1Char('/')); // Skip filename
if(pos <= 0)
return QString();
pos = path.lastIndexOf(QLatin1Char('/'), pos - 1); // Skip last directory
if(pos <= 0)
return QString();
return path.left(pos);
}
#endif
void KStandardDirs::addResourcesFrom_krcdirs()
{
QString localFile = QDir::currentPath() + QLatin1String("/.krcdirs");
if (!QFile::exists(localFile))
return;
QSettings iniFile(localFile, QSettings::IniFormat);
iniFile.beginGroup(QString::fromLatin1("KStandardDirs"));
const QStringList resources = iniFile.allKeys();
foreach(const QString &key, resources)
{
QDir path(iniFile.value(key).toString());
if (!path.exists())
continue;
if(path.makeAbsolute())
- addResourceDir(key.toAscii(), path.path(), false);
+ addResourceDir(key.toLatin1(), path.path(), false);
}
}
void KStandardDirs::addKDEDefaults()
{
addResourcesFrom_krcdirs();
QStringList kdedirList;
// begin KDEDIRS
QString kdedirs = readEnvPath("KDEDIRS");
if (!kdedirs.isEmpty())
{
tokenize(kdedirList, kdedirs, QString(QLatin1Char(KPATH_SEPARATOR)));
}
kdedirList.append(installPath("kdedir"));
QString execPrefix(QFile::decodeName(EXEC_INSTALL_PREFIX));
if (!execPrefix.isEmpty() && !kdedirList.contains(execPrefix))
kdedirList.append(execPrefix);
#ifdef __linux__
const QString linuxExecPrefix = executablePrefix();
if ( !linuxExecPrefix.isEmpty() )
kdedirList.append( linuxExecPrefix );
#endif
#if 0 // No longer applicable in KDE Frameworks 5
// We treat root differently to prevent a "su" shell messing up the
// file permissions in the user's home directory.
QString localKdeDir = readEnvPath(getuid() ? "KDEHOME" : "KDEROOTHOME");
if (!localKdeDir.isEmpty()) {
if (!localKdeDir.endsWith(QLatin1Char('/')))
localKdeDir += QLatin1Char('/');
} else {
#if defined(Q_OS_MAC)
localKdeDir = QDir::homePath() + QLatin1String("/Library/Preferences/KDE/");
#elif defined(Q_OS_WIN)
#ifndef _WIN32_WCE
WCHAR wPath[MAX_PATH+1];
if ( SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wPath) == S_OK) {
localKdeDir = QDir::fromNativeSeparators(QString::fromUtf16((const ushort *) wPath)) + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/');
} else {
#endif
localKdeDir = QDir::homePath() + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/');
#ifndef _WIN32_WCE
}
#endif
#else
localKdeDir = QDir::homePath() + QLatin1Char('/') + QString::fromLatin1(KDE_DEFAULT_HOME) + QLatin1Char('/');
#endif
}
if (localKdeDir != QLatin1String("-/"))
{
localKdeDir = KShell::tildeExpand(localKdeDir);
addPrefix(localKdeDir);
}
#endif
#ifdef Q_OS_MAC
// Adds the "Contents" directory of the current application bundle to
// the search path. This way bundled resources can be found.
QDir bundleDir(mac_app_filename());
if (bundleDir.dirName() == QLatin1String("MacOS")) { // just to be sure we're in a bundle
bundleDir.cdUp();
// now dirName should be "Contents". In there we can find our normal
// dir-structure, beginning with "share"
addPrefix(bundleDir.absolutePath());
}
#endif
QStringList::ConstIterator end(kdedirList.end());
for (QStringList::ConstIterator it = kdedirList.constBegin();
it != kdedirList.constEnd(); ++it)
{
const QString dir = KShell::tildeExpand(*it);
addPrefix(dir);
}
// end KDEDIRS
// begin XDG_CONFIG_XXX
QStringList xdgdirList;
QString xdgdirs = readEnvPath("XDG_CONFIG_DIRS");
if (!xdgdirs.isEmpty())
{
tokenize(xdgdirList, xdgdirs, QString(QLatin1Char(KPATH_SEPARATOR)));
}
else
{
xdgdirList.clear();
xdgdirList.append(QString::fromLatin1("/etc/xdg"));
#ifdef Q_OS_WIN
xdgdirList.append(installPath("kdedir") + QString::fromLatin1("etc/xdg"));
#else
xdgdirList.append(QFile::decodeName(KDESYSCONFDIR "/xdg"));
#endif
}
QString localXdgDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
addXdgConfigPrefix(localXdgDir);
for (QStringList::ConstIterator it = xdgdirList.constBegin();
it != xdgdirList.constEnd(); ++it)
{
QString dir = KShell::tildeExpand(*it);
addXdgConfigPrefix(dir);
}
// end XDG_CONFIG_XXX
// begin XDG_DATA_XXX
QStringList kdedirDataDirs;
for (QStringList::ConstIterator it = kdedirList.constBegin();
it != kdedirList.constEnd(); ++it) {
QString dir = *it;
if (!dir.endsWith(QLatin1Char('/')))
dir += QLatin1Char('/');
kdedirDataDirs.append(dir + QLatin1String("share/"));
}
xdgdirList.clear();
xdgdirs = readEnvPath("XDG_DATA_DIRS");
if (!xdgdirs.isEmpty()) {
tokenize(xdgdirList, xdgdirs, QString(QLatin1Char(KPATH_SEPARATOR)));
// Ensure the kdedirDataDirs are in there too,
// otherwise resourceDirs() will add kdedir/share/applications/kde5
// as returned by installPath(), and that's incorrect.
Q_FOREACH(const QString& dir, kdedirDataDirs) {
if (!xdgdirList.contains(dir))
xdgdirList.append(dir);
}
} else {
xdgdirList = kdedirDataDirs;
#ifndef Q_OS_WIN
xdgdirList.append(QString::fromLatin1("/usr/local/share/"));
xdgdirList.append(QString::fromLatin1("/usr/share/"));
#endif
}
localXdgDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
addXdgDataPrefix(localXdgDir);
for (QStringList::ConstIterator it = xdgdirList.constBegin();
it != xdgdirList.constEnd(); ++it)
{
QString dir = KShell::tildeExpand(*it);
addXdgDataPrefix(dir);
}
// end XDG_DATA_XXX
addResourceType("lib", 0, "lib" KDELIBSUFF "/");
addResourceType("qtplugins", "lib", "plugins");
uint index = 0;
while (types_indices[index] != -1) {
addResourceType(types_string + types_indices[index], 0, types_string + types_indices[index+1], true);
index+=2;
}
// config resource: the XDG paths (xdg/config) have more priority than the KDE4 paths (share/config)
addResourceType("config", "xdgconf", "/", true);
addResourceType("exe", "lib", "kde5/libexec", true );
addResourceDir("home", QDir::homePath(), false);
addResourceType("autostart", "xdgconf-autostart", "/"); // merge them, start with xdg autostart
addResourceType("autostart", NULL, "share/autostart"); // KDE ones are higher priority - KDE 5: deprecated, use xdgconf-autostart
QString appName = QCoreApplication::applicationName();
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
if (appName.isEmpty()) {
appName = qAppName();
}
#endif
addResourceType("appdata", "data", appName + QLatin1Char('/'), true);
}
static QStringList lookupProfiles(const QString &mapFile)
{
QStringList profiles;
if (mapFile.isEmpty() || !QFile::exists(mapFile))
{
profiles << QString::fromLatin1("default");
return profiles;
}
struct passwd *pw = getpwuid(geteuid());
if (!pw)
{
profiles << QString::fromLatin1("default");
return profiles; // Not good
}
QByteArray user = pw->pw_name;
gid_t sup_gids[512];
int sup_gids_nr = getgroups(512, sup_gids);
KConfig mapCfgFile(mapFile);
KConfigGroup mapCfg(&mapCfgFile, "Users");
if (mapCfg.hasKey(user.constData()))
{
profiles = mapCfg.readEntry(user.constData(), QStringList());
return profiles;
}
const KConfigGroup generalGrp(&mapCfgFile, "General");
const QStringList groups = generalGrp.readEntry("groups", QStringList());
const KConfigGroup groupsGrp(&mapCfgFile, "Groups");
for( QStringList::ConstIterator it = groups.begin();
it != groups.end(); ++it )
{
QByteArray grp = (*it).toUtf8();
// Check if user is in this group
struct group *grp_ent = getgrnam(grp);
if (!grp_ent) continue;
gid_t gid = grp_ent->gr_gid;
if (pw->pw_gid == gid)
{
// User is in this group --> add profiles
profiles += groupsGrp.readEntry(*it, QStringList());
}
else
{
for(int i = 0; i < sup_gids_nr; i++)
{
if (sup_gids[i] == gid)
{
// User is in this group --> add profiles
profiles += groupsGrp.readEntry(*it, QStringList());
break;
}
}
}
}
if (profiles.isEmpty())
profiles << QString::fromLatin1("default");
return profiles;
}
extern KSERVICE_EXPORT bool kde_kiosk_admin;
bool KStandardDirs::addCustomized(KConfig *config)
{
if (!d->m_checkRestrictions) // there are already customized entries
return false; // we just quit and hope they are the right ones
// save the numbers of config directories. If this changes,
// we will return true to give KConfig a chance to reparse
int configdirs = resourceDirs("config").count();
if (true)
{
// reading the prefixes in
QString group = QLatin1String("Directories");
KConfigGroup cg(config, group);
QString kioskAdmin = cg.readEntry("kioskAdmin");
if (!kioskAdmin.isEmpty() && !kde_kiosk_admin)
{
int i = kioskAdmin.indexOf(QLatin1Char(':'));
QString user = kioskAdmin.left(i);
QString host = kioskAdmin.mid(i+1);
KUser thisUser;
char hostname[ 256 ];
hostname[ 0 ] = '\0';
if (!gethostname( hostname, 255 ))
hostname[sizeof(hostname)-1] = '\0';
if ((user == thisUser.loginName()) &&
(host.isEmpty() || (host == QLatin1String(hostname))))
{
kde_kiosk_admin = true;
}
}
bool readProfiles = true;
if (kde_kiosk_admin && !qgetenv("KDE_KIOSK_NO_PROFILES").isEmpty())
readProfiles = false;
QString userMapFile = cg.readEntry("userProfileMapFile");
QString profileDirsPrefix = cg.readEntry("profileDirsPrefix");
if (!profileDirsPrefix.isEmpty() && !profileDirsPrefix.endsWith(QLatin1Char('/')))
profileDirsPrefix.append(QLatin1Char('/'));
QStringList profiles;
if (readProfiles)
profiles = lookupProfiles(userMapFile);
QString profile;
bool priority = false;
while(true)
{
KConfigGroup cg(config, group);
const QStringList list = cg.readEntry("prefixes", QStringList());
for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
{
addPrefix(*it, priority);
addXdgConfigPrefix(*it + QLatin1String("/etc/xdg"), priority);
addXdgDataPrefix(*it + QLatin1String("/share"), priority);
}
// If there are no prefixes defined, check if there is a directory
// for this profile under <profileDirsPrefix>
if (list.isEmpty() && !profile.isEmpty() && !profileDirsPrefix.isEmpty())
{
QString dir = profileDirsPrefix + profile;
addPrefix(dir, priority);
addXdgConfigPrefix(dir + QLatin1String("/etc/xdg"), priority);
addXdgDataPrefix(dir + QLatin1String("/share"), priority);
}
// iterating over all entries in the group Directories
// to find entries that start with dir_$type
const QMap<QString, QString> entries = config->entryMap(group);
for (QMap<QString, QString>::ConstIterator it2 = entries.begin();
it2 != entries.end(); ++it2)
{
const QString key = it2.key();
if (key.startsWith(QLatin1String("dir_"))) {
// generate directory list, there may be more than 1.
const QStringList dirs = (*it2).split(QString(QLatin1Char(',')));
QStringList::ConstIterator sIt(dirs.begin());
QString resType = key.mid(4);
for (; sIt != dirs.end(); ++sIt)
{
addResourceDir(resType.toLatin1(), *sIt, priority);
}
}
}
if (profiles.isEmpty())
break;
profile = profiles.back();
group = QString::fromLatin1("Directories-%1").arg(profile);
profiles.pop_back();
priority = true;
}
}
// Process KIOSK restrictions.
if (!kde_kiosk_admin || qgetenv("KDE_KIOSK_NO_RESTRICTIONS").isEmpty())
{
KConfigGroup cg(config, "KDE Resource Restrictions");
const QMap<QString, QString> entries = cg.entryMap();
for (QMap<QString, QString>::ConstIterator it2 = entries.begin();
it2 != entries.end(); ++it2)
{
const QString key = it2.key();
if (!cg.readEntry(key, true))
{
d->m_restrictionsActive = true;
const QByteArray cKey = key.toLatin1();
d->m_restrictions.insert(cKey, true);
d->m_dircache.remove(cKey);
d->m_savelocations.remove(cKey);
}
}
}
// check if the number of config dirs changed
bool configDirsChanged = (resourceDirs("config").count() != configdirs);
// If the config dirs changed, we check kiosk restrictions again.
d->m_checkRestrictions = configDirsChanged;
// return true if the number of config dirs changed: reparse config file
return configDirsChanged;
}
#ifndef KDE_NO_DEPRECATED
QString KStandardDirs::localkdedir() const
{
// Return the prefix to use for saving
return d->m_prefixes.first();
}
#endif
QString KStandardDirs::localxdgdatadir() const
{
// Return the prefix to use for saving
return d->xdgdata_prefixes.first();
}
QString KStandardDirs::localxdgconfdir() const
{
// Return the prefix to use for saving
return d->xdgconf_prefixes.first();
}
// just to make code more readable without macros
QString KStandardDirs::locate(const char *type,
const QString& filename)
{
return KGlobal::dirs()->findResource(type, filename);
}
QString KStandardDirs::locateLocal(const char *type,
const QString& filename)
{
return KGlobal::dirs()->locateLocal(type, filename, true);
}
QString KStandardDirs::locateLocal(const char *type,
const QString& filename, bool createDir)
{
// try to find slashes. If there are some, we have to
// create the subdir first
int slash = filename.lastIndexOf(QLatin1Char('/')) + 1;
if (!slash) { // only one filename
return KGlobal::dirs()->saveLocation(type, QString(), createDir) + filename;
}
// split path from filename
QString dir = filename.left(slash);
QString file = filename.mid(slash);
return KGlobal::dirs()->saveLocation(type, dir, createDir) + file;
}
bool KStandardDirs::checkAccess(const QString& pathname, int mode)
{
int accessOK = KDE::access( pathname, mode );
if ( accessOK == 0 )
return true; // OK, I can really access the file
// else
// if we want to write the file would be created. Check, if the
// user may write to the directory to create the file.
if ( (mode & W_OK) == 0 )
return false; // Check for write access is not part of mode => bail out
if (!KDE::access( pathname, F_OK)) // if it already exists
return false;
//strip the filename (everything until '/' from the end
QString dirName(pathname);
int pos = dirName.lastIndexOf(QLatin1Char('/'));
if ( pos == -1 )
return false; // No path in argument. This is evil, we won't allow this
else if ( pos == 0 ) // don't turn e.g. /root into an empty string
pos = 1;
dirName.truncate(pos); // strip everything starting from the last '/'
accessOK = KDE::access( dirName, W_OK );
// -?- Can I write to the accessed diretory
if ( accessOK == 0 )
return true; // Yes
else
return false; // No
}
diff --git a/kdecore/tests/kstandarddirstest.cpp b/kdecore/tests/kstandarddirstest.cpp
index a0e266aade..d636d8e3b8 100644
--- a/kdecore/tests/kstandarddirstest.cpp
+++ b/kdecore/tests/kstandarddirstest.cpp
@@ -1,556 +1,556 @@
/* This file is part of the KDE libraries
Copyright (c) 2006, 2011 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 version 2 as published by the Free Software Foundation.
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 "kstandarddirstest.h"
#include "qstandardpaths.h"
#include "qtest.h"
#include "kde_qt5_compat.h"
QTEST_MAIN(KStandarddirsTest)
#include <config-kstandarddirs.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kcomponentdata.h>
#include <kconfig.h>
#include <kglobal.h>
#include <qtemporarydir.h>
#include <config-prefix.h>
#include <QtCore/QDebug>
#include <kconfiggroup.h>
// we need case-insensitive comparison of file paths on windows
#ifdef Q_OS_WIN
#define QCOMPARE_PATHS(x,y) QCOMPARE(QString(x).toLower(), QString(y).toLower())
#define PATH_SENSITIVITY Qt::CaseInsensitive
#else
#define QCOMPARE_PATHS(x,y) QCOMPARE(QString(x), QString(y))
#define PATH_SENSITIVITY Qt::CaseSensitive
#endif
void KStandarddirsTest::initTestCase()
{
m_configHome = QDir::homePath() + QLatin1String("/.kde-unit-test/xdg/config");
qputenv("XDG_CONFIG_HOME", QFile::encodeName(m_configHome));
m_dataHome = QDir::homePath() + QLatin1String("/.kde-unit-test/xdg/local");
qputenv("XDG_DATA_HOME", QFile::encodeName(m_dataHome));
QFile::remove(KGlobal::dirs()->saveLocation("config") + "kstandarddirstestrc");
// Create a main component data so that testAppData doesn't suddenly change the main component
// data.
KComponentData mainData("kstandarddirstest");
// Must initialize KStandardDirs only after all the setenv() calls.
QCOMPARE(KGlobal::dirs()->localxdgconfdir(), QString(m_configHome + '/'));
}
void KStandarddirsTest::testSaveLocation()
{
const QString saveLocConfig = KGlobal::dirs()->saveLocation("config");
QCOMPARE_PATHS(saveLocConfig, KGlobal::dirs()->localxdgconfdir());
const QString saveLocXdgConfig = KGlobal::dirs()->saveLocation("xdgconf");
QCOMPARE_PATHS(saveLocConfig, saveLocXdgConfig); // same result
const QString saveLocAppData = KGlobal::dirs()->saveLocation("appdata");
QCOMPARE_PATHS(saveLocAppData, m_dataHome + "/kstandarddirstest/");
}
void KStandarddirsTest::testLocateLocal()
{
const QString configLocal = KStandardDirs::locateLocal( "config", "ksomethingrc" );
// KStandardDirs resolves symlinks, so we must compare with canonicalPath()
QCOMPARE_PATHS( configLocal, m_configHome + "/ksomethingrc" );
}
void KStandarddirsTest::testResourceDirs()
{
const QStringList configDirs = KGlobal::dirs()->resourceDirs("config");
Q_FOREACH(const QString& dir, configDirs) {
QVERIFY2(dir.endsWith("xdg/")
|| dir.endsWith("share/config/") // KDE4 compat path
|| dir.endsWith(".kde-unit-test/xdg/config/"), qPrintable(dir));
}
const QStringList dataDirs = KGlobal::dirs()->resourceDirs("data");
Q_FOREACH(const QString& dir, dataDirs) {
QVERIFY2(dir.endsWith("share/") || dir.endsWith(".kde-unit-test/xdg/local/"), qPrintable(dir));
}
}
void KStandarddirsTest::testAppData()
{
// This API is gone
#if 0
// In addition to testSaveLocation(), we want to also check other KComponentDatas
KComponentData cData("foo");
const QString fooAppData = cData.dirs()->saveLocation( "appdata" );
QCOMPARE_PATHS( fooAppData, m_dataHome + "/foo/" );
#endif
}
void KStandarddirsTest::testChangeSaveLocation()
{
KStandardDirs cData;
QCOMPARE_PATHS(cData.saveLocation("config"), m_configHome + "/");
// Can we change the save location?
const QString newSaveLoc = m_configHome + "/newconfigdir/";
//cData.addResourceDir("config", newSaveLoc); // can't be done, absolute paths have less priority than relative paths
cData.addResourceType("config", 0, "newconfigdir");
QCOMPARE_PATHS(KStandardDirs::realPath(cData.saveLocation("config")), newSaveLoc);
}
static bool isKdelibsInstalled()
{
// If there's only one dir, it's the local one (~/.kde-unit-test/share/),
// meaning that kdelibs wasn't installed (or we don't find where, the environment isn't right).
return KGlobal::dirs()->resourceDirs( "data" ).count() > 1;
}
void KStandarddirsTest::testFindResource()
{
if ( !isKdelibsInstalled() )
QSKIP_PORTING( "kdelibs not installed", SkipAll );
#ifdef Q_OS_WIN
#define EXT ".exe"
#define KIOSLAVE "bin/kioslave.exe"
#else
#define EXT ""
#define KIOSLAVE "kde5/libexec/kioslave"
#endif
const QString bin = KGlobal::dirs()->findResource( "exe", "kioslave" EXT );
QVERIFY( !bin.isEmpty() );
QVERIFY( bin.endsWith( KIOSLAVE ) );
QVERIFY( !QDir::isRelativePath(bin) );
const QString data = KGlobal::dirs()->findResource( "data", "cmake/modules/FindSoprano.cmake" );
QVERIFY( !data.isEmpty() );
QVERIFY( data.endsWith( QLatin1String("share/cmake/modules/FindSoprano.cmake") ) );
QVERIFY( !QDir::isRelativePath(data) );
}
static bool oneEndsWith( const QStringList& lst, const QString& str)
{
for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
if ( (*it).endsWith( str ) )
return true;
}
return false;
}
void KStandarddirsTest::testFindAllResources()
{
if ( !isKdelibsInstalled() )
QSKIP_PORTING( "kdelibs not installed", SkipAll );
const QStringList cmakeModulesFiles = KGlobal::dirs()->findAllResources( "data", "cmake/modules/" );
QVERIFY( !cmakeModulesFiles.isEmpty() );
QVERIFY( cmakeModulesFiles.count() > 80 ); // I have 150 here, installed by kdelibs.
// Create a local config file, the file will be used as expected result
KConfig foorc("foorc");
KConfigGroup dummyGroup(&foorc, "Dummy");
dummyGroup.writeEntry("someEntry", true);
dummyGroup.sync();
const QString localConfigFile = KGlobal::dirs()->localxdgconfdir() + "foorc";
QVERIFY2(QFile::exists(localConfigFile), qPrintable(localConfigFile));
const QStringList configFiles = KGlobal::dirs()->findAllResources( "config" );
QVERIFY( !configFiles.isEmpty() );
QVERIFY( configFiles.count() > 5 ); // I have 9 here
QVERIFY( oneEndsWith( configFiles, "etc/xdg/kdebugrc" ) );
QVERIFY( oneEndsWith( configFiles, "kde-unit-test/xdg/config/foorc" ) );
QVERIFY( !oneEndsWith( configFiles, "etc/xdg/colors/Web.colors" ) ); // recursive was false
{
const QStringList configFilesRecursive = KGlobal::dirs()->findAllResources( "config", QString(),
KStandardDirs::Recursive );
QVERIFY( !configFilesRecursive.isEmpty() );
QVERIFY( configFilesRecursive.count() > 5 ); // I have 15 here
QVERIFY( oneEndsWith( configFilesRecursive, "etc/xdg/kdebugrc" ) );
QVERIFY( oneEndsWith( configFilesRecursive, "etc/xdg/colors/Web.colors" ) ); // proves that recursive worked
}
{
const QStringList configFilesRecursiveWithFilter = KGlobal::dirs()->findAllResources( "config", "*rc",
KStandardDirs::Recursive );
QVERIFY( !configFilesRecursiveWithFilter.isEmpty() );
//qDebug() << configFilesRecursiveWithFilter;
QVERIFY( configFilesRecursiveWithFilter.count() >= 3 ); // foorc, kdebugrc, ui/ui_standards.rc
QVERIFY( oneEndsWith( configFilesRecursiveWithFilter, "kde-unit-test/xdg/config/foorc" ) );
QVERIFY( oneEndsWith( configFilesRecursiveWithFilter, "etc/xdg/kdebugrc" ) );
QVERIFY( oneEndsWith( configFilesRecursiveWithFilter, "etc/xdg/ui/ui_standards.rc" ) );
QVERIFY( !oneEndsWith( configFilesRecursiveWithFilter, "etc/xdg/colors/Web.colors" ) ); // didn't match the filter
}
{
QStringList fileNames;
const QStringList configFilesWithFilter = KGlobal::dirs()->findAllResources("config", "*rc", KStandardDirs::NoDuplicates, fileNames);
QVERIFY( !configFilesWithFilter.isEmpty() );
QVERIFY( configFilesWithFilter.count() >= 2 );
QVERIFY( oneEndsWith( configFilesWithFilter, "kde-unit-test/xdg/config/foorc" ) );
QVERIFY( oneEndsWith( configFilesWithFilter, "kdebugrc" ) ); // either global (etc/xdg/) or local (XDG_HOME)
QVERIFY( !oneEndsWith( configFilesWithFilter, "etc/xdg/ui/ui_standards.rc" ) ); // recursive not set
QVERIFY( !oneEndsWith( configFilesWithFilter, "etc/xdg/accept-languages.codes" ) ); // didn't match the filter
QCOMPARE(fileNames.count(), configFilesWithFilter.count());
QVERIFY(fileNames.contains("kdebugrc"));
}
#if 0
list = t.findAllResources("html", "en/*/index.html", false);
for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
- kDebug() << "docs " << (*it).toAscii().constData();
+ kDebug() << "docs " << (*it).toLatin1().constData();
}
list = t.findAllResources("html", "*/*/*.html", false);
for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
- kDebug() << "docs " << (*it).toAscii().constData();
+ kDebug() << "docs " << (*it).toLatin1().constData();
}
#endif
}
void KStandarddirsTest::testFindAllResourcesNewDir()
{
const QString dir = m_dataHome + "/cmake/modules";
const QString fileName = dir + "/unittest.testfile";
QFile::remove(fileName);
const QStringList origFiles = KGlobal::dirs()->findAllResources("data", "cmake/modules/");
const int origCount = origFiles.count();
QDir().mkpath(dir);
QFile file(fileName);
QVERIFY(file.open(QIODevice::WriteOnly|QIODevice::Text));
file.write("foo");
file.close();
const int newCount = KGlobal::dirs()->findAllResources("data", "cmake/modules/").count();
QCOMPARE(newCount, origCount+1);
file.remove();
QDir().rmpath(dir);
}
void KStandarddirsTest::testFindDirs()
{
if ( !isKdelibsInstalled() )
QSKIP_PORTING( "kdelibs not installed", SkipAll );
const QString t = KStandardDirs::locateLocal("data", "kconf_update/" );
QCOMPARE(t, QString(m_dataHome + "/kconf_update/"));
const QStringList dirs = KGlobal::dirs()->findDirs( "data", "kconf_update" );
QVERIFY( !dirs.isEmpty() );
QVERIFY2(dirs.count() >= 2, qPrintable(dirs.join(","))); // at least local and global
//qDebug() << dirs;
}
void KStandarddirsTest::testFindResourceDir()
{
if ( !isKdelibsInstalled() )
QSKIP_PORTING( "kdelibs not installed", SkipAll );
const QString configDir = KGlobal::dirs()->findResourceDir( "config", "foorc" );
QVERIFY( !configDir.isEmpty() );
QVERIFY2(configDir.endsWith(QLatin1String("/xdg/config/")), qPrintable(configDir));
}
void KStandarddirsTest::testFindExe()
{
if ( !isKdelibsInstalled() )
QSKIP_PORTING( "kdelibs not installed", SkipAll );
// findExe with a result in bin
const QString kdeinit = KGlobal::dirs()->findExe( "kdeinit5" );
QVERIFY( !kdeinit.isEmpty() );
#ifdef Q_OS_MAC
QVERIFY2(kdeinit.endsWith("kdeinit5", PATH_SENSITIVITY), qPrintable(kdeinit));
#else
QVERIFY2(kdeinit.endsWith("bin/kdeinit5" EXT, PATH_SENSITIVITY), qPrintable(kdeinit));
#endif
#ifdef Q_OS_UNIX
// findExe with a result in libexec
const QString kioslave = KGlobal::dirs()->findExe( "kioslave" );
QVERIFY( !kioslave.isEmpty() );
QVERIFY( kioslave.endsWith( "lib" KDELIBSUFF "/kde5/libexec/kioslave" EXT, PATH_SENSITIVITY ) );
#endif
#ifndef Q_OS_MAC // kdeinit5 is a bundle on Mac, so the below doesn't work
// Check the "exe" resource too
QString kdeinitPath1 = KStandardDirs::realFilePath(kdeinit);
QString kdeinitPath2 = KGlobal::dirs()->locate( "exe", "kdeinit5" );
QCOMPARE_PATHS( kdeinitPath1, kdeinitPath2 );
// Check realFilePath behavior with complete command lines, like KRun does
const QString cmd = kdeinit + " -c foo -x bar";
const QString fromKStdDirs = KStandardDirs::realFilePath(cmd);
QCOMPARE(fromKStdDirs, cmd);
const QString fromQFileInfo = QFileInfo(cmd).canonicalFilePath();
QVERIFY(fromQFileInfo.isEmpty()); // !! different result, since this doesn't exist as a file
#endif
#ifdef Q_OS_UNIX
QCOMPARE_PATHS(KGlobal::dirs()->realFilePath(kioslave), KGlobal::dirs()->locate("exe", "kioslave"));
// findExe with relative path
const QString pwd = QDir::currentPath();
QDir::setCurrent("/bin");
QStringList possibleResults;
possibleResults << QString::fromLatin1("/bin/sh") << QString::fromLatin1("/usr/bin/sh");
const QString sh = KGlobal::dirs()->findExe("./sh");
if (!possibleResults.contains(sh)) {
kDebug() << sh;
}
QVERIFY(possibleResults.contains(sh));
QDir::setCurrent(pwd);
#endif
#if 0 // Broken test, findExe doesn't look in kdehome, but in kdehome/bin (in kde4) and in $PATH.
#ifdef Q_OS_UNIX
QFile home( m_kdehome );
const QString target = m_kdehome + "/linktodir";
home.link( target );
QVERIFY( KGlobal::dirs()->findExe( target ).isEmpty() );
#endif
#endif
#ifdef Q_OS_UNIX
// findExe for a binary not part of KDE
const QString ls = KGlobal::dirs()->findExe( "ls" );
QVERIFY( !ls.isEmpty() );
QVERIFY( ls.endsWith( QLatin1String( "bin/ls" ) ) );
#endif
// findExe with no result
const QString idontexist = KGlobal::dirs()->findExe( "idontexist" );
QVERIFY( idontexist.isEmpty() );
// findExe with empty string
const QString empty = KGlobal::dirs()->findExe( "" );
QVERIFY( empty.isEmpty() );
}
void KStandarddirsTest::testLocate()
{
QString textPlain = "text/x-patch.xml";
Q_FOREACH( const QString &path, KGlobal::dirs()->resourceDirs("xdgdata-mime") ) {
if (QFile::exists(path + textPlain)) {
textPlain = path + textPlain;
break;
}
}
if( textPlain == "text/x-patch.xml" )
QSKIP_PORTING("xdg-share-mime not installed", SkipAll);
const QString res = KGlobal::dirs()->locate("xdgdata-mime", "text/x-patch.xml");
QCOMPARE_PATHS(res, textPlain);
}
void KStandarddirsTest::testRelativeLocation()
{
const QString file = "kdebugrc";
QString located = KGlobal::dirs()->locate( "config", file );
QCOMPARE_PATHS( KGlobal::dirs()->relativeLocation( "config", located ), file );
}
void KStandarddirsTest::testAddResourceType()
{
if ( !isKdelibsInstalled() )
QSKIP_PORTING( "kdelibs not installed", SkipAll );
QString ret = KStandardDirs::locate( "dtd", "customization/catalog.xml" );
QCOMPARE(ret, QString()); // normal, there's no "dtd" resource in kstandarddirs by default
KGlobal::dirs()->addResourceType("dtd", "data", "ksgmltools2/");
ret = KStandardDirs::locate( "dtd", "customization/catalog.xml" );
QVERIFY(!ret.isEmpty());
ret = KStandardDirs::locate("dtd", "customization/kde-chunk.xsl");
QVERIFY(!ret.isEmpty());
const QStringList files = KGlobal::dirs()->findAllResources("dtd", "customization/*", KStandardDirs::NoDuplicates);
QVERIFY(files.count() > 2);
KGlobal::dirs()->addResourceType("xdgdata-ontology", 0, "ontology");
const QStringList ontologyDirs = KGlobal::dirs()->resourceDirs("xdgdata-ontology");
QCOMPARE(ontologyDirs.first(), KStandardDirs::realPath(QString(qgetenv("XDG_DATA_HOME")) + "/ontology/"));
if (QFile::exists("/usr/share/ontology") &&
QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).contains("/usr/share")) {
QVERIFY(ontologyDirs.contains("/usr/share/ontology/"));
}
}
void KStandarddirsTest::testAddResourceDir()
{
const QString dir = QString::fromLatin1(KDESRCDIR);
const QString file = "Cairo";
QString ret = KStandardDirs::locate( "here", file );
QCOMPARE(ret, QString()); // not set up yet
KGlobal::dirs()->addResourceDir("here", dir);
ret = KStandardDirs::locate( "here", file );
QCOMPARE_PATHS(ret, KStandardDirs::realPath(dir) + "Cairo");
}
void KStandarddirsTest::testSetXdgDataDirs()
{
// By default we should have KDEDIR/share/applications in `kde5-config --path xdgdata-apps`
const QStringList dirs = KGlobal::dirs()->resourceDirs("xdgdata-apps");
const QString kdeDataApps = KStandardDirs::realPath(CMAKE_INSTALL_PREFIX "/share/applications/");
if (!dirs.contains(kdeDataApps)) {
kDebug() << "ERROR:" << kdeDataApps << "not in" << dirs;
kDebug() << "XDG_DATA_DIRS=" << qgetenv("XDG_DATA_DIRS");
kDebug() << "installprefix=" << KStandardDirs::installPath("kdedir");
kDebug() << "installdir=" << KStandardDirs::installPath("xdgdata-apps");
kDebug() << "GenericDataLocation=" << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
}
QVERIFY(dirs.contains(kdeDataApps, PATH_SENSITIVITY));
// When setting XDG_DATA_DIRS this should still be true
const QString localApps = m_dataHome + "/applications/";
QVERIFY(KStandardDirs::makeDir(localApps));
const QString customDataDir = QDir::homePath() + QLatin1String("/.kde-unit-test/xdg/global");
qputenv("XDG_DATA_DIRS", QFile::encodeName(customDataDir));
QVERIFY(QDir(customDataDir).mkpath("applications"));
KStandardDirs newStdDirs;
const QStringList newDirs = newStdDirs.resourceDirs("xdgdata-apps");
//qDebug() << newDirs;
QVERIFY(newDirs.contains(kdeDataApps, PATH_SENSITIVITY));
QVERIFY(newDirs.contains(localApps, PATH_SENSITIVITY));
QVERIFY(newDirs.contains(customDataDir + "/applications/", PATH_SENSITIVITY));
}
void KStandarddirsTest::testRestrictedResources()
{
// Ensure we have a local xdgdata-apps dir
QFile localFile(KStandardDirs::locateLocal("xdgdata-apps", "foo.desktop"));
QVERIFY(localFile.open(QIODevice::WriteOnly|QIODevice::Text));
localFile.write("foo");
localFile.close();
const QString localAppsDir = KStandardDirs::realPath(QFileInfo(localFile).absolutePath() + '/');
QVERIFY(!localAppsDir.contains("foo.desktop"));
// Ensure we have a local share/kstandarddirstest dir
const QString localDataDir = KStandardDirs::locateLocal("data", "kstandarddirstest/");
QVERIFY(!localDataDir.isEmpty());
QVERIFY(QFile::exists(localDataDir));
const QString localOtherDataDir = KStandardDirs::locateLocal("data", "other/");
QVERIFY(!localOtherDataDir.isEmpty());
// Check unrestricted results first
const QStringList appsDirs = KGlobal::dirs()->resourceDirs("xdgdata-apps");
QCOMPARE_PATHS(appsDirs.first(), localAppsDir);
const QString kdeDataApps = KStandardDirs::realPath(CMAKE_INSTALL_PREFIX "/share/applications/");
QVERIFY(appsDirs.contains(kdeDataApps, PATH_SENSITIVITY));
const QStringList dataDirs = KGlobal::dirs()->findDirs("data", "kstandarddirstest");
QCOMPARE_PATHS(dataDirs.first(), localDataDir);
const QStringList otherDataDirs = KGlobal::dirs()->findDirs("data", "other");
QCOMPARE_PATHS(otherDataDirs.first(), localOtherDataDir);
// Initialize restrictions.
// Need a new componentdata to trigger restricted-resource initialization
// And we need to write the config _before_ creating the KStandardDirs.
KConfig foorc("kstandarddirstestrc");
KConfigGroup restrictionsGroup(&foorc, "KDE Resource Restrictions");
restrictionsGroup.writeEntry("xdgdata-apps", false);
restrictionsGroup.writeEntry("data_kstandarddirstest", false);
restrictionsGroup.sync();
// Check restrictions.
//KComponentData cData("foo");
KStandardDirs* dirs = new KStandardDirs;
dirs->addCustomized(&foorc); // like KGlobal::dirs() does
QVERIFY(dirs->isRestrictedResource("xdgdata-apps"));
QVERIFY(dirs->isRestrictedResource("data", "kstandarddirstest"));
const QStringList newAppsDirs = dirs->resourceDirs("xdgdata-apps");
QVERIFY(newAppsDirs.contains(kdeDataApps, PATH_SENSITIVITY));
QVERIFY(!newAppsDirs.contains(localAppsDir, PATH_SENSITIVITY)); // restricted!
const QStringList newDataDirs = dirs->findDirs("data", "kstandarddirstest");
QVERIFY(!newDataDirs.contains(localDataDir, PATH_SENSITIVITY)); // restricted!
const QStringList newOtherDataDirs = dirs->findDirs("data", "other");
QVERIFY(newOtherDataDirs.contains(localOtherDataDir, PATH_SENSITIVITY)); // not restricted!
restrictionsGroup.deleteGroup();
localFile.remove();
delete dirs;
}
void KStandarddirsTest::testSymlinkResolution()
{
#ifndef Q_OS_WIN
// This makes the save location for the david resource, "<XDG_DATA_HOME>/symlink/test/"
// where symlink points to "real", and the subdir test will be created later
// This used to confuse KStandardDirs and make it return unresolved paths,
// and thus making comparisons fail later on in KConfig.
QString baseDir = m_dataHome;
const QString symlink = baseDir + "/symlink";
const QString expected = baseDir + "/real/test/";
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
QVERIFY(QTemporaryDir::removeRecursively(baseDir + "/real"));
#else
QDir d_(baseDir + "/real");
QVERIFY(d_.removeRecursively());
#endif
QVERIFY(QDir(baseDir).mkdir("real"));
QFile::remove(symlink);
QVERIFY(!QFile::exists(symlink));
QVERIFY(QFile::link("real", symlink));
QVERIFY(QFileInfo(symlink).isSymLink());
QVERIFY(!QFile::exists(expected));
KGlobal::dirs()->addResourceType("david", 0, "symlink/test");
QVERIFY(!QFile::exists(expected));
const QString saveLoc = KGlobal::dirs()->resourceDirs("david").first();
QVERIFY(!QFile::exists(expected));
// The issue at this point is that saveLoc does not actually exist yet.
QVERIFY(QDir(saveLoc).canonicalPath().isEmpty()); // this is why we can't use canonicalPath
QVERIFY(!QFile::exists(saveLoc));
QCOMPARE(saveLoc, KStandardDirs::realPath(saveLoc)); // must be resolved
QCOMPARE(saveLoc, expected);
QVERIFY(QDir(baseDir).mkpath("real/test")); // KConfig calls mkdir on its own, we simulate that here
const QString sameSaveLoc = KGlobal::dirs()->resourceDirs("david").first();
QCOMPARE(sameSaveLoc, saveLoc);
QCOMPARE(sameSaveLoc, KGlobal::dirs()->saveLocation("david"));
// While we're here...
QCOMPARE(KStandardDirs::realPath(QString()), QString());
QCOMPARE(KStandardDirs::realPath(QString("/")), QString("/"));
QCOMPARE(KStandardDirs::realPath(QString("/does_not_exist/")), QString("/does_not_exist/"));
#endif
}
#include <QThreadPool>
#include <QFutureSynchronizer>
#include <qtconcurrentrun.h>
// To find multithreading bugs: valgrind --tool=helgrind ./kstandarddirstest testThreads
void KStandarddirsTest::testThreads()
{
QThreadPool::globalInstance()->setMaxThreadCount(6);
QFutureSynchronizer<void> sync;
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testLocateLocal));
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testSaveLocation));
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testAppData));
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testFindResource));
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testFindAllResources));
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testLocate));
sync.addFuture(QtConcurrent::run(this, &KStandarddirsTest::testRelativeLocation));
sync.waitForFinished();
}
diff --git a/kdecore/util/kde_file_win.cpp b/kdecore/util/kde_file_win.cpp
index 48817cd931..7ee5055bb9 100644
--- a/kdecore/util/kde_file_win.cpp
+++ b/kdecore/util/kde_file_win.cpp
@@ -1,199 +1,199 @@
/*
This file is part of the KDE libraries
Copyright (C) 2004 Jarosław Staniek <staniek@kde.org>
Copyright (C) 2009 Christian Ehrlicher <ch.ehrlicher@gmx.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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.
*/
// needed for _wstat64
#define __MSVCRT_VERSION__ 0x601
#include "kde_file.h"
#include <QtCore/QFile>
#include <errno.h>
#include <sys/utime.h>
#include <sys/stat.h>
#include <wchar.h>
#define CONV(x) ((wchar_t*)x.utf16())
/** @internal, from kdewin32 lib */
static int kdewin_fix_mode_string(char *fixed_mode, const char *mode)
{
if (strlen(mode)<1 || strlen(mode)>3) {
errno = EINVAL;
return 1;
}
strncpy(fixed_mode, mode, 3);
if (fixed_mode[0]=='b' || fixed_mode[1]=='b' || fixed_mode[0]=='t' || fixed_mode[1]=='t')
return 0;
/* no 't' or 'b': append 'b' */
if (fixed_mode[1]=='+') {
fixed_mode[1]='b';
fixed_mode[2]='+';
fixed_mode[3]=0;
}
else {
fixed_mode[1]='b';
fixed_mode[2]=0;
}
return 0;
}
/** @internal */
static int kdewin_fix_flags(int flags)
{
if ((flags & O_TEXT) == 0 && (flags & O_BINARY) == 0)
return flags | O_BINARY;
return flags;
}
namespace KDE
{
int access(const QString &path, int mode)
{
int x_mode = 0;
// X_OK gives an assert on msvc2005 and up - use stat() instead
if( ( mode & X_OK ) == X_OK ) {
KDE_struct_stat st;
if( KDE::stat( path, &st ) != 0 )
return 1;
if( ( st.st_mode & S_IXUSR ) != S_IXUSR )
return 1;
}
mode &= ~X_OK;
return _waccess( CONV(path), mode );
}
int chmod(const QString &path, mode_t mode)
{
return _wchmod( CONV(path), mode );
}
FILE *fopen(const QString &pathname, const char *mode)
{
- return _wfopen( CONV(pathname), CONV(QString::fromAscii( mode )) );
+ return _wfopen( CONV(pathname), CONV(QString::fromLatin1( mode )) );
}
int lstat(const QString &path, KDE_struct_stat *buf)
{
return KDE::stat( path, buf );
}
int mkdir(const QString &pathname, mode_t)
{
return _wmkdir( CONV(pathname) );
}
int open(const QString &pathname, int flags, mode_t mode)
{
return _wopen( CONV(pathname), kdewin_fix_flags(flags), mode );
}
int rename(const QString &in, const QString &out)
{
// better than :waccess/_wunlink/_wrename
#ifndef _WIN32_WCE
bool ok = ( MoveFileExW( CONV(in), CONV(out),
MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED ) != 0 );
#else
bool ok = ( MoveFileW( CONV(in), CONV(out)) != 0 );
#endif
return ok ? 0 : -1;
}
int stat(const QString &path, KDE_struct_stat *buf)
{
int result;
#ifdef Q_CC_MSVC
#ifndef _WIN32_WCE
struct _stat64 s64;
#else
struct stat st;
#endif
#else
struct __stat64 s64;
#endif
const int len = path.length();
if ( (len==2 || len==3) && path[1]==QLatin1Char(':') && path[0].isLetter() ) {
/* 1) */
QString newPath(path);
if (len==2)
newPath += QLatin1Char('\\');
#ifndef _WIN32_WCE
result = _wstat64( CONV(newPath), &s64 );
#else
result = wstat( CONV(newPath), &st );
#endif
} else
if ( len > 1 && (path.endsWith(QLatin1Char('\\')) || path.endsWith(QLatin1Char('/'))) ) {
/* 2) */
const QString newPath = path.left( len - 1 );
#ifndef _WIN32_WCE
result = _wstat64( CONV(newPath), &s64 );
#else
result = wstat( CONV(newPath), &st );
#endif
} else {
//TODO: is stat("/") ok?
#ifndef _WIN32_WCE
result = _wstat64( CONV(path), &s64 );
#else
result = wstat( CONV(path), &st );
#endif
}
if( result != 0 )
return result;
// KDE5: fixme!
#ifndef _WIN32_WCE
buf->st_dev = s64.st_dev;
buf->st_ino = s64.st_ino;
buf->st_mode = s64.st_mode;
buf->st_nlink = s64.st_nlink;
#else
buf->st_dev = st.st_dev;
buf->st_ino = st.st_ino;
buf->st_mode = st.st_mode;
buf->st_nlink = st.st_nlink;
#endif
buf->st_uid = -2; // be in sync with Qt4
buf->st_gid = -2; // be in sync with Qt4
#ifndef _WIN32_WCE
buf->st_rdev = s64.st_rdev;
buf->st_size = s64.st_size;
buf->st_atime = s64.st_atime;
buf->st_mtime = s64.st_mtime;
buf->st_ctime = s64.st_ctime;
#else
buf->st_rdev = st.st_rdev;
buf->st_size = st.st_size;
buf->st_atime = st.st_atime;
buf->st_mtime = st.st_mtime;
buf->st_ctime = st.st_ctime;
#endif
return result;
}
int utime(const QString &filename, struct utimbuf *buf)
{
#ifndef _WIN32_WCE
return _wutime( CONV(filename), (struct _utimbuf*)buf );
#else
return _wutime( CONV(filename), (struct utimbuf*)buf );
#endif
}
};
diff --git a/kdecore/util/kshell_unix.cpp b/kdecore/util/kshell_unix.cpp
index 21afa27a7c..8614424f4d 100644
--- a/kdecore/util/kshell_unix.cpp
+++ b/kdecore/util/kshell_unix.cpp
@@ -1,295 +1,295 @@
/*
This file is part of the KDE libraries
Copyright (c) 2003,2007 Oswald Buddenhagen <ossi@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.
*/
#include "kshell.h"
#include "kshell_p.h"
#include <kuser.h>
#include <QtCore/QChar>
#include <QtCore/QStringList>
static int fromHex( QChar cUnicode )
{
- char c = cUnicode.toAscii ();
+ char c = cUnicode.toLatin1 ();
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1;
}
inline static bool isQuoteMeta( QChar cUnicode )
{
#if 0 // it's not worth it, especially after seeing gcc's asm output ...
static const uchar iqm[] = {
0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00
}; // \'"$
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
#else
- char c = cUnicode.toAscii();
+ char c = cUnicode.toLatin1();
return c == '\\' || c == '\'' || c == '"' || c == '$';
#endif
}
inline static bool isMeta( QChar cUnicode )
{
static const uchar iqm[] = {
0x00, 0x00, 0x00, 0x00, 0xdc, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x38
}; // \'"$`<>|;&(){}*?#[]
uint c = cUnicode.unicode();
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
}
QStringList KShell::splitArgs( const QString &args, Options flags, Errors *err)
{
QStringList ret;
bool firstword = flags & AbortOnMeta;
for (int pos = 0; ; ) {
QChar c;
do {
if (pos >= args.length())
goto okret;
c = args.unicode()[pos++];
} while (c.isSpace());
QString cret;
if ((flags & TildeExpand) && c == QLatin1Char('~')) {
int opos = pos;
for (; ; pos++) {
if (pos >= args.length())
break;
c = args.unicode()[pos];
if (c == QLatin1Char('/') || c.isSpace())
break;
if (isQuoteMeta( c )) {
pos = opos;
c = QLatin1Char('~');
goto notilde;
}
if ((flags & AbortOnMeta) && isMeta( c ))
goto metaerr;
}
QString ccret = homeDir( args.mid(opos, pos-opos) );
if (ccret.isEmpty()) {
pos = opos;
c = QLatin1Char('~');
goto notilde;
}
if (pos >= args.length()) {
ret += ccret;
goto okret;
}
pos++;
if (c.isSpace()) {
ret += ccret;
firstword = false;
continue;
}
cret = ccret;
}
// before the notilde label, as a tilde does not match anyway
if (firstword) {
if (c == QLatin1Char('_') ||
(c >= QLatin1Char('A') && c <= QLatin1Char('Z')) ||
(c >= QLatin1Char('a') && c <= QLatin1Char('z')))
{
int pos2 = pos;
QChar cc;
do {
if (pos2 >= args.length()) {
// Exactly one word
ret += args.mid(pos - 1);
goto okret;
}
cc = args.unicode()[pos2++];
} while (cc == QLatin1Char('_') ||
(cc >= QLatin1Char('A') && cc <= QLatin1Char('Z')) ||
(cc >= QLatin1Char('a') && cc <= QLatin1Char('z')) ||
(cc >= QLatin1Char('0') && cc <= QLatin1Char('9')));
if (cc == QLatin1Char('='))
goto metaerr;
}
}
notilde:
do {
if (c == QLatin1Char('\'')) {
int spos = pos;
do {
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
} while (c != QLatin1Char('\''));
cret += args.mid(spos, pos-spos-1);
} else if (c == QLatin1Char('"')) {
for (;;) {
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
if (c == QLatin1Char('"'))
break;
if (c == QLatin1Char('\\')) {
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
if (c != QLatin1Char('"') &&
c != QLatin1Char('\\') &&
!((flags & AbortOnMeta) &&
(c == QLatin1Char('$') ||
c == QLatin1Char('`'))))
cret += QLatin1Char('\\');
} else if ((flags & AbortOnMeta) &&
(c == QLatin1Char('$') ||
c == QLatin1Char('`')))
goto metaerr;
cret += c;
}
} else if (c == QLatin1Char('$') && pos < args.length() &&
args.unicode()[pos] == QLatin1Char('\'')) {
pos++;
for (;;) {
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
if (c == QLatin1Char('\''))
break;
if (c == QLatin1Char('\\')) {
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
- switch (c.toAscii()) {
+ switch (c.toLatin1()) {
case 'a': cret += QLatin1Char('\a'); break;
case 'b': cret += QLatin1Char('\b'); break;
case 'e': cret += QLatin1Char('\033'); break;
case 'f': cret += QLatin1Char('\f'); break;
case 'n': cret += QLatin1Char('\n'); break;
case 'r': cret += QLatin1Char('\r'); break;
case 't': cret += QLatin1Char('\t'); break;
case '\\': cret += QLatin1Char('\\'); break;
case '\'': cret += QLatin1Char('\''); break;
case 'c':
if (pos >= args.length())
goto quoteerr;
- cret += args.unicode()[pos++].toAscii() & 31;
+ cret += args.unicode()[pos++].toLatin1() & 31;
break;
case 'x':
{
if (pos >= args.length())
goto quoteerr;
int hv = fromHex( args.unicode()[pos++] );
if (hv < 0)
goto quoteerr;
if (pos < args.length()) {
int hhv = fromHex( args.unicode()[pos] );
if (hhv > 0) {
hv = hv * 16 + hhv;
pos++;
}
cret += QChar( hv );
}
break;
}
default:
- if (c.toAscii() >= '0' && c.toAscii() <= '7') {
- char cAscii = c.toAscii();
+ if (c.toLatin1() >= '0' && c.toAscii() <= '7') {
+ char cAscii = c.toLatin1();
int hv = cAscii - '0';
for (int i = 0; i < 2; i++) {
if (pos >= args.length())
break;
c = args.unicode()[pos];
- if (c.toAscii() < '0' || c.toAscii() > '7')
+ if (c.toLatin1() < '0' || c.toAscii() > '7')
break;
- hv = hv * 8 + (c.toAscii() - '0');
+ hv = hv * 8 + (c.toLatin1() - '0');
pos++;
}
cret += QChar( hv );
} else {
cret += QLatin1Char('\\');
cret += c;
}
break;
}
} else
cret += c;
}
} else {
if (c == QLatin1Char('\\')) {
if (pos >= args.length())
goto quoteerr;
c = args.unicode()[pos++];
} else if ((flags & AbortOnMeta) && isMeta( c ))
goto metaerr;
cret += c;
}
if (pos >= args.length())
break;
c = args.unicode()[pos++];
} while (!c.isSpace());
ret += cret;
firstword = false;
}
okret:
if (err)
*err = NoError;
return ret;
quoteerr:
if (err)
*err = BadQuoting;
return QStringList();
metaerr:
if (err)
*err = FoundMeta;
return QStringList();
}
inline static bool isSpecial( QChar cUnicode )
{
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
uint c = cUnicode.unicode ();
return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
}
QString KShell::quoteArg( const QString &arg )
{
if (!arg.length())
return QString::fromLatin1("''");
for (int i = 0; i < arg.length(); i++)
if (isSpecial( arg.unicode()[i] )) {
QChar q( QLatin1Char('\'') );
return QString( arg ).replace( q, QLatin1String("'\\''") ).prepend( q ).append( q );
}
return arg;
}
diff --git a/kdecore/util/kuser_win.cpp b/kdecore/util/kuser_win.cpp
index f7b0c43947..cae926dc5c 100644
--- a/kdecore/util/kuser_win.cpp
+++ b/kdecore/util/kuser_win.cpp
@@ -1,474 +1,474 @@
/*
* KUser - represent a user/account (Windows)
* Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de>
*
* 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 "kuser.h"
#include <QtCore/QMutableStringListIterator>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <windows.h>
#include <lm.h>
#include <sddl.h>
class KUser::Private : public KShared
{
public:
PUSER_INFO_11 userInfo;
PSID sid;
Private() : userInfo(0), sid(0) {}
Private(PUSER_INFO_11 userInfo_, PSID sid_ = 0) : userInfo(userInfo_) {}
Private(const QString &name, PSID sid_ = 0) : userInfo(0), sid(NULL)
{
LPBYTE servername;
NET_API_STATUS status = NetGetAnyDCName(0, 0, &servername);
if (status != NERR_Success)
{
servername = NULL;
}
if (NetUserGetInfo((LPCWSTR) servername, (LPCWSTR) name.utf16(), 11, (LPBYTE *) &userInfo) != NERR_Success) {
goto error;
}
if (servername)
{
NetApiBufferFree(servername);
servername = 0;
}
if (!sid_) {
DWORD size = 0;
SID_NAME_USE nameuse;
DWORD cchReferencedDomainName = 0;
WCHAR* referencedDomainName = NULL;
// the following line definitely fails:
// both the sizes for sid and for referencedDomainName are Null
// the needed sizes are set in size and cchReferencedDomainName
LookupAccountNameW(NULL, (LPCWSTR) name.utf16(), sid, &size, referencedDomainName, &cchReferencedDomainName, &nameuse);
sid = (PSID) new SID[size + 1];
referencedDomainName = new WCHAR[cchReferencedDomainName + 1];
if (!LookupAccountNameW(NULL, (LPCWSTR) name.utf16(), sid, &size, referencedDomainName, &cchReferencedDomainName, &nameuse)) {
delete[] referencedDomainName;
goto error;
}
// if you want to see both the DomainName and the sid of the user
// uncomment the following lines
/* LPWSTR sidstring;
ConvertSidToStringSidW(sid, &sidstring);
qDebug() << QString("\\\\") + QString::fromUtf16(reinterpret_cast<ushort*>(referencedDomainName)) + \
"\\" + name + "(" + QString::fromUtf16(reinterpret_cast<ushort*>(sidstring)) + ")";
LocalFree(sidstring);*/
delete[] referencedDomainName;
}
else {
if (!IsValidSid(sid_))
goto error;
DWORD sidlength = GetLengthSid(sid_);
sid = (PSID) new BYTE[sidlength];
if (!CopySid(sidlength, sid, sid_))
goto error;
}
return;
error:
delete[] sid;
sid = 0;
if (userInfo) {
NetApiBufferFree(userInfo);
userInfo = 0;
}
if (servername)
{
NetApiBufferFree(servername);
servername = 0;
}
}
~Private()
{
if (userInfo)
NetApiBufferFree(userInfo);
delete[] sid;
}
};
KUser::KUser(UIDMode mode)
: d(0)
{
Q_UNUSED(mode)
DWORD bufferLen = UNLEN + 1;
ushort buffer[UNLEN + 1];
if (GetUserNameW((LPWSTR) buffer, &bufferLen))
d = new Private(QString::fromUtf16(buffer));
}
KUser::KUser(K_UID uid)
: d(0)
{
DWORD bufferLen = UNLEN + 1;
ushort buffer[UNLEN + 1];
SID_NAME_USE eUse;
if (LookupAccountSidW(NULL, uid, (LPWSTR) buffer, &bufferLen, NULL, NULL, &eUse))
d = new Private(QString::fromUtf16(buffer), uid);
}
KUser::KUser(const QString &name)
: d(new Private(name))
{
}
KUser::KUser(const char *name)
:d(new Private(QString::fromLocal8Bit(name)))
{
}
KUser::KUser(const KUser &user)
: d(user.d)
{
}
KUser &KUser::operator=(const KUser &user)
{
d = user.d;
return *this;
}
bool KUser::operator==(const KUser &user) const
{
if (!isValid() || !user.isValid())
return false;
return EqualSid(d->sid, user.d->sid);
}
bool KUser::operator !=(const KUser &user) const
{
return !operator==(user);
}
bool KUser::isValid() const
{
return d->userInfo != 0 && d->sid != 0;
}
bool KUser::isSuperUser() const
{
return d->userInfo && d->userInfo->usri11_priv == USER_PRIV_ADMIN;
}
QString KUser::loginName() const
{
return (d->userInfo ? QString::fromUtf16((ushort *) d->userInfo->usri11_name) : QString());
}
#ifndef KDE_NO_DEPRECATED
QString KUser::fullName() const
{
return (d->userInfo ? QString::fromUtf16((ushort *) d->userInfo->usri11_full_name) : QString());
}
#endif
QString KUser::homeDir() const
{
return QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv("USERPROFILE")));
}
QString KUser::faceIconPath() const
{
// FIXME: this needs to be adapted to windows systems (BC changes)
return QString();
}
QString KUser::shell() const
{
- return QString::fromAscii("cmd.exe");
+ return QString::fromLatin1("cmd.exe");
}
QList<KUserGroup> KUser::groups() const
{
QList<KUserGroup> result;
Q_FOREACH (const QString &name, groupNames()) {
result.append(KUserGroup(name));
}
return result;
}
QStringList KUser::groupNames() const
{
QStringList result;
if (!d->userInfo) {
return result;
}
PGROUP_USERS_INFO_0 pGroups = NULL;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
NET_API_STATUS nStatus;
nStatus = NetUserGetGroups(NULL, d->userInfo->usri11_name, 0, (LPBYTE *) &pGroups, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries);
if (nStatus == NERR_Success) {
for (DWORD i = 0; i < dwEntriesRead; ++i) {
result.append(QString::fromUtf16((ushort *) pGroups[i].grui0_name));
}
}
if (pGroups) {
NetApiBufferFree(pGroups);
}
return result;
}
K_UID KUser::uid() const
{
return d->sid;
}
QVariant KUser::property(UserProperty which) const
{
if (which == FullName)
return QVariant(d->userInfo ? QString::fromUtf16((ushort *) d->userInfo->usri11_full_name) : QString());
return QVariant();
}
QList<KUser> KUser::allUsers()
{
QList<KUser> result;
NET_API_STATUS nStatus;
PUSER_INFO_11 pUser = NULL;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
DWORD dwResumeHandle = 0;
KUser tmp;
do {
nStatus = NetUserEnum(NULL, 11, 0, (LPBYTE*) &pUser, 1, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
if ((nStatus == NERR_Success || nStatus == ERROR_MORE_DATA) && dwEntriesRead > 0) {
tmp.d = new Private(pUser);
result.append(tmp);
}
} while (nStatus == ERROR_MORE_DATA);
return result;
}
QStringList KUser::allUserNames()
{
QStringList result;
NET_API_STATUS nStatus;
PUSER_INFO_0 pUsers = NULL;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
nStatus = NetUserEnum(NULL, 0, 0, (LPBYTE*) &pUsers, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, NULL);
if (nStatus == NERR_Success) {
for (DWORD i = 0; i < dwEntriesRead; ++i) {
result.append(QString::fromUtf16((ushort *) pUsers[i].usri0_name));
}
}
if (pUsers) {
NetApiBufferFree(pUsers);
}
return result;
}
KUser::~KUser()
{
}
class KUserGroup::Private : public KShared
{
public:
PGROUP_INFO_0 groupInfo;
Private() : groupInfo(NULL) {}
Private(PGROUP_INFO_0 groupInfo_) : groupInfo(groupInfo_) {}
Private(const QString &Name) : groupInfo(NULL)
{
NetGroupGetInfo(NULL, (PCWSTR) Name.utf16(), 0, (PBYTE *) &groupInfo);
}
~Private()
{
if (groupInfo) {
NetApiBufferFree(groupInfo);
}
}
};
KUserGroup::KUserGroup(const QString &_name)
: d(new Private(_name))
{
}
KUserGroup::KUserGroup(const char *_name)
: d(new Private(QLatin1String(_name)))
{
}
KUserGroup::KUserGroup(const KUserGroup &group)
: d(group.d)
{
}
KUserGroup& KUserGroup::operator =(const KUserGroup &group)
{
d = group.d;
return *this;
}
bool KUserGroup::operator==(const KUserGroup &group) const
{
if (d->groupInfo == NULL || group.d->groupInfo == NULL) {
return false;
}
return wcscmp(d->groupInfo->grpi0_name, group.d->groupInfo->grpi0_name) == 0;
}
bool KUserGroup::operator!=(const KUserGroup &group) const
{
return !operator==(group);
}
bool KUserGroup::isValid() const
{
return d->groupInfo != NULL;
}
QString KUserGroup::name() const
{
if(d && d->groupInfo)
return QString::fromUtf16((ushort *) d->groupInfo->grpi0_name);
return QString();
}
QList<KUser> KUserGroup::users() const
{
QList<KUser> Result;
Q_FOREACH(const QString &user, userNames()) {
Result.append(KUser(user));
}
return Result;
}
QStringList KUserGroup::userNames() const
{
QStringList result;
if (!d->groupInfo) {
return result;
}
PGROUP_USERS_INFO_0 pUsers = NULL;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
NET_API_STATUS nStatus;
nStatus = NetGroupGetUsers(NULL, d->groupInfo->grpi0_name, 0, (LPBYTE *) &pUsers, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, NULL);
if (nStatus == NERR_Success) {
for (DWORD i = 0; i < dwEntriesRead; ++i) {
result.append(QString::fromUtf16((ushort *) pUsers[i].grui0_name));
}
}
if (pUsers) {
NetApiBufferFree(pUsers);
}
return result;
}
QList<KUserGroup> KUserGroup::allGroups()
{
QList<KUserGroup> result;
NET_API_STATUS nStatus;
PGROUP_INFO_0 pGroup=NULL;
DWORD dwEntriesRead=0;
DWORD dwTotalEntries=0;
DWORD dwResumeHandle=0;
KUserGroup tmp("");
do {
nStatus = NetGroupEnum(NULL, 0, (LPBYTE*) &pGroup, 1, &dwEntriesRead, &dwTotalEntries, (PDWORD_PTR)&dwResumeHandle);
if ((nStatus == NERR_Success || nStatus == ERROR_MORE_DATA) && dwEntriesRead > 0) {
tmp.d = new Private(pGroup);
result.append(tmp);
}
} while (nStatus == ERROR_MORE_DATA);
return result;
}
QStringList KUserGroup::allGroupNames()
{
QStringList result;
NET_API_STATUS nStatus;
PGROUP_INFO_0 pGroups=NULL;
DWORD dwEntriesRead=0;
DWORD dwTotalEntries=0;
nStatus = NetGroupEnum(NULL, 0, (LPBYTE*) &pGroups, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, NULL);
if (nStatus == NERR_Success) {
for (DWORD i = 0; i < dwEntriesRead; ++i) {
result.append(QString::fromUtf16((ushort *) pGroups[i].grpi0_name));
}
}
if (pGroups) {
NetApiBufferFree(pGroups);
}
return result;
}
KUserGroup::~KUserGroup()
{
}
diff --git a/kdecore/util/kuser_wince.cpp b/kdecore/util/kuser_wince.cpp
index e05ab33fbb..ebc0bdb9bc 100644
--- a/kdecore/util/kuser_wince.cpp
+++ b/kdecore/util/kuser_wince.cpp
@@ -1,238 +1,238 @@
/*
* KUser - represent a user/account (Windows)
* Copyright (C) 2010 Andreas Holzammer <andreas.holzammer@kdab.com>
*
* 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 "kuser.h"
#include <QStringList>
class KUser::Private : public KShared
{
};
KUser::KUser(UIDMode mode)
: d(0)
{
Q_UNUSED(mode)
}
KUser::KUser(K_UID uid)
: d(0)
{
Q_UNUSED(uid)
}
KUser::KUser(const QString &name)
: d(0)
{
}
KUser::KUser(const char *name)
:d(0)
{
}
KUser::KUser(const KUser &user)
: d(user.d)
{
}
KUser &KUser::operator=(const KUser &user)
{
d = user.d;
return *this;
}
bool KUser::operator==(const KUser &user) const
{
if (!isValid() || !user.isValid())
return false;
return true;
}
bool KUser::operator !=(const KUser &user) const
{
return !operator==(user);
}
bool KUser::isValid() const
{
return true;
}
bool KUser::isSuperUser() const
{
return true;
}
QString KUser::loginName() const
{
return QString("default");
}
#ifndef KDE_NO_DEPRECATED
QString KUser::fullName() const
{
return QString("default");
}
#endif
QString KUser::homeDir() const
{
return QString("\\Documents and Settings\\default");
}
QString KUser::faceIconPath() const
{
return QString();
}
QString KUser::shell() const
{
- return QString::fromAscii("cmd.exe");
+ return QString::fromLatin1("cmd.exe");
}
QList<KUserGroup> KUser::groups() const
{
return QList<KUserGroup>();
}
QStringList KUser::groupNames() const
{
return QStringList();
}
K_UID KUser::uid() const
{
return (K_UID)100;
}
QVariant KUser::property(UserProperty which) const
{
return QVariant();
}
QList<KUser> KUser::allUsers()
{
QList<KUser> result;
result.append(KUser());
return result;
}
QStringList KUser::allUserNames()
{
QStringList result;
result.append("wince");
return result;
}
KUser::~KUser()
{
}
class KUserGroup::Private : public KShared
{
};
KUserGroup::KUserGroup(const QString &_name)
: d(0)
{
}
KUserGroup::KUserGroup(const char *_name)
: d(0)
{
}
KUserGroup::KUserGroup(const KUserGroup &group)
: d(0)
{
}
KUserGroup& KUserGroup::operator =(const KUserGroup &group)
{
d = group.d;
return *this;
}
bool KUserGroup::operator==(const KUserGroup &group) const
{
return true;
}
bool KUserGroup::operator!=(const KUserGroup &group) const
{
return !operator==(group);
}
bool KUserGroup::isValid() const
{
return true;
}
QString KUserGroup::name() const
{
return QString("wince");
}
QList<KUser> KUserGroup::users() const
{
QList<KUser> Result;
Result.append(KUser());
return Result;
}
QStringList KUserGroup::userNames() const
{
QStringList result;
result.append("default");
return result;
}
QList<KUserGroup> KUserGroup::allGroups()
{
QList<KUserGroup> result;
result.append(KUserGroup(""));
return result;
}
QStringList KUserGroup::allGroupNames()
{
QStringList result;
result.append("wince");
return result;
}
KUserGroup::~KUserGroup()
{
}
diff --git a/kdesu/client.cpp b/kdesu/client.cpp
index 221a68ea75..bb4294511d 100644
--- a/kdesu/client.cpp
+++ b/kdesu/client.cpp
@@ -1,446 +1,446 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* This is free software; you can use this library under the GNU Library
* General Public License, version 2. See the file "COPYING.LIB" for the
* exact licensing terms.
*
* client.cpp: A client for kdesud.
*/
#include "client.h"
#include <config-kdesu.h>
#include <config-prefix.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <QtCore/QFile>
#include <QtCore/QRegExp>
#include <qstandardpaths.h>
#include <kdebug.h>
#include <ktoolinvocation.h>
#include <kde_file.h>
extern int kdesuDebugArea();
namespace KDESu {
class KDEsuClient::KDEsuClientPrivate {
public:
KDEsuClientPrivate() : sockfd(-1) {}
QString daemon;
int sockfd;
QByteArray sock;
};
#ifndef SUN_LEN
#define SUN_LEN(ptr) ((socklen_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
#endif
KDEsuClient::KDEsuClient()
:d(new KDEsuClientPrivate)
{
#if HAVE_X11
- QString display = QString::fromAscii(qgetenv("DISPLAY"));
+ QString display = QString::fromLatin1(qgetenv("DISPLAY"));
if (display.isEmpty())
{
kWarning(kdesuDebugArea()) << k_lineinfo << "$DISPLAY is not set.";
return;
}
// strip the screen number from the display
display.remove(QRegExp("\\.[0-9]+$"));
#else
QByteArray display("NODISPLAY");
#endif
d->sock = QFile::encodeName( QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QLatin1Char('/') + QString("kdesud_").append(display));
connect();
}
KDEsuClient::~KDEsuClient()
{
if (d->sockfd >= 0)
close(d->sockfd);
delete d;
}
int KDEsuClient::connect()
{
if (d->sockfd >= 0)
close(d->sockfd);
if (access(d->sock, R_OK|W_OK))
{
d->sockfd = -1;
return -1;
}
d->sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (d->sockfd < 0)
{
kWarning(kdesuDebugArea()) << k_lineinfo << "socket():" << perror;
return -1;
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, d->sock);
if (::connect(d->sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0)
{
kWarning(kdesuDebugArea()) << k_lineinfo << "connect():" << perror;
close(d->sockfd); d->sockfd = -1;
return -1;
}
#if !defined(SO_PEERCRED) || ! HAVE_STRUCT_UCRED
# if HAVE_GETPEEREID
uid_t euid;
gid_t egid;
// Security: if socket exists, we must own it
if (getpeereid(d->sockfd, &euid, &egid) == 0)
{
if (euid != getuid())
{
kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << euid;
close(d->sockfd); d->sockfd = -1;
return -1;
}
}
# else
# ifdef __GNUC__
# warning "Using sloppy security checks"
# endif
// We check the owner of the socket after we have connected.
// If the socket was somehow not ours an attacker will be able
// to delete it after we connect but shouldn't be able to
// create a socket that is owned by us.
KDE_struct_stat s;
if (KDE_lstat(d->sock, &s)!=0)
{
kWarning(kdesuDebugArea()) << "stat failed (" << d->sock << ")";
close(d->sockfd); d->sockfd = -1;
return -1;
}
if (s.st_uid != getuid())
{
kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << s.st_uid;
close(d->sockfd); d->sockfd = -1;
return -1;
}
if (!S_ISSOCK(s.st_mode))
{
kWarning(kdesuDebugArea()) << "socket is not a socket (" << d->sock << ")";
close(d->sockfd); d->sockfd = -1;
return -1;
}
# endif
#else
struct ucred cred;
socklen_t siz = sizeof(cred);
// Security: if socket exists, we must own it
if (getsockopt(d->sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0)
{
if (cred.uid != getuid())
{
kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << cred.uid;
close(d->sockfd); d->sockfd = -1;
return -1;
}
}
#endif
return 0;
}
QByteArray KDEsuClient::escape(const QByteArray &str)
{
QByteArray copy;
copy.reserve(str.size() + 4);
copy.append('"');
for (int i = 0; i < str.size(); i++) {
uchar c = str.at(i);
if (c < 32) {
copy.append('\\');
copy.append('^');
copy.append(c + '@');
} else {
if (c == '\\' || c == '"')
copy.append('\\');
copy.append(c);
}
}
copy.append('"');
return copy;
}
int KDEsuClient::command(const QByteArray &cmd, QByteArray *result)
{
if (d->sockfd < 0)
return -1;
if (send(d->sockfd, cmd, cmd.length(), 0) != (int) cmd.length())
return -1;
char buf[1024];
int nbytes = recv(d->sockfd, buf, 1023, 0);
if (nbytes <= 0)
{
kWarning(kdesuDebugArea()) << k_lineinfo << "no reply from daemon.";
return -1;
}
buf[nbytes] = '\000';
QByteArray reply = buf;
if (reply.left(2) != "OK")
return -1;
if (result)
*result = reply.mid(3, reply.length()-4);
return 0;
}
int KDEsuClient::setPass(const char *pass, int timeout)
{
QByteArray cmd = "PASS ";
cmd += escape(pass);
cmd += ' ';
cmd += QByteArray().setNum(timeout);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::exec(const QByteArray &prog, const QByteArray &user, const QByteArray &options, const QList<QByteArray> &env)
{
QByteArray cmd;
cmd = "EXEC ";
cmd += escape(prog);
cmd += ' ';
cmd += escape(user);
if (!options.isEmpty() || !env.isEmpty())
{
cmd += ' ';
cmd += escape(options);
for (int i = 0; i < env.count(); ++i)
{
cmd += ' ';
cmd += escape(env.at(i));
}
}
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setHost(const QByteArray &host)
{
QByteArray cmd = "HOST ";
cmd += escape(host);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setPriority(int prio)
{
QByteArray cmd;
cmd += "PRIO ";
cmd += QByteArray::number(prio);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setScheduler(int sched)
{
QByteArray cmd;
cmd += "SCHD ";
cmd += QByteArray::number(sched);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::delCommand(const QByteArray &key, const QByteArray &user)
{
QByteArray cmd = "DEL ";
cmd += escape(key);
cmd += ' ';
cmd += escape(user);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setVar(const QByteArray &key, const QByteArray &value, int timeout,
const QByteArray &group)
{
QByteArray cmd = "SET ";
cmd += escape(key);
cmd += ' ';
cmd += escape(value);
cmd += ' ';
cmd += escape(group);
cmd += ' ';
cmd += QByteArray().setNum(timeout);
cmd += '\n';
return command(cmd);
}
QByteArray KDEsuClient::getVar(const QByteArray &key)
{
QByteArray cmd = "GET ";
cmd += escape(key);
cmd += '\n';
QByteArray reply;
command(cmd, &reply);
return reply;
}
QList<QByteArray> KDEsuClient::getKeys(const QByteArray &group)
{
QByteArray cmd = "GETK ";
cmd += escape(group);
cmd += '\n';
QByteArray reply;
command(cmd, &reply);
int index=0, pos;
QList<QByteArray> list;
if( !reply.isEmpty() )
{
// kDebug(kdesuDebugArea()) << "Found a matching entry:" << reply;
while (1)
{
pos = reply.indexOf( '\007', index );
if( pos == -1 )
{
if( index == 0 )
list.append( reply );
else
list.append( reply.mid(index) );
break;
}
else
{
list.append( reply.mid(index, pos-index) );
}
index = pos+1;
}
}
return list;
}
bool KDEsuClient::findGroup(const QByteArray &group)
{
QByteArray cmd = "CHKG ";
cmd += escape(group);
cmd += '\n';
if( command(cmd) == -1 )
return false;
return true;
}
int KDEsuClient::delVar(const QByteArray &key)
{
QByteArray cmd = "DELV ";
cmd += escape(key);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::delGroup(const QByteArray &group)
{
QByteArray cmd = "DELG ";
cmd += escape(group);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::delVars(const QByteArray &special_key)
{
QByteArray cmd = "DELS ";
cmd += escape(special_key);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::ping()
{
return command("PING\n");
}
int KDEsuClient::exitCode()
{
QByteArray result;
if (command("EXIT\n", &result) != 0)
return -1;
return result.toInt();
}
int KDEsuClient::stopServer()
{
return command("STOP\n");
}
static QString findDaemon()
{
QString daemon = CMAKE_INSTALL_PREFIX "/" LIBEXEC_INSTALL_DIR "/kdesud";
if (!QFile::exists(daemon)) { // if not in libexec, find it in PATH
daemon = QStandardPaths::findExecutable("kdesud");
if (daemon.isEmpty()) {
kWarning(kdesuDebugArea()) << "kdesud daemon not found.";
}
}
return daemon;
}
bool KDEsuClient::isServerSGID()
{
if (d->daemon.isEmpty())
d->daemon = findDaemon();
if (d->daemon.isEmpty())
return false;
KDE_struct_stat sbuf;
if (KDE::stat(d->daemon, &sbuf) < 0)
{
kWarning(kdesuDebugArea()) << k_lineinfo << "stat():" << perror;
return false;
}
return (sbuf.st_mode & S_ISGID);
}
int KDEsuClient::startServer()
{
if (d->daemon.isEmpty())
d->daemon = findDaemon();
if (d->daemon.isEmpty())
return -1;
if (!isServerSGID()) {
kWarning(kdesuDebugArea()) << k_lineinfo << "kdesud not setgid!";
}
// kdesud only forks to the background after it is accepting
// connections.
// We start it via kdeinit to make sure that it doesn't inherit
// any fd's from the parent process.
int ret = KToolInvocation::kdeinitExecWait(d->daemon);
connect();
return ret;
}
}
diff --git a/kdeui/dialogs/kassistantdialog.cpp b/kdeui/dialogs/kassistantdialog.cpp
index 090d391bee..9869db2ae2 100644
--- a/kdeui/dialogs/kassistantdialog.cpp
+++ b/kdeui/dialogs/kassistantdialog.cpp
@@ -1,169 +1,169 @@
/* This file is part of the KDE libraries
Copyright (C) 2006 Olivier Goffart <ogoffart at 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 version 2 as published by the Free Software Foundation.
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 "kassistantdialog.h"
#include <kstandardguiitem.h>
#include <klocalizedstring.h>
#include <kdebug.h>
#include <QIcon>
#include <kiconloader.h>
#include <QHash>
class KAssistantDialog::Private
{
public:
Private(KAssistantDialog *q)
: q(q)
{
}
KAssistantDialog *q;
QHash<KPageWidgetItem*, bool> valid;
QHash<KPageWidgetItem*, bool> appropriate;
KPageWidgetModel *pageModel;
void init();
void _k_slotUpdateButtons();
QModelIndex getNext(QModelIndex nextIndex)
{
QModelIndex currentIndex;
do {
currentIndex=nextIndex;
nextIndex=currentIndex.child(0, 0);
if (!nextIndex.isValid())
nextIndex=currentIndex.sibling(currentIndex.row() + 1, 0);
} while (nextIndex.isValid() && !appropriate.value(pageModel->item(nextIndex), true));
return nextIndex;
}
QModelIndex getPrevious(QModelIndex nextIndex)
{
QModelIndex currentIndex;
do {
currentIndex=nextIndex;
nextIndex=currentIndex.sibling(currentIndex.row() - 1, 0);
if (!nextIndex.isValid())
nextIndex=currentIndex.parent();
} while (nextIndex.isValid() && !appropriate.value(pageModel->item(nextIndex), true));
return nextIndex;
}
};
-KAssistantDialog::KAssistantDialog(QWidget * parent, Qt::WFlags flags)
+KAssistantDialog::KAssistantDialog(QWidget * parent, Qt::WindowFlags flags)
: KPageDialog(parent, flags), d(new Private(this))
{
d->init();
//workaround to get the page model
KPageWidget *pagewidget=findChild<KPageWidget*>();
Q_ASSERT(pagewidget);
d->pageModel=static_cast<KPageWidgetModel*>(pagewidget->model());
}
-KAssistantDialog::KAssistantDialog(KPageWidget *widget, QWidget *parent, Qt::WFlags flags)
+KAssistantDialog::KAssistantDialog(KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags)
: KPageDialog(widget, parent, flags), d(new Private(this))
{
d->init();
d->pageModel=static_cast<KPageWidgetModel*>(widget->model());
}
KAssistantDialog::~KAssistantDialog()
{
delete d;
}
void KAssistantDialog::Private::init()
{
q->setButtons(KDialog::Cancel | KDialog::User1 | KDialog::User2 | KDialog::User3 | KDialog::Help);
q->setButtonGuiItem( KDialog::User3, KStandardGuiItem::back(KStandardGuiItem::UseRTL) );
q->setButtonText( KDialog::User2, i18nc("Opposite to Back", "Next") );
q->setButtonText(KDialog::User1, i18n("Finish"));
q->setButtonIcon( KDialog::User2, KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon() );
q->setButtonIcon( KDialog::User1, KDE::icon("dialog-ok-apply") );
q->setDefaultButton(KDialog::User2);
q->setFaceType(KPageDialog::Plain);
q->connect(q, SIGNAL(user3Clicked()), q, SLOT(back()));
q->connect(q, SIGNAL(user2Clicked()), q, SLOT(next()));
q->connect(q, SIGNAL(user1Clicked()), q, SLOT(accept()));
q->connect(q, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), q, SLOT(_k_slotUpdateButtons()));
}
void KAssistantDialog::back()
{
QModelIndex nextIndex=d->getPrevious(d->pageModel->index(currentPage()));
if (nextIndex.isValid())
setCurrentPage(d->pageModel->item(nextIndex));
}
void KAssistantDialog::next()
{
QModelIndex nextIndex=d->getNext(d->pageModel->index(currentPage()));
if (nextIndex.isValid())
setCurrentPage(d->pageModel->item(nextIndex));
else if (isValid(currentPage()))
accept();
}
void KAssistantDialog::setValid(KPageWidgetItem * page, bool enable)
{
d->valid[page]=enable;
if (page == currentPage())
d->_k_slotUpdateButtons();
}
bool KAssistantDialog::isValid(KPageWidgetItem * page) const
{
return d->valid.value(page, true);
}
void KAssistantDialog::Private::_k_slotUpdateButtons()
{
QModelIndex currentIndex=pageModel->index(q->currentPage());
//change the caption of the next/finish button
QModelIndex nextIndex=getNext(currentIndex);
q->enableButton(KDialog::User1, !nextIndex.isValid() && q->isValid(q->currentPage()));
q->enableButton(KDialog::User2, nextIndex.isValid() && q->isValid(q->currentPage()));
q->setDefaultButton(nextIndex.isValid() ? KDialog::User2 : KDialog::User1);
//enable or disable the back button;
nextIndex=getPrevious(currentIndex);
q->enableButton(KDialog::User3, nextIndex.isValid());
}
void KAssistantDialog::showEvent(QShowEvent * event)
{
d->_k_slotUpdateButtons(); //called because last time that function was called is when the first page was added, so the next button show "finish"
KPageDialog::showEvent(event);
}
void KAssistantDialog::setAppropriate(KPageWidgetItem * page, bool appropriate)
{
d->appropriate[page]=appropriate;
d->_k_slotUpdateButtons();
}
bool KAssistantDialog::isAppropriate(KPageWidgetItem * page) const
{
return d->appropriate.value(page, true);
}
#include "moc_kassistantdialog.cpp"
diff --git a/kdeui/dialogs/kassistantdialog.h b/kdeui/dialogs/kassistantdialog.h
index 44e99578af..373e6cf066 100644
--- a/kdeui/dialogs/kassistantdialog.h
+++ b/kdeui/dialogs/kassistantdialog.h
@@ -1,150 +1,150 @@
/* This file is part of the KDE libraries
Copyright (C) 2006 Olivier Goffart <ogoffart at 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 version 2 as published by the Free Software Foundation.
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 KASSISTANTDIALOG_H
#define KASSISTANTDIALOG_H
#include <kpagedialog.h>
/**
* This class provides a framework for assistant dialogs.
*
* The use of this class is the same as KWizard in KDE3.
* You should use the word "assistant" instead of "wizard" both in the source
* and translatable strings.
*
* An assistant dialog consists of a sequence of pages.
* Its purpose is to guide the user (assist) through a process step by step.
* Assistant dialogs are useful for complex or infrequently occurring tasks
* that people may find difficult to learn or do.
* Sometimes a task requires too many input fields to fit them on a single dialog.
*
* KAssistantDialog provides page titles and displays Next, Back, Finish, Cancel,
* and Help push buttons, as appropriate to the current position in the page sequence.
* The Finish Button has the code KDialog::User1, The Next button is KDialog::User2
* and the Back button is KDialog::User3.
* The help button may be hidden using showButton(KDialog::Help, false)
*
* Create and populate dialog pages that inherit from QWidget and add them
* to the assistant dialog using addPage().
*
* The functions next() and back() are virtual and may be reimplemented to
* override the default actions of the next and back buttons.
*
* \image html kassistantdialog.png "KDE Assistant Dialog"
*
* @author Olivier Goffart <ogoffart at kde.org>
*/
class KDEUI_EXPORT KAssistantDialog : public KPageDialog
{
Q_OBJECT
public:
/**
* Construct a new assistant dialog with @p parent as parent.
* @param parent is the parent of the widget.
* @flags the window flags to give to the assistant dialog. The
* default of zero is usually what you want.
*/
- explicit KAssistantDialog(QWidget *parent=0, Qt::WFlags flags=0);
+ explicit KAssistantDialog(QWidget *parent=0, Qt::WindowFlags flags=0);
virtual ~KAssistantDialog();
/**
* Specify if the content of the page is valid, and if the next button may be enabled on this page.
* By default all pages are valid.
*
* This will disable or enable the next button on the specified page
*
* @param page the page on which the next button will be enabled/disable
* @param enable if true the next button will be enabled, if false it will be disabled
*/
void setValid(KPageWidgetItem* page, bool enable);
/**
* return if a page is valid
* @see setValid
* @param page the page to check the validity of
*/
bool isValid(KPageWidgetItem *page) const;
/**
* Specify whether a page is appropriate.
*
* A page is considered inappropriate if it should not be shown due to
* the contents of other pages making it inappropriate.
*
* A page which is inappropriate will not be shown.
*
* The last page in an assistant dialog should always be appropriate
* @param page the page to set as appropriate
* @param appropriate flag indicating the appropriateness of the page.
* If @p appropriate is true, then @p page is appropriate and will be
* shown in the assistant dialog. If false, @p page will not be shown.
*/
void setAppropriate(KPageWidgetItem *page, bool appropriate);
/**
* Check if a page is appropriate for use in the assistant dialog.
* @param page is the page to check the appropriateness of.
* @return true if @p page is appropriate, false if it is not
*/
bool isAppropriate(KPageWidgetItem *page) const;
public Q_SLOTS:
/**
* Called when the user clicks the Back button.
*
* This function will show the preceding relevant page in the sequence.
* Do nothing if the current page is the first page in the sequence.
*/
virtual void back();
/**
* Called when the user clicks the Next/Finish button.
*
* This function will show the next relevant page in the sequence.
* If the current page is the last page, it will call accept()
*/
virtual void next();
protected:
/**
* Construct an assistant dialog from a single widget.
* @param widget the widget to construct the dialog with
* @param parent the parent of the assistant dialog
* @flags the window flags to use when creating the widget. The default
* of zero is usually fine.
*
- * Calls the KPageDialog(KPageWidget *widget, QWidget *parent, Qt::WFlags flags) constructor
+ * Calls the KPageDialog(KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags) constructor
*/
- explicit KAssistantDialog(KPageWidget *widget, QWidget *parent=0, Qt::WFlags flags=0);
+ explicit KAssistantDialog(KPageWidget *widget, QWidget *parent=0, Qt::WindowFlags flags=0);
virtual void showEvent(QShowEvent * event);
private:
class Private;
Private * const d;
Q_PRIVATE_SLOT( d, void _k_slotUpdateButtons() )
Q_DISABLE_COPY( KAssistantDialog )
};
#endif
diff --git a/kdeui/paged/kpagedialog.cpp b/kdeui/paged/kpagedialog.cpp
index 7536ddcdc7..1f3705fdec 100644
--- a/kdeui/paged/kpagedialog.cpp
+++ b/kdeui/paged/kpagedialog.cpp
@@ -1,134 +1,134 @@
/*
* This file is part of the KDE Libraries
* Copyright (C) 1999-2001 Mirko Boehm (mirko@kde.org) and
* Espen Sand (espen@kde.org)
* Holger Freyther <freyther@kde.org>
* 2005-2006 Olivier Goffart <ogoffart at 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.
*
*/
#include "kpagedialog.h"
#include "kpagedialog_p.h"
#include <QTimer>
#include <QLayout>
-KPageDialog::KPageDialog( QWidget *parent, Qt::WFlags flags )
+KPageDialog::KPageDialog( QWidget *parent, Qt::WindowFlags flags )
: KDialog(*new KPageDialogPrivate, parent, flags)
{
Q_D(KPageDialog);
d->mPageWidget = new KPageWidget( this );
d->init();
}
-KPageDialog::KPageDialog( KPageWidget *widget, QWidget *parent, Qt::WFlags flags )
+KPageDialog::KPageDialog( KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags )
: KDialog(*new KPageDialogPrivate, parent, flags)
{
Q_D(KPageDialog);
Q_ASSERT(widget);
widget->setParent(this);
d->mPageWidget = widget;
d->init();
}
-KPageDialog::KPageDialog(KPageDialogPrivate &dd, KPageWidget *widget, QWidget *parent, Qt::WFlags flags)
+KPageDialog::KPageDialog(KPageDialogPrivate &dd, KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags)
: KDialog(dd, parent, flags)
{
Q_D(KPageDialog);
if (widget) {
widget->setParent(this);
d->mPageWidget = widget;
} else {
d->mPageWidget = new KPageWidget(this);
}
d->init();
}
KPageDialog::~KPageDialog()
{
}
void KPageDialog::setFaceType( FaceType faceType )
{
d_func()->mPageWidget->setFaceType(static_cast<KPageWidget::FaceType>(faceType));
}
KPageWidgetItem* KPageDialog::addPage( QWidget *widget, const QString &name )
{
return d_func()->mPageWidget->addPage(widget, name);
}
void KPageDialog::addPage( KPageWidgetItem *item )
{
d_func()->mPageWidget->addPage(item);
}
KPageWidgetItem* KPageDialog::insertPage( KPageWidgetItem *before, QWidget *widget, const QString &name )
{
return d_func()->mPageWidget->insertPage(before, widget, name);
}
void KPageDialog::insertPage( KPageWidgetItem *before, KPageWidgetItem *item )
{
d_func()->mPageWidget->insertPage(before, item);
}
KPageWidgetItem* KPageDialog::addSubPage( KPageWidgetItem *parent, QWidget *widget, const QString &name )
{
return d_func()->mPageWidget->addSubPage(parent, widget, name);
}
void KPageDialog::addSubPage( KPageWidgetItem *parent, KPageWidgetItem *item )
{
d_func()->mPageWidget->addSubPage(parent, item);
}
void KPageDialog::removePage( KPageWidgetItem *item )
{
d_func()->mPageWidget->removePage(item);
}
void KPageDialog::setCurrentPage( KPageWidgetItem *item )
{
d_func()->mPageWidget->setCurrentPage(item);
}
KPageWidgetItem* KPageDialog::currentPage() const
{
return d_func()->mPageWidget->currentPage();
}
KPageWidget* KPageDialog::pageWidget()
{
return d_func()->mPageWidget;
}
void KPageDialog::setPageWidget(KPageWidget *widget)
{
delete d_func()->mPageWidget;
d_func()->mPageWidget = widget;
d_func()->init();
}
const KPageWidget* KPageDialog::pageWidget() const
{
return d_func()->mPageWidget;
}
diff --git a/kdeui/paged/kpagedialog.h b/kdeui/paged/kpagedialog.h
index 8dc37736c3..5b638f96c5 100644
--- a/kdeui/paged/kpagedialog.h
+++ b/kdeui/paged/kpagedialog.h
@@ -1,235 +1,235 @@
/*
* This file is part of the KDE Libraries
* Copyright (C) 1999-2001 Mirko Boehm (mirko@kde.org) and
* Espen Sand (espen@kde.org)
* Holger Freyther <freyther@kde.org>
* 2005-2006 Olivier Goffart <ogoffart at kde.org>
* 2006 Tobias Koenig <tokoe@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 KPAGEDIALOG_H
#define KPAGEDIALOG_H
#include <kdialog.h>
#include <kpagewidget.h>
class KPageDialogPrivate;
/**
* @short A dialog base class which can handle multiple pages.
*
* This class provides a dialog base class which handles multiple
* pages and allows the user to switch between these pages in
* different ways.
*
* Currently, @p Auto, @p Plain, @p List, @p Tree and @p Tabbed face
* types are available (@see KPageView).
*
* <b>Example:</b>\n
*
* \code
* UrlDialog::UrlDialog( QWidget *parent )
* : KPageDialog( parent )
* {
* setFaceType( List );
*
* QLabel *label = new QLabel( "Test Page" );
* addPage( label, i18n( "My Test Page" ) );
*
* label = new QLabel( "Second Test Page" );
* KPageWidgetItem *page = new KPageWidgetItem( label, i18n( "My Second Test Page" ) );
* page->setHeader( i18n( "My header string" ) );
* page->setIcon( KDE::icon( "file" ) );
*
* addPage( page );
* }
* \endcode
*
* @author Tobias Koenig (tokoe@kde.org)
*/
class KDEUI_EXPORT KPageDialog : public KDialog
{
Q_OBJECT
Q_DECLARE_PRIVATE(KPageDialog)
public:
/**
* @li @p Auto - A dialog with a face based on the structure of the
* available pages.
* If only a single page is added, the dialog behaves like
* in @p Plain mode, with multiple pages without sub pages
* it behaves like in @p List mode and like in @p Tree mode
* otherwise.
* @li @p Plain - A normal dialog.
* @li @p List - A dialog with an icon list on the left side and a
* representation of the contents on the right side.
* @li @p Tree - A dialog with a tree on the left side and a
* representation of the contents on the right side.
* @li @p Tabbed - A dialog with a tab bar above the representation
* of the contents.
*/
enum FaceType
{
Auto = KPageView::Auto,
Plain = KPageView::Plain,
List = KPageView::List,
Tree = KPageView::Tree,
Tabbed = KPageView::Tabbed
};
public:
/**
* Creates a new page dialog.
*/
- explicit KPageDialog( QWidget *parent = 0, Qt::WFlags flags = 0 );
+ explicit KPageDialog( QWidget *parent = 0, Qt::WindowFlags flags = 0 );
/**
* Destroys the page dialog.
*/
~KPageDialog();
/**
* Sets the face type of the dialog.
*/
void setFaceType( FaceType faceType );
/**
* Adds a new top level page to the dialog.
*
* @param widget The widget of the page.
* @param name The name which is displayed in the navigation view.
*
* @returns The associated @see KPageWidgetItem.
*/
KPageWidgetItem* addPage( QWidget *widget, const QString &name );
/**
* Adds a new top level page to the dialog.
*
* @param item The @see KPageWidgetItem which describes the page.
*/
void addPage( KPageWidgetItem *item );
/**
* Inserts a new page in the dialog.
*
* @param before The new page will be insert before this @see KPageWidgetItem
* on the same level in hierarchy.
* @param widget The widget of the page.
* @param name The name which is displayed in the navigation view.
*
* @returns The associated @see KPageWidgetItem.
*/
KPageWidgetItem* insertPage( KPageWidgetItem *before, QWidget *widget, const QString &name );
/**
* Inserts a new page in the dialog.
*
* @param before The new page will be insert before this @see KPageWidgetItem
* on the same level in hierarchy.
*
* @param item The @see KPageWidgetItem which describes the page.
*/
void insertPage( KPageWidgetItem *before, KPageWidgetItem *item );
/**
* Inserts a new sub page in the dialog.
*
* @param parent The new page will be insert as child of this @see KPageWidgetItem.
* @param widget The widget of the page.
* @param name The name which is displayed in the navigation view.
*
* @returns The associated @see KPageWidgetItem.
*/
KPageWidgetItem* addSubPage( KPageWidgetItem *parent, QWidget *widget, const QString &name );
/**
* Inserts a new sub page in the dialog.
*
* @param parent The new page will be insert as child of this @see KPageWidgetItem.
*
* @param item The @see KPageWidgetItem which describes the page.
*/
void addSubPage( KPageWidgetItem *parent, KPageWidgetItem *item );
/**
* Removes the page associated with the given @see KPageWidgetItem.
*/
void removePage( KPageWidgetItem *item );
/**
* Sets the page which is associated with the given @see KPageWidgetItem to
* be the current page and emits the currentPageChanged() signal.
*/
void setCurrentPage( KPageWidgetItem *item );
/**
* Returns the @see KPageWidgetItem for the current page or 0 if there is no
* current page.
*/
KPageWidgetItem* currentPage() const;
Q_SIGNALS:
/**
* This signal is emitted whenever the current page has changed.
*
* @param item The new current page or 0 if no current page is available.
*/
void currentPageChanged( KPageWidgetItem *current, KPageWidgetItem *before );
/**
* This signal is emitted whenever a page has been removed.
*
* @param page The page which has been removed
**/
void pageRemoved( KPageWidgetItem *page );
protected:
/**
* This constructor can be used by subclasses to provide a custom page widget.
*
* \param widget The KPageWidget object will be reparented to this object, so you can create
* it without parent and you are not allowed to delete it.
*/
- KPageDialog(KPageWidget *widget, QWidget *parent, Qt::WFlags flags = 0);
- KPageDialog(KPageDialogPrivate &dd, KPageWidget *widget, QWidget *parent, Qt::WFlags flags = 0);
+ KPageDialog(KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags = 0);
+ KPageDialog(KPageDialogPrivate &dd, KPageWidget *widget, QWidget *parent, Qt::WindowFlags flags = 0);
/**
* Returns the page widget of the dialog or 0 if no page widget is set.
*/
KPageWidget *pageWidget();
/**
* Returns the page widget of the dialog or 0 if no page widget is set.
*/
const KPageWidget *pageWidget() const;
/**
* Set the page widget of the dialog.
*
* @note the previous pageWidget will be deleted.
*
* @param widget The KPageWidget object will be reparented to this object, so you can create
* it without parent and you are not allowed to delete it.
*/
void setPageWidget(KPageWidget *widget);
};
#endif
diff --git a/kdeui/tests/kcolorcollectiontest.cpp b/kdeui/tests/kcolorcollectiontest.cpp
index 81a96bd912..b199a3a668 100644
--- a/kdeui/tests/kcolorcollectiontest.cpp
+++ b/kdeui/tests/kcolorcollectiontest.cpp
@@ -1,38 +1,38 @@
#include <QWidget>
#include <QtCore/QTimer>
#include <stdlib.h>
#include "kcolorcollection.h"
#include <stdio.h>
#include <QApplication>
#include <QtCore/QStringList>
int main( int argc, char **argv )
{
QApplication::setApplicationName("KColorCollectionTest");
QApplication a(argc, argv);
QStringList collections = KColorCollection::installedCollections();
for(QStringList::ConstIterator it = collections.constBegin();
it != collections.constEnd(); ++it)
{
- printf("Palette = %s\n", (*it).toAscii().constData());
+ printf("Palette = %s\n", (*it).toLatin1().constData());
KColorCollection myColorCollection = KColorCollection(*it);
- printf("Palette Name = \"%s\"\n", myColorCollection.name().toAscii().constData());
- printf("Description:\n\"%s\"\n", myColorCollection.description().toAscii().constData());
+ printf("Palette Name = \"%s\"\n", myColorCollection.name().toLatin1().constData());
+ printf("Description:\n\"%s\"\n", myColorCollection.description().toLatin1().constData());
printf("Nr of Colors = %d\n", myColorCollection.count());
for(int i = 0; i < myColorCollection.count(); i++)
{
int r,g,b;
myColorCollection.color(i).getRgb(&r, &g, &b);
printf("#%d Name = \"%s\" #%02x%02x%02x\n",
- i, myColorCollection.name(i).toAscii().constData(), r,g,b);
+ i, myColorCollection.name(i).toLatin1().constData(), r,g,b);
}
}
}
diff --git a/kdeui/tests/knewpassworddialogtest.cpp b/kdeui/tests/knewpassworddialogtest.cpp
index 93ebaee585..1ddee7d06b 100644
--- a/kdeui/tests/knewpassworddialogtest.cpp
+++ b/kdeui/tests/knewpassworddialogtest.cpp
@@ -1,47 +1,47 @@
/* This file is part of the KDE libraries
Copyright (C) 2007 Olivier Goffart <ogoffart at 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.
*/
#include <qapplication.h>
#include <knewpassworddialog.h>
#include <klocalizedstring.h>
#include <iostream>
int main( int argc, char *argv[] )
{
QApplication::setColorSpec( QApplication::CustomColor );
QApplication::setApplicationName("KNewPasswordDialogTest");
QApplication a(argc, argv);
KNewPasswordDialog dlg;
dlg.setPrompt(i18n("Enter a password for the test"));
if( dlg.exec() )
{
- std::cout << "Entered password: " << (const char*)dlg.password().toAscii() << std::endl;
+ std::cout << "Entered password: " << (const char*)dlg.password().toLatin1() << std::endl;
return 0;
}
else
{
std::cout << "No password" << std::endl;
return -1;
}
}
diff --git a/kdeui/tests/kpassworddialogtest.cpp b/kdeui/tests/kpassworddialogtest.cpp
index db967e733b..865efda90f 100644
--- a/kdeui/tests/kpassworddialogtest.cpp
+++ b/kdeui/tests/kpassworddialogtest.cpp
@@ -1,96 +1,96 @@
/* This file is part of the KDE libraries
Copyright (C) 2007 Olivier Goffart <ogoffart at 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.
*/
#include <QApplication>
#include <kpassworddialog.h>
#include <klocalizedstring.h>
#include <iostream>
int main( int argc, char *argv[] )
{
QApplication::setColorSpec( QApplication::CustomColor );
QApplication::setApplicationName("KNewPasswordDialogTest");
QApplication a(argc, argv);
//step 1 simple password
{
KPasswordDialog dlg(0, KPasswordDialog::ShowKeepPassword);
dlg.setPrompt(i18n("This is a long prompt line. It is important it to be long so we can test the dialog does not get broken because of multiline labels. Please enter a password:"));
dlg.addCommentLine(i18n("This is a rather large left comment line") , i18n("Right part of the comment line has to be long too so be test the layouting works really ok. Please visit http://www.kde.org"));
if( dlg.exec() )
{
- std::cout << "Entered password: " << (const char*)dlg.password().toAscii() << std::endl;
+ std::cout << "Entered password: " << (const char*)dlg.password().toLatin1() << std::endl;
}
else
{
std::cout << "No password" << std::endl;
return -1;
}
}
//step 2 readonly username
{
KPasswordDialog dlg(0, KPasswordDialog::ShowUsernameLine | KPasswordDialog::UsernameReadOnly);
dlg.setPrompt(i18n("Enter a password for the test"));
dlg.setUsername("konqui");
dlg.addCommentLine( i18n("Site") , i18n("http://www.kde.org") );
if( dlg.exec() )
{
- std::cout << "Entered password: " << (const char*)dlg.password().toAscii() << std::endl;
+ std::cout << "Entered password: " << (const char*)dlg.password().toLatin1() << std::endl;
}
else
{
std::cout << "No password" << std::endl;
return -1;
}
}
//step 3 with some username preset
{
KPasswordDialog dlg(0, KPasswordDialog::ShowUsernameLine);
dlg.setPrompt(i18n("Enter a password for the test"));
QMap<QString,QString> logins;
logins.insert("konqui" , "foo");
logins.insert("watson" , "bar");
logins.insert("ogoffart" , "");
dlg.setKnownLogins(logins);
if( dlg.exec() )
{
- std::cout << "Entered password: " << (const char*)dlg.password().toAscii() << " for username " << (const char*)dlg.username().toAscii() <<std::endl;
+ std::cout << "Entered password: " << (const char*)dlg.password().toLatin1() << " for username " << (const char*)dlg.username().toAscii() <<std::endl;
}
else
{
std::cout << "No password" << std::endl;
return -1;
}
}
return 0;
}
diff --git a/kdeui/util/kpassivepopup.cpp b/kdeui/util/kpassivepopup.cpp
index 588e1a4bf5..fce36fcd09 100644
--- a/kdeui/util/kpassivepopup.cpp
+++ b/kdeui/util/kpassivepopup.cpp
@@ -1,649 +1,649 @@
/*
* Copyright (C) 2001-2006 by Richard Moore <rich@kde.org>
* Copyright (C) 2004-2005 by Sascha Cunz <sascha.cunz@tiscali.de>
*
* 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,
*/
#include "kpassivepopup.h"
// Qt
#include <QApplication>
#include <QBitmap>
#include <QLabel>
#include <QLayout>
#include <QBoxLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QDesktopWidget>
#include <QPolygonF>
#include <QTimer>
#include <QToolTip>
#include <QSystemTrayIcon>
#include <kdebug.h>
#include <kdialog.h>
#include <kglobalsettings.h>
#include <kconfig.h>
#include <config-kdeui.h>
#if HAVE_X11
#include <qx11info_x11.h>
#include <netwm.h>
#endif
static const int DEFAULT_POPUP_TYPE = KPassivePopup::Boxed;
static const int DEFAULT_POPUP_TIME = 6*1000;
static const Qt::WindowFlags POPUP_FLAGS = Qt::Tool | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint;
class KPassivePopup::Private
{
public:
Private()
: popupStyle( DEFAULT_POPUP_TYPE ),
msgView(0),
topLayout(0),
hideDelay( DEFAULT_POPUP_TIME ),
hideTimer(0),
autoDelete( false )
{
}
int popupStyle;
QPolygon surround;
QPoint anchor;
QPoint fixedPosition;
WId window;
QWidget *msgView;
QBoxLayout *topLayout;
int hideDelay;
QTimer *hideTimer;
QLabel *ttlIcon;
QLabel *ttl;
QLabel *msg;
bool autoDelete;
};
-KPassivePopup::KPassivePopup( QWidget *parent, Qt::WFlags f )
+KPassivePopup::KPassivePopup( QWidget *parent, Qt::WindowFlags f )
: QFrame( 0, f ? f : POPUP_FLAGS ),
d(new Private())
{
init( parent ? parent->effectiveWinId() : 0L );
}
KPassivePopup::KPassivePopup( WId win )
: QFrame( 0 ),
d(new Private())
{
init( win );
}
#if 0 // These break macos and win32 where the definition of WId makes them ambiguous
-KPassivePopup::KPassivePopup( int popupStyle, QWidget *parent, Qt::WFlags f )
+KPassivePopup::KPassivePopup( int popupStyle, QWidget *parent, Qt::WindowFlags f )
: QFrame( 0, f ? f : POPUP_FLAGS ),
d(new Private())
{
init( parent ? parent->winId() : 0L );
setPopupStyle( popupStyle );
}
-KPassivePopup::KPassivePopup( int popupStyle, WId win, Qt::WFlags f )
+KPassivePopup::KPassivePopup( int popupStyle, WId win, Qt::WindowFlags f )
: QFrame( 0, f ? f : POPUP_FLAGS ),
d(new Private())
{
init( win );
setPopupStyle( popupStyle );
}
#endif
void KPassivePopup::init( WId window )
{
d->window = window;
d->hideTimer = new QTimer( this );
setWindowFlags( POPUP_FLAGS );
setFrameStyle( QFrame::Box| QFrame::Plain );
setLineWidth( 2 );
if( d->popupStyle == Boxed )
{
setFrameStyle( QFrame::Box| QFrame::Plain );
setLineWidth( 2 );
}
else if( d->popupStyle == Balloon )
{
setPalette(QToolTip::palette());
//XXX dead ? setAutoMask(true);
}
connect( d->hideTimer, SIGNAL(timeout()), SLOT(hide()) );
connect( this, SIGNAL(clicked()), SLOT(hide()) );
}
KPassivePopup::~KPassivePopup()
{
delete d;
}
void KPassivePopup::setPopupStyle( int popupstyle )
{
if ( d->popupStyle == popupstyle )
return;
d->popupStyle = popupstyle;
if( d->popupStyle == Boxed )
{
setFrameStyle( QFrame::Box| QFrame::Plain );
setLineWidth( 2 );
}
else if( d->popupStyle == Balloon )
{
setPalette(QToolTip::palette());
//XXX dead ? setAutoMask(true);
}
}
void KPassivePopup::setView( QWidget *child )
{
delete d->msgView;
d->msgView = child;
delete d->topLayout;
d->topLayout = new QVBoxLayout( this );
if ( d->popupStyle == Balloon ) {
d->topLayout->setMargin( 2 * KDialog::marginHint() );
}
d->topLayout->addWidget( d->msgView );
d->topLayout->activate();
}
void KPassivePopup::setView( const QString &caption, const QString &text,
const QPixmap &icon )
{
// kDebug() << "KPassivePopup::setView " << caption << ", " << text;
setView( standardView( caption, text, icon, this ) );
}
QWidget * KPassivePopup::standardView( const QString& caption,
const QString& text,
const QPixmap& icon,
QWidget *parent )
{
QWidget *top = new QWidget(parent ? parent : this);
QVBoxLayout *vb = new QVBoxLayout(top);
vb->setMargin(0);
top->setLayout(vb);
QHBoxLayout *hb = 0;
if ( !icon.isNull() ) {
hb = new QHBoxLayout( top );
hb->setMargin( 0 );
vb->addLayout( hb );
d->ttlIcon = new QLabel( top );
d->ttlIcon->setPixmap( icon );
d->ttlIcon->setAlignment( Qt::AlignLeft );
hb->addWidget( d->ttlIcon );
}
if ( !caption.isEmpty() ) {
d->ttl = new QLabel( caption, top );
QFont fnt = d->ttl->font();
fnt.setBold( true );
d->ttl->setFont( fnt );
d->ttl->setAlignment( Qt::AlignHCenter );
if ( hb ) {
hb->addWidget(d->ttl);
hb->setStretchFactor( d->ttl, 10 ); // enforce centering
} else {
vb->addWidget(d->ttl);
}
}
if ( !text.isEmpty() ) {
d->msg = new QLabel( text, top );
d->msg->setAlignment( Qt::AlignLeft );
d->msg->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
d->msg->setOpenExternalLinks(true);
vb->addWidget(d->msg);
}
return top;
}
void KPassivePopup::setView( const QString &caption, const QString &text )
{
setView( caption, text, QPixmap() );
}
QWidget *KPassivePopup::view() const
{
return d->msgView;
}
int KPassivePopup::timeout() const
{
return d->hideDelay;
}
void KPassivePopup::setTimeout( int delay )
{
d->hideDelay = delay;
if( d->hideTimer->isActive() )
{
if( delay ) {
d->hideTimer->start( delay );
} else {
d->hideTimer->stop();
}
}
}
bool KPassivePopup::autoDelete() const
{
return d->autoDelete;
}
void KPassivePopup::setAutoDelete( bool autoDelete )
{
d->autoDelete = autoDelete;
}
void KPassivePopup::mouseReleaseEvent( QMouseEvent *e )
{
emit clicked();
emit clicked( e->pos() );
}
//
// Main Implementation
//
void KPassivePopup::setVisible( bool visible )
{
if (! visible ) {
QFrame::setVisible( visible );
return;
}
if ( size() != sizeHint() )
resize( sizeHint() );
if ( d->fixedPosition.isNull() )
positionSelf();
else {
if( d->popupStyle == Balloon )
setAnchor( d->fixedPosition );
else
move( d->fixedPosition );
}
QFrame::setVisible( /*visible=*/ true );
int delay = d->hideDelay;
if ( delay < 0 ) {
delay = DEFAULT_POPUP_TIME;
}
if ( delay > 0 ) {
d->hideTimer->start( delay );
}
}
void KPassivePopup::show()
{
QFrame::show();
}
void KPassivePopup::show(const QPoint &p)
{
d->fixedPosition = p;
show();
}
void KPassivePopup::hideEvent( QHideEvent * )
{
d->hideTimer->stop();
if ( d->autoDelete )
deleteLater();
}
QRect KPassivePopup::defaultArea() const
{
#if HAVE_X11
NETRootInfo info( QX11Info::display(),
NET::NumberOfDesktops |
NET::CurrentDesktop |
NET::WorkArea,
-1, false );
info.activate();
NETRect workArea = info.workArea( info.currentDesktop() );
QRect r;
r.setRect( workArea.pos.x, workArea.pos.y, 0, 0 ); // top left
#else
// FIX IT
QRect r;
r.setRect( 100, 100, 200, 200 ); // top left
#endif
return r;
}
void KPassivePopup::positionSelf()
{
QRect target;
#if HAVE_X11
if ( !d->window ) {
target = defaultArea();
}
else {
NETWinInfo ni( QX11Info::display(), d->window, QX11Info::appRootWindow(),
NET::WMIconGeometry );
// Figure out where to put the popup. Note that we must handle
// windows that skip the taskbar cleanly
if ( ni.state() & NET::SkipTaskbar ) {
target = defaultArea();
}
else {
NETRect r = ni.iconGeometry();
target.setRect( r.pos.x, r.pos.y, r.size.width, r.size.height );
if ( target.isNull() ) { // bogus value, use the exact position
NETRect dummy;
ni.kdeGeometry( dummy, r );
target.setRect( r.pos.x, r.pos.y,
r.size.width, r.size.height);
}
}
}
#else
target = defaultArea();
#endif
moveNear( target );
}
void KPassivePopup::moveNear( const QRect &target )
{
QPoint pos = calculateNearbyPoint(target);
if( d->popupStyle == Balloon )
setAnchor( pos );
else
move( pos.x(), pos.y() );
}
QPoint KPassivePopup::calculateNearbyPoint( const QRect &target) {
QPoint pos = target.topLeft();
int x = pos.x();
int y = pos.y();
int w = minimumSizeHint().width();
int h = minimumSizeHint().height();
QRect r = QApplication::desktop()->screenGeometry(QPoint(x+w/2,y+h/2));
if( d->popupStyle == Balloon )
{
// find a point to anchor to
if( x + w > r.width() ){
x = x + target.width();
}
if( y + h > r.height() ){
y = y + target.height();
}
} else
{
if ( x < r.center().x() )
x = x + target.width();
else
x = x - w;
// It's apparently trying to go off screen, so display it ALL at the bottom.
if ( (y + h) > r.bottom() )
y = r.bottom() - h;
if ( (x + w) > r.right() )
x = r.right() - w;
}
if ( y < r.top() )
y = r.top();
if ( x < r.left() )
x = r.left();
return QPoint( x, y );
}
QPoint KPassivePopup::anchor() const
{
return d->anchor;
}
void KPassivePopup::setAnchor(const QPoint &anchor)
{
d->anchor = anchor;
updateMask();
}
void KPassivePopup::paintEvent( QPaintEvent* pe )
{
if( d->popupStyle == Balloon )
{
QPainter p;
p.begin( this );
p.drawPolygon( d->surround );
} else
QFrame::paintEvent( pe );
}
void KPassivePopup::updateMask()
{
// get screen-geometry for screen our anchor is on
// (geometry can differ from screen to screen!
QRect deskRect = QApplication::desktop()->screenGeometry(d->anchor);
int xh = 70, xl = 40;
if( width() < 80 )
xh = xl = 40;
else if( width() < 110 )
xh = width() - 40;
bool bottom = (d->anchor.y() + height()) > ((deskRect.y() + deskRect.height()-48));
bool right = (d->anchor.x() + width()) > ((deskRect.x() + deskRect.width()-48));
QPoint corners[4] = {
QPoint( width() - 50, 10 ),
QPoint( 10, 10 ),
QPoint( 10, height() - 50 ),
QPoint( width() - 50, height() - 50 )
};
QBitmap mask( width(), height() );
mask.clear();
QPainter p( &mask );
QBrush brush( Qt::color1, Qt::SolidPattern );
p.setBrush( brush );
int i = 0, z = 0;
for (; i < 4; ++i) {
QPainterPath path;
path.moveTo(corners[i].x(),corners[i].y());
path.arcTo(corners[i].x(),corners[i].y(),40,40, i * 90 , 90);
QPolygon corner = path.toFillPolygon().toPolygon();
d->surround.resize( z + corner.count() - 1 );
for (int s = 1; s < corner.count() - 1; s++, z++) {
d->surround.setPoint( z, corner[s] );
}
if (bottom && i == 2) {
if (right) {
d->surround.resize( z + 3 );
d->surround.setPoint( z++, QPoint( width() - xh, height() - 10 ) );
d->surround.setPoint( z++, QPoint( width() - 20, height() ) );
d->surround.setPoint( z++, QPoint( width() - xl, height() - 10 ) );
} else {
d->surround.resize( z + 3 );
d->surround.setPoint( z++, QPoint( xl, height() - 10 ) );
d->surround.setPoint( z++, QPoint( 20, height() ) );
d->surround.setPoint( z++, QPoint( xh, height() - 10 ) );
}
} else if (!bottom && i == 0) {
if (right) {
d->surround.resize( z + 3 );
d->surround.setPoint( z++, QPoint( width() - xl, 10 ) );
d->surround.setPoint( z++, QPoint( width() - 20, 0 ) );
d->surround.setPoint( z++, QPoint( width() - xh, 10 ) );
} else {
d->surround.resize( z + 3 );
d->surround.setPoint( z++, QPoint( xh, 10 ) );
d->surround.setPoint( z++, QPoint( 20, 0 ) );
d->surround.setPoint( z++, QPoint( xl, 10 ) );
}
}
}
d->surround.resize( z + 1 );
d->surround.setPoint( z, d->surround[0] );
p.drawPolygon( d->surround );
setMask(mask);
move( right ? d->anchor.x() - width() + 20 : ( d->anchor.x() < 11 ? 11 : d->anchor.x() - 20 ),
bottom ? d->anchor.y() - height() : ( d->anchor.y() < 11 ? 11 : d->anchor.y() ) );
update();
}
//
// Convenience Methods
//
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout )
{
return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
}
KPassivePopup *KPassivePopup::message( const QString &text, QWidget *parent )
{
return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
QWidget *parent )
{
return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
const QPixmap &icon, WId parent, int timeout )
{
return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
const QPixmap &icon,
QSystemTrayIcon *parent, int timeout )
{
return message( DEFAULT_POPUP_TYPE, caption, text, icon, parent, timeout );
}
KPassivePopup *KPassivePopup::message( const QString &text, QSystemTrayIcon *parent )
{
return message( DEFAULT_POPUP_TYPE, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( const QString &caption, const QString &text,
QSystemTrayIcon *parent )
{
return message( DEFAULT_POPUP_TYPE, caption, text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout )
{
KPassivePopup *pop = new KPassivePopup( parent );
pop->setPopupStyle( popupStyle );
pop->setAutoDelete( true );
pop->setView( caption, text, icon );
pop->d->hideDelay = timeout;
pop->show();
return pop;
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QWidget *parent )
{
return message( popupStyle, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
QWidget *parent )
{
return message( popupStyle, caption, text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon, WId parent, int timeout )
{
KPassivePopup *pop = new KPassivePopup( parent );
pop->setPopupStyle( popupStyle );
pop->setAutoDelete( true );
pop->setView( caption, text, icon );
pop->d->hideDelay = timeout;
pop->show();
return pop;
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QSystemTrayIcon *parent, int timeout )
{
KPassivePopup *pop = new KPassivePopup( );
pop->setPopupStyle( popupStyle );
pop->setAutoDelete( true );
pop->setView( caption, text, icon );
pop->d->hideDelay = timeout;
QPoint pos = pop->calculateNearbyPoint(parent->geometry());
pop->show(pos);
pop->moveNear(parent->geometry());
return pop;
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &text, QSystemTrayIcon *parent )
{
return message( popupStyle, QString(), text, QPixmap(), parent );
}
KPassivePopup *KPassivePopup::message( int popupStyle, const QString &caption, const QString &text,
QSystemTrayIcon *parent )
{
return message( popupStyle, caption, text, QPixmap(), parent );
}
// Local Variables:
// c-basic-offset: 4
// End:
diff --git a/kdeui/util/kpassivepopup.h b/kdeui/util/kpassivepopup.h
index 568c292142..83d8f7cf66 100644
--- a/kdeui/util/kpassivepopup.h
+++ b/kdeui/util/kpassivepopup.h
@@ -1,439 +1,439 @@
// -*- c++ -*-
/*
* Copyright (C) 2001-2006 by Richard Moore <rich@kde.org>
* Copyright (C) 2004-2005 by Sascha Cunz <sascha.cunz@tiscali.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KPASSIVEPOPUP_H
#define KPASSIVEPOPUP_H
#include <kdeui_export.h>
#include <QFrame>
class QSystemTrayIcon;
/**
* @short A dialog-like popup that displays messages without interrupting the user.
*
* The simplest uses of KPassivePopup are by using the various message() static
* methods. The position the popup appears at depends on the type of the parent window:
*
* @li Normal Windows: The popup is placed adjacent to the icon of the window.
* @li System Tray Windows: The popup is placed adjact to the system tray window itself.
* @li Skip Taskbar Windows: The popup is placed adjact to the window
* itself if it is visible, and at the edge of the desktop otherwise.
*
* You also have the option of calling show with a QPoint as a parameter that
* removes the automatic placing of KPassivePopup and shows it in the point you want.
*
* The most basic use of KPassivePopup displays a popup containing a piece of text:
* \code
* KPassivePopup::message( "This is the message", this );
* \endcode
* We can create popups with titles and icons too, as this example shows:
* \code
* QPixmap px;
* px.load( "hi32-app-logtracker.png" );
* KPassivePopup::message( "Some title", "This is the main text", px, this );
* \endcode
* This screenshot shows a popup with both a caption and a main text which is
* being displayed next to the toolbar icon of the window that triggered it:
* \image html kpassivepopup.png "A passive popup"
*
* For more control over the popup, you can use the setView(QWidget *) method
* to create a custom popup.
* \code
* KPassivePopup *pop = new KPassivePopup( parent );
*
* QWidget* content = new QWidget( pop );
* QVBoxLayout* vBox = new QVBoxLayout( content );
* QLabel* label = new QLabel( "<b>Isn't this great?</b>", content );
* vBox->addWidget( label );
*
* QPushButton* btnYes = new QPushButton( "Yes", content );
* QPushButton* btnNo = new QPushButton( "No", content );
*
* QHBoxLayout* hBox = new QHBoxLayout( content );
* hBox->addWidget( btnYes );
* hBox->addWidget( btnNo );
*
* vBox->addLayout( vBox );
*
* pop->setView( content );
* pop->show();
* \endcode
*
* @author Richard Moore, rich@kde.org
* @author Sascha Cunz, sascha.cunz@tiscali.de
*/
class KDEUI_EXPORT KPassivePopup : public QFrame
{
Q_OBJECT
Q_PROPERTY (bool autoDelete READ autoDelete WRITE setAutoDelete )
Q_PROPERTY (int timeout READ timeout WRITE setTimeout )
Q_PROPERTY (QRect defaultArea READ defaultArea )
public:
/**
* Styles that a KPassivePopup can have.
*/
enum PopupStyle
{
Boxed, ///< Information will appear in a framed box (default)
Balloon, ///< Information will appear in a comic-alike balloon
CustomStyle=128 ///< Ids greater than this are reserved for use by subclasses
};
/**
* Creates a popup for the specified widget.
*/
- explicit KPassivePopup( QWidget *parent=0, Qt::WFlags f = 0 );
+ explicit KPassivePopup( QWidget *parent=0, Qt::WindowFlags f = 0 );
/**
* Creates a popup for the specified window.
*/
explicit KPassivePopup( WId parent );
#if 0 // These break macos and win32 where the definition of WId makes them ambiguous
/**
* Creates a popup for the specified widget.
* THIS WILL BE REMOVED, USE setPopupStyle().
*/
- explicit KPassivePopup( int popupStyle, QWidget *parent=0, Qt::WFlags f=0 ) KDEUI_DEPRECATED;
+ explicit KPassivePopup( int popupStyle, QWidget *parent=0, Qt::WindowFlags f=0 ) KDEUI_DEPRECATED;
/**
* Creates a popup for the specified window.
* THIS WILL BE REMOVED, USE setPopupStyle().
*/
- KPassivePopup( int popupStyle, WId parent, Qt::WFlags f=0 ) KDEUI_DEPRECATED;
+ KPassivePopup( int popupStyle, WId parent, Qt::WindowFlags f=0 ) KDEUI_DEPRECATED;
#endif
/**
* Cleans up.
*/
virtual ~KPassivePopup();
/**
* Sets the main view to be the specified widget (which must be a child of the popup).
*/
void setView( QWidget *child );
/**
* Creates a standard view then calls setView(QWidget*) .
*/
void setView( const QString &caption, const QString &text = QString() );
/**
* Creates a standard view then calls setView(QWidget*) .
*/
virtual void setView( const QString &caption, const QString &text, const QPixmap &icon );
/**
* Returns a widget that is used as standard view if one of the
* setView() methods taking the QString arguments is used.
* You can use the returned widget to customize the passivepopup while
* keeping the look similar to the "standard" passivepopups.
*
* After customizing the widget, pass it to setView( QWidget* )
*
* @param caption The window caption (title) on the popup
* @param text The text for the popup
* @param icon The icon to use for the popup
* @param parent The parent widget used for the returned widget. If left 0,
* then "this", i.e. the passive popup object will be used.
*
* @return a QWidget containing the given arguments, looking like the
* standard passivepopups. The returned widget contains a QVBoxLayout,
* which is accessible through layout().
* @see setView( QWidget * )
* @see setView( const QString&, const QString& )
* @see setView( const QString&, const QString&, const QPixmap& )
*/
QWidget * standardView( const QString& caption, const QString& text,
const QPixmap& icon, QWidget *parent = 0L );
/**
* Returns the main view.
*/
QWidget *view() const;
/**
* Returns the delay before the popup is removed automatically.
*/
int timeout() const;
/**
* Enables / disables auto-deletion of this widget when the timeout
* occurs.
* The default is false. If you use the class-methods message(),
* auto-deletion is turned on by default.
*/
virtual void setAutoDelete( bool autoDelete );
/**
* @returns true if the widget auto-deletes itself when the timeout occurs.
* @see setAutoDelete
*/
bool autoDelete() const;
/**
* If no relative window (eg taskbar button, system tray window) is
* available, use this rectangle (pass it to moveNear()).
* Basically KWindowSystem::workArea() with width and height set to 0
* so that moveNear uses the upper-left position.
* @return The QRect to be passed to moveNear() if no other is
* available.
*/
QRect defaultArea() const;
/**
* Returns the position to which this popup is anchored.
*/
QPoint anchor() const;
/**
* Sets the anchor of this popup. The popup tries automatically to adjust
* itself somehow around the point.
*/
void setAnchor( const QPoint& anchor );
// TODO KDE4: give all the static methods a const QPoint p = QPoint() that in
// case the point is not null calls the show(const QPoint &p) method instead
// of the show() one.
/**
* Convenience method that displays popup with the specified message beside the
* icon of the specified widget.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &text, QWidget *parent );
/**
* Convenience method that displays popup with the specified message beside the
* icon of the specified QSystemTrayIcon.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &text, QSystemTrayIcon *parent );
/**
* Convenience method that displays popup with the specified caption and message
* beside the icon of the specified widget.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &caption, const QString &text,
QWidget *parent );
/**
* Convenience method that displays popup with the specified caption and message
* beside the icon of the specified QSystemTrayIcon.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &caption, const QString &text,
QSystemTrayIcon *parent );
/**
* Convenience method that displays popup with the specified icon, caption and
* message beside the icon of the specified widget.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout = -1 );
/**
* Convenience method that displays popup with the specified icon, caption and
* message beside the icon of the specified QSystemTrayIcon.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &caption, const QString &text,
const QPixmap &icon,
QSystemTrayIcon *parent, int timeout = -1 );
/**
* Convenience method that displays popup with the specified icon, caption and
* message beside the icon of the specified window.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( const QString &caption, const QString &text,
const QPixmap &icon,
WId parent, int timeout = -1 );
/**
* Convenience method that displays popup with the specified popup-style and message beside the
* icon of the specified widget.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &text, QWidget *parent );
/**
* Convenience method that displays popup with the specified popup-style and message beside the
* icon of the specified QSystemTrayIcon.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &text, QSystemTrayIcon *parent );
/**
* Convenience method that displays popup with the specified popup-style, caption and message
* beside the icon of the specified QSystemTrayIcon.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &caption, const QString &text,
QSystemTrayIcon *parent );
/**
* Convenience method that displays popup with the specified popup-style, caption and message
* beside the icon of the specified widget.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &caption, const QString &text,
QWidget *parent );
/**
* Convenience method that displays popup with the specified popup-style, icon, caption and
* message beside the icon of the specified widget.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QWidget *parent, int timeout = -1 );
/**
* Convenience method that displays popup with the specified popup-style, icon, caption and
* message beside the icon of the specified QSystemTrayIcon.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
QSystemTrayIcon *parent, int timeout = -1 );
/**
* Convenience method that displays popup with the specified popup-style, icon, caption and
* message beside the icon of the specified window.
* Note that the returned object is destroyed when it is hidden.
* @see setAutoDelete
*/
static KPassivePopup *message( int popupStyle, const QString &caption, const QString &text,
const QPixmap &icon,
WId parent, int timeout = -1 );
public Q_SLOTS:
/**
* Sets the delay for the popup is removed automatically. Setting the delay to 0
* disables the timeout, if you're doing this, you may want to connect the
* clicked() signal to the hide() slot.
* Setting the delay to -1 makes it use the default value.
*
* @see timeout
*/
void setTimeout( int delay );
/**
* Sets the visual appearance of the popup.
* @see PopupStyle
*/
void setPopupStyle( int popupstyle );
/**
* Reimplemented to reposition the popup.
*/
void show();
/**
* Shows the popup in the given point
*/
void show(const QPoint &p);
virtual void setVisible(bool visible);
Q_SIGNALS:
/**
* Emitted when the popup is clicked.
*/
void clicked();
/**
* Emitted when the popup is clicked.
*/
void clicked( const QPoint &pos );
protected:
/**
* This method positions the popup.
*/
virtual void positionSelf();
/**
* Reimplemented to destroy the object when autoDelete() is
* enabled.
*/
virtual void hideEvent( QHideEvent * );
/**
* Moves the popup to be adjacent to the icon of the specified rectangle.
*/
void moveNear( const QRect &target );
/**
* Calculates the position to place the popup near the specified rectangle.
*/
QPoint calculateNearbyPoint( const QRect &target);
/**
* Reimplemented to detect mouse clicks.
*/
virtual void mouseReleaseEvent( QMouseEvent *e );
/**
* Updates the transparency mask. Unused if PopupStyle == Boxed
*/
void updateMask();
/**
* Overwrite to paint the border when PopupStyle == Balloon.
* Unused if PopupStyle == Boxed
*/
virtual void paintEvent( QPaintEvent* pe );
private:
void init( WId window );
/* @internal */
class Private;
Private *const d;
};
#endif // KPASSIVEPOPUP_H
// Local Variables:
// c-basic-offset: 4
// End:
diff --git a/kdeui/widgets/kmainwindow.cpp b/kdeui/widgets/kmainwindow.cpp
index 0a9398b651..c86b22c87f 100644
--- a/kdeui/widgets/kmainwindow.cpp
+++ b/kdeui/widgets/kmainwindow.cpp
@@ -1,926 +1,926 @@
/* This file is part of the KDE libraries
Copyright
(C) 2000 Reginald Stadlbauer (reggie@kde.org)
(C) 1997 Stephan Kulow (coolo@kde.org)
(C) 1997-2000 Sven Radej (radej@kde.org)
(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)
(C) 1999 Chris Schlaeger (cs@kde.org)
(C) 2002 Joseph Wenninger (jowenn@kde.org)
(C) 2005-2006 Hamish Rodda (rodda@kde.org)
(C) 2000-2008 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 version 2 as published by the Free Software Foundation.
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 "kmainwindow.h"
#include "kmainwindow_p.h"
#include "kmainwindowiface_p.h"
#include "ktoolbarhandler_p.h"
#include "ktoggleaction.h"
#include <QApplication>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QCloseEvent>
#include <QDesktopWidget>
#include <QDockWidget>
#include <QLayout>
#include <QMenuBar>
#include <QSessionManager>
#include <QStatusBar>
#include <QStyle>
#include <QWidget>
#include <kaction.h>
#include <kcomponentdata.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kdialog.h>
#include <khelpmenu.h>
#include <klocalizedstring.h>
#include <ktoolbar.h>
#include <kwindowsystem.h>
#include <kconfiggroup.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kwindowconfig.h>
#include <kconfiggui.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
static QMenuBar *internalMenuBar(KMainWindow *mw)
{
return KGlobal::findDirectChild<QMenuBar *>(mw);
}
static QStatusBar *internalStatusBar(KMainWindow *mw)
{
// Don't use qFindChild here, it's recursive!
// (== slow, but also finds konqueror's per-view statusbars)
return KGlobal::findDirectChild<QStatusBar *>(mw);
}
/**
* Listens to resize events from QDockWidgets. The KMainWindow
* settings are set as dirty, as soon as at least one resize
* event occurred. The listener is attached to the dock widgets
* by dock->installEventFilter(dockResizeListener) inside
* KMainWindow::event().
*/
class DockResizeListener : public QObject
{
public:
DockResizeListener(KMainWindow *win);
virtual ~DockResizeListener();
virtual bool eventFilter(QObject *watched, QEvent *event);
private:
KMainWindow *m_win;
};
DockResizeListener::DockResizeListener(KMainWindow *win) :
QObject(win),
m_win(win)
{
}
DockResizeListener::~DockResizeListener()
{
}
bool DockResizeListener::eventFilter(QObject *watched, QEvent *event)
{
switch( event->type() ) {
case QEvent::Resize:
case QEvent::Move:
case QEvent::Hide:
m_win->k_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls);
break;
default:
break;
}
return QObject::eventFilter(watched, event);
}
KMWSessionManager::KMWSessionManager()
{
connect(qApp, SIGNAL(saveStateRequest(QSessionManager&)),
this, SLOT(saveState(QSessionManager&)));
}
KMWSessionManager::~KMWSessionManager()
{
}
bool KMWSessionManager::saveState(QSessionManager&)
{
KConfig* config = KConfigGui::sessionConfig();
if ( KMainWindow::memberList().count() ){
// According to Jochen Wilhelmy <digisnap@cs.tu-berlin.de>, this
// hook is useful for better document orientation
KMainWindow::memberList().first()->saveGlobalProperties(config);
}
int n = 0;
foreach (KMainWindow* mw, KMainWindow::memberList()) {
n++;
mw->savePropertiesInternal(config, n);
}
KConfigGroup group( config, "Number" );
group.writeEntry("NumberOfWindows", n );
return true;
}
Q_GLOBAL_STATIC(KMWSessionManager, ksm)
Q_GLOBAL_STATIC(QList<KMainWindow*>, sMemberList)
-KMainWindow::KMainWindow( QWidget* parent, Qt::WFlags f )
+KMainWindow::KMainWindow( QWidget* parent, Qt::WindowFlags f )
: QMainWindow(parent, f), k_ptr(new KMainWindowPrivate)
{
k_ptr->init(this);
}
-KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WFlags f)
+KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f)
: QMainWindow(parent, f), k_ptr(&dd)
{
k_ptr->init(this);
}
void KMainWindowPrivate::init(KMainWindow *_q)
{
KGlobal::ref();
// We set allow quit to true when the first mainwindow is created, so that when the refcounting
// reaches 0 the application can quit. We don't want this to happen before the first mainwindow
// is created, otherwise running a job in main would exit the app too early.
KGlobal::setAllowQuit(true);
q = _q;
q->setAnimated(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects);
q->setAttribute( Qt::WA_DeleteOnClose );
// We handle this functionality (quitting the app) ourselves, with KGlobal::ref/deref.
// This makes apps stay alive even if they only have a systray icon visible, or
// a progress widget with "keep open" checked, for instance.
// So don't let the default Qt mechanism allow any toplevel widget to just quit the app on us.
// Setting WA_QuitOnClose to false for all KMainWindows is not enough, any progress widget
// or dialog box would still quit the app...
if (qApp)
qApp->setQuitOnLastWindowClosed(false);
helpMenu = 0;
//actionCollection()->setWidget( this );
QObject::connect(qApp, SIGNAL(aboutToQuit()), q, SLOT(_k_shuttingDown()));
QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
q, SLOT(_k_slotSettingsChanged(int)));
// force KMWSessionManager creation
ksm();
sMemberList()->append( q );
settingsDirty = false;
autoSaveSettings = false;
autoSaveWindowSize = true; // for compatibility
//d->kaccel = actionCollection()->kaccel();
settingsTimer = 0;
sizeTimer = 0;
shuttingDown = false;
q->setWindowTitle( KGlobal::caption() );
dockResizeListener = new DockResizeListener(_q);
letDirtySettings = true;
sizeApplied = false;
}
static bool endsWithHashNumber( const QString& s )
{
for( int i = s.length() - 1;
i > 0;
--i )
{
if( s[ i ] == '#' && i != s.length() - 1 )
return true; // ok
if( !s[ i ].isDigit())
break;
}
return false;
}
static inline bool isValidDBusObjectPathCharacter(const QChar &c)
{
register ushort u = c.unicode();
return (u >= 'a' && u <= 'z')
|| (u >= 'A' && u <= 'Z')
|| (u >= '0' && u <= '9')
|| (u == '_') || (u == '/');
}
void KMainWindowPrivate::polish(KMainWindow *q)
{
// Set a unique object name. Required by session management, window management, and for the dbus interface.
QString objname;
QString s;
int unusedNumber = 1;
const QString name = q->objectName();
bool startNumberingImmediately = true;
bool tryReuse = false;
if ( name.isEmpty() )
{ // no name given
objname = "MainWindow#";
}
else if( name.endsWith( QLatin1Char( '#' ) ) )
{ // trailing # - always add a number - KWin uses this for better grouping
objname = name;
}
else if( endsWithHashNumber( name ))
{ // trailing # with a number - like above, try to use the given number first
objname = name;
tryReuse = true;
startNumberingImmediately = false;
}
else
{
objname = name;
startNumberingImmediately = false;
}
s = objname;
if ( startNumberingImmediately )
s += '1';
for(;;) {
const QList<QWidget*> list = qApp->topLevelWidgets();
bool found = false;
foreach ( QWidget* w, list ) {
if( w != q && w->objectName() == s )
{
found = true;
break;
}
}
if( !found )
break;
if( tryReuse ) {
objname = name.left( name.length() - 1 ); // lose the hash
unusedNumber = 0; // start from 1 below
tryReuse = false;
}
s.setNum( ++unusedNumber );
s = objname + s;
}
q->setObjectName( s );
q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT
q->setWindowRole( s ); // will keep insisting that object name suddenly should not be used for window role
QString appName = QCoreApplication::applicationName();
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
if (appName.isEmpty()) {
appName = qAppName();
}
#endif
dbusName = '/' + appName + '/' + q->objectName().replace(QLatin1Char('/'), QLatin1Char('_'));
// Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
const int len = dbusName.length();
for ( int i = 0; i < len; ++i ) {
if ( !isValidDBusObjectPathCharacter( dbusName[i] ) )
dbusName[i] = QLatin1Char('_');
}
QDBusConnection::sessionBus().registerObject(dbusName, q, QDBusConnection::ExportScriptableSlots |
QDBusConnection::ExportScriptableProperties |
QDBusConnection::ExportNonScriptableSlots |
QDBusConnection::ExportNonScriptableProperties |
QDBusConnection::ExportAdaptors);
}
void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression)
{
if (!letDirtySettings) {
return;
}
settingsDirty = true;
if (autoSaveSettings) {
if (callCompression == CompressCalls) {
if (!settingsTimer) {
settingsTimer = new QTimer(q);
settingsTimer->setInterval(500);
settingsTimer->setSingleShot(true);
QObject::connect(settingsTimer, SIGNAL(timeout()), q, SLOT(saveAutoSaveSettings()));
}
settingsTimer->start();
} else {
q->saveAutoSaveSettings();
}
}
}
void KMainWindowPrivate::setSizeDirty()
{
if (autoSaveWindowSize) {
if (!sizeTimer) {
sizeTimer = new QTimer(q);
sizeTimer->setInterval(500);
sizeTimer->setSingleShot(true);
QObject::connect(sizeTimer, SIGNAL(timeout()), q, SLOT(_k_slotSaveAutoSaveSize()));
}
sizeTimer->start();
}
}
KMainWindow::~KMainWindow()
{
sMemberList()->removeAll( this );
delete static_cast<QObject *>(k_ptr->dockResizeListener); //so we don't get anymore events after k_ptr is destroyed
delete k_ptr;
KGlobal::deref();
}
KMenu* KMainWindow::helpMenu( const QString &aboutAppText, bool showWhatsThis )
{
K_D(KMainWindow);
if(!d->helpMenu) {
if ( aboutAppText.isEmpty() )
d->helpMenu = new KHelpMenu( this, KComponentData::mainComponent().aboutData(), showWhatsThis);
else
d->helpMenu = new KHelpMenu( this, aboutAppText, showWhatsThis );
if (!d->helpMenu)
return 0;
}
return d->helpMenu->menu();
}
KMenu* KMainWindow::customHelpMenu( bool showWhatsThis )
{
K_D(KMainWindow);
if (!d->helpMenu) {
d->helpMenu = new KHelpMenu( this, QString(), showWhatsThis );
connect(d->helpMenu, SIGNAL(showAboutApplication()),
this, SLOT(showAboutApplication()));
}
return d->helpMenu->menu();
}
bool KMainWindow::canBeRestored( int number )
{
if ( !qApp->isSessionRestored() )
return false;
KConfig *config = KConfigGui::sessionConfig();
if ( !config )
return false;
KConfigGroup group( config, "Number" );
const int n = group.readEntry( "NumberOfWindows", 1 );
return number >= 1 && number <= n;
}
const QString KMainWindow::classNameOfToplevel( int number )
{
if ( !qApp->isSessionRestored() )
return QString();
KConfig *config = KConfigGui::sessionConfig();
if ( !config )
return QString();
QString s;
s.setNum( number );
s.prepend( QLatin1String("WindowProperties") );
KConfigGroup group( config, s );
if ( !group.hasKey( "ClassName" ) )
return QString();
else
return group.readEntry( "ClassName" );
}
bool KMainWindow::restore( int number, bool show )
{
if ( !canBeRestored( number ) )
return false;
KConfig *config = KConfigGui::sessionConfig();
if ( readPropertiesInternal( config, number ) ){
if ( show )
KMainWindow::show();
return false;
}
return false;
}
void KMainWindow::setCaption( const QString &caption )
{
setPlainCaption( KDialog::makeStandardCaption( caption, this ) );
}
void KMainWindow::setCaption( const QString &caption, bool modified )
{
KDialog::CaptionFlags flags = KDialog::HIGCompliantCaption;
if ( modified )
{
flags |= KDialog::ModifiedCaption;
}
setPlainCaption( KDialog::makeStandardCaption(caption, this, flags) );
}
void KMainWindow::setPlainCaption( const QString &caption )
{
setWindowTitle(caption);
}
void KMainWindow::appHelpActivated( void )
{
K_D(KMainWindow);
if( !d->helpMenu ) {
d->helpMenu = new KHelpMenu( this );
if ( !d->helpMenu )
return;
}
d->helpMenu->appHelpActivated();
}
void KMainWindow::closeEvent ( QCloseEvent *e )
{
K_D(KMainWindow);
// Save settings if auto-save is enabled, and settings have changed
if (d->settingsTimer && d->settingsTimer->isActive()) {
d->settingsTimer->stop();
saveAutoSaveSettings();
}
if (d->sizeTimer && d->sizeTimer->isActive()) {
d->sizeTimer->stop();
d->_k_slotSaveAutoSaveSize();
}
if (queryClose()) {
e->accept();
int not_withdrawn = 0;
foreach (KMainWindow* mw, KMainWindow::memberList()) {
if ( !mw->isHidden() && mw->isTopLevel() && mw != this )
not_withdrawn++;
}
if ( not_withdrawn <= 0 ) { // last window close accepted?
if ( !queryExit() || d->shuttingDown ) {
// cancel closing, it's stupid to end up with no windows at all....
e->ignore();
}
}
} else e->ignore(); //if the window should not be closed, don't close it
}
bool KMainWindow::queryExit()
{
return true;
}
bool KMainWindow::queryClose()
{
return true;
}
void KMainWindow::saveGlobalProperties( KConfig* )
{
}
void KMainWindow::readGlobalProperties( KConfig* )
{
}
void KMainWindow::showAboutApplication()
{
}
void KMainWindow::savePropertiesInternal( KConfig *config, int number )
{
K_D(KMainWindow);
const bool oldASWS = d->autoSaveWindowSize;
d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size
QString s;
s.setNum(number);
s.prepend(QLatin1String("WindowProperties"));
KConfigGroup cg(config, s);
// store objectName, className, Width and Height for later restoring
// (Only useful for session management)
cg.writeEntry(QLatin1String("ObjectName"), objectName());
cg.writeEntry(QLatin1String("ClassName"), metaObject()->className());
saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings.
s.setNum(number);
cg = KConfigGroup(config, s);
saveProperties(cg);
d->autoSaveWindowSize = oldASWS;
}
void KMainWindow::saveMainWindowSettings(KConfigGroup &cg)
{
K_D(KMainWindow);
//kDebug(200) << "KMainWindow::saveMainWindowSettings " << cg.name();
// Called by session management - or if we want to save the window size anyway
if ( d->autoSaveWindowSize )
KWindowConfig::saveWindowSize( this, cg );
// One day will need to save the version number, but for now, assume 0
// Utilise the QMainWindow::saveState() functionality.
const QByteArray state = saveState();
cg.writeEntry(QString("State"), state.toBase64());
QStatusBar* sb = internalStatusBar(this);
if (sb) {
if(!cg.hasDefault("StatusBar") && !sb->isHidden() )
cg.revertToDefault("StatusBar");
else
cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled");
}
QMenuBar* mb = internalMenuBar(this);
if (mb) {
const QString MenuBar = QLatin1String("MenuBar");
if(!cg.hasDefault("MenuBar") && !mb->isHidden() )
cg.revertToDefault("MenuBar");
else
cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled");
}
if ( !autoSaveSettings() || cg.name() == autoSaveGroup() ) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
if(!cg.hasDefault("ToolBarsMovable") && !KToolBar::toolBarsLocked())
cg.revertToDefault("ToolBarsMovable");
else
cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled");
}
int n = 1; // Toolbar counter. toolbars are counted from 1,
foreach (KToolBar* toolbar, toolBars()) {
QString group("Toolbar");
// Give a number to the toolbar, but prefer a name if there is one,
// because there's no real guarantee on the ordering of toolbars
group += (toolbar->objectName().isEmpty() ? QString::number(n) : QString(" ")+toolbar->objectName());
KConfigGroup toolbarGroup(&cg, group);
toolbar->saveSettings(toolbarGroup);
n++;
}
}
bool KMainWindow::readPropertiesInternal( KConfig *config, int number )
{
K_D(KMainWindow);
const bool oldLetDirtySettings = d->letDirtySettings;
d->letDirtySettings = false;
if ( number == 1 )
readGlobalProperties( config );
// in order they are in toolbar list
QString s;
s.setNum(number);
s.prepend(QLatin1String("WindowProperties"));
KConfigGroup cg(config, s);
// restore the object name (window role)
if ( cg.hasKey(QLatin1String("ObjectName" )) )
setObjectName( cg.readEntry("ObjectName").toLatin1()); // latin1 is right here
d->sizeApplied = false; // since we are changing config file, reload the size of the window
// if necessary. Do it before the call to applyMainWindowSettings.
applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings.
s.setNum(number);
KConfigGroup grp(config, s);
readProperties(grp);
d->letDirtySettings = oldLetDirtySettings;
return true;
}
void KMainWindow::applyMainWindowSettings(const KConfigGroup &cg, bool force)
{
K_D(KMainWindow);
kDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name();
QWidget *focusedWidget = QApplication::focusWidget();
const bool oldLetDirtySettings = d->letDirtySettings;
d->letDirtySettings = false;
if (!d->sizeApplied) {
KWindowConfig::restoreWindowSize(this, cg);
d->sizeApplied = true;
}
QStatusBar* sb = internalStatusBar(this);
if (sb) {
QString entry = cg.readEntry("StatusBar", "Enabled");
if ( entry == "Disabled" )
sb->hide();
else
sb->show();
}
QMenuBar* mb = internalMenuBar(this);
if (mb) {
QString entry = cg.readEntry ("MenuBar", "Enabled");
if ( entry == "Disabled" )
mb->hide();
else
mb->show();
}
if ( !autoSaveSettings() || cg.name() == autoSaveGroup() ) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
QString entry = cg.readEntry ("ToolBarsMovable", "Disabled");
if ( entry == "Disabled" )
KToolBar::setToolBarsLocked(true);
else
KToolBar::setToolBarsLocked(false);
}
int n = 1; // Toolbar counter. toolbars are counted from 1,
foreach (KToolBar* toolbar, toolBars()) {
QString group("Toolbar");
// Give a number to the toolbar, but prefer a name if there is one,
// because there's no real guarantee on the ordering of toolbars
group += (toolbar->objectName().isEmpty() ? QString::number(n) : QString(" ")+toolbar->objectName());
KConfigGroup toolbarGroup(&cg, group);
toolbar->applySettings(toolbarGroup, force);
n++;
}
QByteArray state;
if (cg.hasKey("State")) {
state = cg.readEntry("State", state);
state = QByteArray::fromBase64(state);
// One day will need to load the version number, but for now, assume 0
restoreState(state);
}
if (focusedWidget) {
focusedWidget->setFocus();
}
d->settingsDirty = false;
d->letDirtySettings = oldLetDirtySettings;
}
#ifndef KDE_NO_DEPRECATED
void KMainWindow::restoreWindowSize( const KConfigGroup & cg )
{
KWindowConfig::restoreWindowSize(this, cg);
}
#endif
#ifndef KDE_NO_DEPRECATED
void KMainWindow::saveWindowSize( KConfigGroup & cg ) const
{
KWindowConfig::saveWindowSize(this, cg);
}
#endif
void KMainWindow::setSettingsDirty()
{
K_D(KMainWindow);
d->setSettingsDirty();
}
bool KMainWindow::settingsDirty() const
{
K_D(const KMainWindow);
return d->settingsDirty;
}
void KMainWindow::setAutoSaveSettings( const QString & groupName, bool saveWindowSize )
{
setAutoSaveSettings(KConfigGroup(KSharedConfig::openConfig(), groupName), saveWindowSize);
}
void KMainWindow::setAutoSaveSettings( const KConfigGroup & group,
bool saveWindowSize )
{
K_D(KMainWindow);
d->autoSaveSettings = true;
d->autoSaveGroup = group;
d->autoSaveWindowSize = saveWindowSize;
if (!saveWindowSize && d->sizeTimer) {
d->sizeTimer->stop();
}
// Now read the previously saved settings
applyMainWindowSettings(d->autoSaveGroup);
}
void KMainWindow::resetAutoSaveSettings()
{
K_D(KMainWindow);
d->autoSaveSettings = false;
if (d->settingsTimer) {
d->settingsTimer->stop();
}
}
bool KMainWindow::autoSaveSettings() const
{
K_D(const KMainWindow);
return d->autoSaveSettings;
}
QString KMainWindow::autoSaveGroup() const
{
K_D(const KMainWindow);
return d->autoSaveSettings ? d->autoSaveGroup.name() : QString();
}
KConfigGroup KMainWindow::autoSaveConfigGroup() const
{
K_D(const KMainWindow);
return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup();
}
void KMainWindow::saveAutoSaveSettings()
{
K_D(KMainWindow);
Q_ASSERT( d->autoSaveSettings );
//kDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings";
saveMainWindowSettings(d->autoSaveGroup);
d->autoSaveGroup.sync();
d->settingsDirty = false;
}
bool KMainWindow::event( QEvent* ev )
{
K_D(KMainWindow);
switch( ev->type() ) {
#ifdef Q_OS_WIN
case QEvent::Move:
#endif
case QEvent::Resize:
d->setSizeDirty();
break;
case QEvent::Polish:
d->polish(this);
break;
case QEvent::ChildPolished:
{
QChildEvent *event = static_cast<QChildEvent*>(ev);
QDockWidget *dock = qobject_cast<QDockWidget*>(event->child());
KToolBar *toolbar = qobject_cast<KToolBar*>(event->child());
QMenuBar *menubar = qobject_cast<QMenuBar*>(event->child());
if (dock) {
connect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
this, SLOT(setSettingsDirty()));
connect(dock, SIGNAL(visibilityChanged(bool)),
this, SLOT(setSettingsDirty()), Qt::QueuedConnection);
connect(dock, SIGNAL(topLevelChanged(bool)),
this, SLOT(setSettingsDirty()));
// there is no signal emitted if the size of the dock changes,
// hence install an event filter instead
dock->installEventFilter(k_ptr->dockResizeListener);
} else if (toolbar) {
// there is no signal emitted if the size of the toolbar changes,
// hence install an event filter instead
toolbar->installEventFilter(k_ptr->dockResizeListener);
} else if (menubar) {
// there is no signal emitted if the size of the menubar changes,
// hence install an event filter instead
menubar->installEventFilter(k_ptr->dockResizeListener);
}
}
break;
case QEvent::ChildRemoved:
{
QChildEvent *event = static_cast<QChildEvent*>(ev);
QDockWidget *dock = qobject_cast<QDockWidget*>(event->child());
KToolBar *toolbar = qobject_cast<KToolBar*>(event->child());
QMenuBar *menubar = qobject_cast<QMenuBar*>(event->child());
if (dock) {
disconnect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
this, SLOT(setSettingsDirty()));
disconnect(dock, SIGNAL(visibilityChanged(bool)),
this, SLOT(setSettingsDirty()));
disconnect(dock, SIGNAL(topLevelChanged(bool)),
this, SLOT(setSettingsDirty()));
dock->removeEventFilter(k_ptr->dockResizeListener);
} else if (toolbar) {
toolbar->removeEventFilter(k_ptr->dockResizeListener);
} else if (menubar) {
menubar->removeEventFilter(k_ptr->dockResizeListener);
}
}
break;
default:
break;
}
return QMainWindow::event( ev );
}
bool KMainWindow::hasMenuBar()
{
return internalMenuBar(this);
}
void KMainWindowPrivate::_k_shuttingDown()
{
// Needed for Qt <= 3.0.3 at least to prevent reentrancy
// when queryExit() shows a dialog. Check before removing!
static bool reentrancy_protection = false;
if (!reentrancy_protection)
{
reentrancy_protection = true;
shuttingDown = true;
// call the virtual queryExit
q->queryExit();
reentrancy_protection = false;
}
}
void KMainWindowPrivate::_k_slotSettingsChanged(int category)
{
Q_UNUSED(category);
// This slot will be called when the style KCM changes settings that need
// to be set on the already running applications.
// At this level (KMainWindow) the only thing we need to restore is the
// animations setting (whether the user wants builtin animations or not).
q->setAnimated(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects);
}
void KMainWindowPrivate::_k_slotSaveAutoSaveSize()
{
if (autoSaveGroup.isValid()) {
KWindowConfig::saveWindowSize(q, autoSaveGroup);
}
}
KToolBar *KMainWindow::toolBar( const QString& name )
{
QString childName = name;
if (childName.isEmpty())
childName = "mainToolBar";
KToolBar *tb = findChild<KToolBar*>(childName);
if ( tb )
return tb;
KToolBar* toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar
return toolbar;
}
QList<KToolBar*> KMainWindow::toolBars() const
{
QList<KToolBar*> ret;
foreach (QObject* child, children())
if (KToolBar* toolBar = qobject_cast<KToolBar*>(child))
ret.append(toolBar);
return ret;
}
QList<KMainWindow*> KMainWindow::memberList() { return *sMemberList(); }
QString KMainWindow::dbusName() const
{
return k_func()->dbusName;
}
#include "moc_kmainwindow.cpp"
diff --git a/kdeui/widgets/kmainwindow.h b/kdeui/widgets/kmainwindow.h
index 9a1e630531..ce6be40207 100644
--- a/kdeui/widgets/kmainwindow.h
+++ b/kdeui/widgets/kmainwindow.h
@@ -1,784 +1,784 @@
/*
This file is part of the KDE libraries
Copyright
(C) 2000 Reginald Stadlbauer (reggie@kde.org)
(C) 1997 Stephan Kulow (coolo@kde.org)
(C) 1997-2000 Sven Radej (radej@kde.org)
(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)
(C) 1999 Chris Schlaeger (cs@kde.org)
(C) 2002 Joseph Wenninger (jowenn@kde.org)
(C) 2005-2006 Hamish Rodda (rodda@kde.org)
(C) 2000-2008 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 version 2 as published by the Free Software Foundation.
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 KMAINWINDOW_H
#define KMAINWINDOW_H
#include <kdeui_export.h>
#include <QMainWindow>
#include <QtCore/QMetaClassInfo>
class KMenu;
class KConfig;
class KConfigGroup;
class KMWSessionManager;
class KMainWindowPrivate;
class KToolBar;
// internal, not public API, may change any time
#define KDEUI_DECLARE_PRIVATE(classname) \
inline classname ## Private *k_func() { return reinterpret_cast<classname ## Private *>(k_ptr); } \
inline const classname ## Private *k_func() const { return reinterpret_cast<classname ## Private *>(k_ptr); } \
friend class classname ## Private;
// This is mostly from KDE3. TODO KDE5: remove the constructor parameter.
#define KDE_DEFAULT_WINDOWFLAGS 0
/**
* @short %KDE top level main window
*
* Top level widget that provides toolbars, a status line and a frame.
*
* It should be used as a top level (parent-less) widget.
* It manages the geometry for all its children, including your
* main widget.
*
* Normally, you will inherit from KMainWindow,
* then construct (or use some existing) widget as
* your main view. You can set only one main view.
*
* You can add as many toolbars as you like. There can be only one menubar
* and only one statusbar.
*
* The toolbars, menubar, and statusbar can be created by the
* KMainWindow and - unlike the old KMainWindow - may, but do not
* have to, be deleted by you. KMainWindow will handle that internally.
*
* Height and width can be operated independently from each other. Simply
* define the minimum/maximum height/width of your main widget and
* KMainWindow will take this into account. For fixed size windows set
* your main widget to a fixed size.
*
* Fixed aspect ratios (heightForWidth()) and fixed width widgets are
* not supported.
*
* KMainWindow will set icon, mini icon and caption, which it gets
* from KApplication. It provides full session management, and
* will save its position, geometry and positions of toolbars and
* menubar on logout. If you want to save additional data, reimplement
* saveProperties() and (to read them again on next login)
* readProperties(). To save special data about your data, reimplement
* saveGlobalProperties(). To warn user that application or
* windows have unsaved data on close or logout, reimplement
* queryClose() and/or queryExit().
*
* You have to implement session restoring also in your main() function.
* There are also kRestoreMainWindows convenience functions which
* can do this for you and restore all your windows on next login.
*
* Note that KMainWindow uses KGlobal::ref() and KGlobal::deref() so that closing
* the last mainwindow will quit the application unless there is still something
* that holds a ref in KGlobal - like a KIO job, or a systray icon.
*
* @see KApplication
* @author Reginald Stadlbauer (reggie@kde.org) Stephan Kulow (coolo@kde.org), Matthias Ettrich (ettrich@kde.org), Chris Schlaeger (cs@kde.org), Sven Radej (radej@kde.org). Maintained by David Faure (faure@kde.org)
*/
class KDEUI_EXPORT KMainWindow : public QMainWindow
{
friend class KMWSessionManager;
friend class DockResizeListener;
KDEUI_DECLARE_PRIVATE(KMainWindow)
Q_OBJECT
Q_PROPERTY( bool hasMenuBar READ hasMenuBar )
Q_PROPERTY( bool autoSaveSettings READ autoSaveSettings )
Q_PROPERTY( QString autoSaveGroup READ autoSaveGroup )
public:
/**
* Construct a main window.
*
* @param parent The widget parent. This is usually 0 but it may also be the window
* group leader. In that case, the KMainWindow becomes sort of a
* secondary window.
*
* @param f Specify the window flags. The default is none.
*
* Note that a KMainWindow per-default is created with the
* WA_DeleteOnClose attribute, i.e. it is automatically destroyed when the
* window is closed. If you do not want this behavior, call
* setAttribute(Qt::WA_DeleteOnClose, false);
*
* KMainWindows must be created on the heap with 'new', like:
* \code
* KMainWindow *kmw = new KMainWindow(...);
* kmw->setObjectName(...);
* \endcode
*
* IMPORTANT: For session management and window management to work
* properly, all main windows in the application should have a
* different name. If you don't do it, KMainWindow will create
* a unique name, but it's recommended to explicitly pass a window name that will
* also describe the type of the window. If there can be several windows of the same
* type, append '#' (hash) to the name, and KMainWindow will replace it with numbers to make
* the names unique. For example, for a mail client which has one main window showing
* the mails and folders, and which can also have one or more windows for composing
* mails, the name for the folders window should be e.g. "mainwindow" and
* for the composer windows "composer#".
*
*/
explicit KMainWindow( QWidget* parent = 0, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS );
/**
* \brief Destructor.
*
* Will also destroy the toolbars, and menubar if
* needed.
*/
virtual ~KMainWindow();
/**
* Retrieve the standard help menu.
*
* It contains entries for the
* help system (activated by F1), an optional "What's This?" entry
* (activated by Shift F1), an application specific dialog box,
* and an "About KDE" dialog box.
*
* Example (adding a standard help menu to your application):
* \code
* KMenu *help = helpMenu( <myTextString> );
* menuBar()->addMenu( help );
* \endcode
*
* @param aboutAppText The string that is used in the application
* specific dialog box. If you leave this string empty the
* information in the global KAboutData of the
* application will be used to make a standard dialog box.
*
* @param showWhatsThis Set this to false if you do not want to include
* the "What's This" menu entry.
*
* @return A standard help menu.
* @deprecated use KHelpMenu directly
*/
KDEUI_DEPRECATED KMenu* helpMenu(const QString &aboutAppText = QString(),
bool showWhatsThis = true );
/**
* Returns the help menu. Creates a standard help menu if none exists yet.
*
* It contains entries for the
* help system (activated by F1), an optional "What's This?" entry
* (activated by Shift F1), an application specific dialog box,
* and an "About KDE" dialog box. You must create the application
* specific dialog box yourself. When the "About application"
* menu entry is activated, a signal will trigger the
* showAboutApplication slot. See showAboutApplication for more
* information.
*
* Example (adding a help menu to your application):
* \code
* menuBar()->addMenu( customHelpMenu() );
* \endcode
*
* @param showWhatsThis Set this to @p false if you do not want to include
* the "What's This" menu entry.
*
* @return A standard help menu.
* @deprecated use XMLGUI instead, or KHelpMenu directly
*/
KDEUI_DEPRECATED KMenu* customHelpMenu( bool showWhatsThis = true );
/**
* If the session did contain so high a number, @p true is returned,
* else @p false.
* @see restore()
**/
static bool canBeRestored( int number );
/**
* Returns the className() of the @p number of the toplevel window which
* should be restored.
*
* This is only useful if your application uses
* different kinds of toplevel windows.
*/
static const QString classNameOfToplevel( int number );
/**
* Try to restore the toplevel widget as defined by @p number (1..X).
*
* You should call canBeRestored() first.
*
* If the session did not contain so high a number, the configuration
* is not changed and @p false returned.
*
* That means clients could simply do the following:
* \code
* if (qApp->isSessionRestored()){
* int n = 1;
* while (KMainWindow::canBeRestored(n)){
* (new childMW)->restore(n);
* n++;
* }
* } else {
* // create default application as usual
* }
* \endcode
* Note that if @p show is true (default), QWidget::show() is called
* implicitly in restore.
*
* With this you can easily restore all toplevel windows of your
* application.
*
* If your application uses different kinds of toplevel
* windows, then you can use KMainWindow::classNameOfToplevel(n)
* to determine the exact type before calling the childMW
* constructor in the example from above.
*
* <i>Note that you don't need to deal with this function. Use the
* kRestoreMainWindows() convenience template function instead!</i>
* @see kRestoreMainWindows()
* @see #RESTORE
* @see readProperties()
* @see canBeRestored()
*/
bool restore( int number, bool show = true );
/**
* Returns true, if there is a menubar
*/
bool hasMenuBar();
/**
* List of members of KMainWindow class.
*/
static QList<KMainWindow*> memberList();
/**
* Returns a pointer to the toolbar with the specified name.
* This refers to toolbars created dynamically from the XML UI
* framework. If the toolbar does not exist one will be created.
*
* @param name The internal name of the toolbar. If no name is
* specified "mainToolBar" is assumed.
*
* @return A pointer to the toolbar
**/
KToolBar *toolBar( const QString& name = QString() );
/**
* @return A list of all toolbars for this window
*/
QList<KToolBar*> toolBars() const;
/**
* Call this to enable "auto-save" of toolbar/menubar/statusbar settings
* (and optionally window size).
* If the *bars were moved around/shown/hidden when the window is closed,
* saveMainWindowSettings( KConfigGroup(KSharedConfig::openConfig(), groupName) ) will be called.
*
* @param groupName a name that identifies this "type of window".
* You can have several types of window in the same application.
*
* @param saveWindowSize set it to true to include the window size
* when saving.
*
* Typically, you will call setAutoSaveSettings() in your
* KMainWindow-inherited class constructor, and it will take care
* of restoring and saving automatically. Make sure you call this
* _after all_ your *bars have been created.
*
* To make sure that KMainWindow propertly obtains the default
* size of the window you should do the following:
* - Remove hard coded resize() calls in the constructor or main, they
* should be removed in favor of letting the automatic resizing
* determine the default window size. Hard coded window sizes will
* be wrong for users that have big fonts, use different styles,
* long/small translations, large toolbars, and other factors.
* - Put the setAutoSaveSettings ( or setupGUI() ) call after all widgets
* have been created and placed inside the main window (i.e. for 99% of
* apps setCentralWidget())
* - Widgets that inherit from QWidget (like game boards) should overload
* "virtual QSize sizeHint() const;" to specify a default size rather
* than letting QWidget::adjust use the default size of 0x0.
*/
void setAutoSaveSettings( const QString & groupName = QLatin1String("MainWindow"),
bool saveWindowSize = true );
/**
* Overload that lets you specify a KConfigGroup.
* This allows the settings to be saved into another file than KSharedConfig::openConfig().
* @since 4.1
*/
void setAutoSaveSettings(const KConfigGroup & group,
bool saveWindowSize = true);
/**
* Disable the auto-save-settings feature.
* You don't normally need to call this, ever.
*/
void resetAutoSaveSettings();
/**
* @return the current autosave setting, i.e. true if setAutoSaveSettings() was called,
* false by default or if resetAutoSaveSettings() was called.
*/
bool autoSaveSettings() const;
/**
* @return the group used for setting-autosaving.
* Only meaningful if setAutoSaveSettings(QString) was called.
* This can be useful for forcing a save or an apply, e.g. before and after
* using KEditToolbar.
*
* NOTE: you should rather use saveAutoSaveSettings() for saving or autoSaveConfigGroup() for loading.
* This method doesn't make sense if setAutoSaveSettings(KConfigGroup) was called.
*/
QString autoSaveGroup() const;
/**
* @return the group used for setting-autosaving.
* Only meaningful if setAutoSaveSettings() was called.
* This can be useful for forcing an apply, e.g. after using KEditToolbar.
* @since 4.1
*/
KConfigGroup autoSaveConfigGroup() const;
/**
* Read settings for statusbar, menubar and toolbar from their respective
* groups in the config file and apply them.
*
* @param config Config group to read the settings from.
* @param forceGlobal see the same argument in KToolBar::applySettings
*/
virtual void applyMainWindowSettings( const KConfigGroup &config, bool forceGlobal = false);
/**
* Save settings for statusbar, menubar and toolbar to their respective
* groups in the config group @p config.
*
* @param config Config group to save the settings to.
*/
void saveMainWindowSettings(KConfigGroup &config);
/**
* Returns the path under which this window's D-Bus object is exported.
* @since 4.0.1
*/
QString dbusName() const;
public Q_SLOTS:
/**
* Makes a KDE compliant caption (window title).
*
* @param caption Your caption. @em Do @em not include the application name
* in this string. It will be added automatically according to the KDE
* standard.
*/
virtual void setCaption( const QString &caption );
/**
* Makes a KDE compliant caption.
*
* @param caption Your caption. @em Do @em not include the application name
* in this string. It will be added automatically according to the KDE
* standard.
* @param modified Specify whether the document is modified. This displays
* an additional sign in the title bar, usually "**".
*/
virtual void setCaption( const QString &caption, bool modified );
/**
* Make a plain caption without any modifications.
*
* @param caption Your caption. This is the string that will be
* displayed in the window title.
*/
virtual void setPlainCaption( const QString &caption );
/**
* Open the help page for the application.
*
* The application name is
* used as a key to determine what to display and the system will attempt
* to open \<appName\>/index.html.
*
* This method is intended for use by a help button in the toolbar or
* components outside the regular help menu. Use helpMenu() when you
* want to provide access to the help system from the help menu.
*
* Example (adding a help button to the first toolbar):
*
* \code
* toolBar(0)->addAction(KDE::icon("help-contents"), i18n("Help"),
* this, SLOT(appHelpActivated()));
* \endcode
*
*/
void appHelpActivated( void );
/**
* Tell the main window that it should save its settings when being closed.
* This is part of the auto-save-settings feature.
* For everything related to toolbars this happens automatically,
* but you have to call setSettingsDirty() in the slot that toggles
* the visibility of the statusbar.
*/
void setSettingsDirty();
protected:
/**
* Reimplemented to catch QEvent::Polish in order to adjust the object name
* if needed, once all constructor code for the main window has run.
* Also reimplemented to catch when a QDockWidget is added or removed.
*/
virtual bool event( QEvent * event );
/**
* Reimplemented to call the queryClose() and queryExit() handlers.
*
* We recommend that you reimplement the handlers rather than closeEvent().
* If you do it anyway, ensure to call the base implementation to keep
* queryExit() running.
*/
virtual void closeEvent ( QCloseEvent *);
// KDE4 This seems to be flawed to me. Either the app has only one
// mainwindow, so queryClose() is enough, or if it can have more of them,
// then the windows should take care of themselves, and queryExit()
// would be useful only for the annoying 'really quit' dialog, which
// also doesn't make sense in apps with multiple mainwindows.
// And saving configuration in something called queryExit()? IMHO
// one can e.g. use QCoreApplication::aboutToQuit(), which if nothing else
// has at least better fitting name.
// See also KApplication::sessionSaving().
// This stuff should get changed somehow, so that it at least doesn't
// mess with session management.
/**
Called before the very last window is closed, either by the
user or indirectly by the session manager.
It is not recommended to do any user interaction in this
function other than indicating severe errors. Better ask the
user on queryClose() (see below).
A typical usage of queryExit() is to write configuration data back.
Note that the application may continue to run after queryExit()
(the user may have canceled a shutdown), so you should not do any cleanups
here. The purpose of queryExit() is purely to prepare the application
(with possible user interaction) so it can safely be closed later (without
user interaction).
If you need to do serious things on exit (like shutting a
dial-up connection down), connect to the signal
QCoreApplication::aboutToQuit().
Default implementation returns @p true. Returning @p false will
cancel the exiting. In the latter case, the last window will
remain visible. If KApplication::sessionSaving() is true, refusing
the exit will also cancel KDE logout.
@see queryClose()
@see KApplication::sessionSaving()
*/
virtual bool queryExit();
/**
Called before the window is closed, either by the user or indirectly by
the session manager.
The purpose of this function is to prepare the window in a way that it is
safe to close it, i.e. without the user losing some data.
Default implementation returns true. Returning @p false will cancel
the closing, and, if KApplication::sessionSaving() is true, it will also
cancel KDE logout.
Reimplement this function to prevent the user from losing data.
Example:
\code
switch ( KMessageBox::warningYesNoCancel( this,
i18n("Save changes to document foo?")) ) {
case KMessageBox::Yes :
// save document here. If saving fails, return false;
return true;
case KMessageBox::No :
return true;
default: // cancel
return false;
\endcode
Note that you should probably @em not actually close the document from
within this method, as it may be called by the session manager before the
session is saved. If the document is closed before the session save occurs,
its location might not be properly saved. In addition, the session shutdown
may be canceled, in which case the document should remain open.
@see queryExit()
@see KApplication::sessionSaving()
*/
virtual bool queryClose();
/**
* Save your instance-specific properties. The function is
* invoked when the session manager requests your application
* to save its state.
*
* Please reimplement these function in childclasses.
*
* Note: No user interaction is allowed
* in this function!
*
*/
virtual void saveProperties( KConfigGroup & ) {}
/**
* Read your instance-specific properties.
*
* Is called indirectly by restore().
*/
virtual void readProperties( const KConfigGroup & ) {}
/**
* Save your application-wide properties. The function is
* invoked when the session manager requests your application
* to save its state.
*
* This function is similar to saveProperties() but is only called for
* the very first main window, regardless how many main window are open.
* Override it if you need to save other data about your documents on
* session end. sessionConfig is a config to which that data should be
* saved. Normally, you don't need this function. But if you want to save
* data about your documents that are not in opened windows you might need
* it.
*
* Default implementation does nothing.
*/
virtual void saveGlobalProperties( KConfig* sessionConfig );
/**
* The counterpart of saveGlobalProperties().
*
* Read the application-specific properties in again.
*/
virtual void readGlobalProperties( KConfig* sessionConfig );
void savePropertiesInternal( KConfig*, int );
bool readPropertiesInternal( KConfig*, int );
/**
* For inherited classes
*/
bool settingsDirty() const;
/**
* For inherited classes
* @deprecated use KWindowConfig::saveWindowSize
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED void saveWindowSize( KConfigGroup &config ) const;
#endif
/**
* For inherited classes
* @deprecated use KWindowConfig::restoreWindowSize
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED void restoreWindowSize( const KConfigGroup & config );
#endif
protected Q_SLOTS:
/**
* This slot does nothing.
*
* It must be reimplemented if you want
* to use a custom About Application dialog box. This slot is
* connected to the About Application entry in the menu returned
* by customHelpMenu.
*
* Example:
* \code
*
* void MyMainLevel::setupInterface()
* {
* ..
* menuBar()->addMenu( customHelpMenu() );
* ..
* }
*
* void MyMainLevel::showAboutApplication()
* {
* <activate your custom dialog>
* }
* \endcode
* @deprecated use KHelpMenu
*/
virtual KDEUI_DEPRECATED void showAboutApplication();
/**
* This slot should only be called in case you reimplement closeEvent() and
* if you are using the "auto-save" feature. In all other cases,
* setSettingsDirty() should be called instead to benefit from the delayed
* saving.
*
* @see setAutoSaveSettings
* @see setSettingsDirty
*
* Example:
* \code
*
* void MyMainWindow::closeEvent( QCloseEvent *e )
* {
* // Save settings if auto-save is enabled, and settings have changed
* if ( settingsDirty() && autoSaveSettings() )
* saveAutoSaveSettings();
* ..
* }
* \endcode
*/
void saveAutoSaveSettings();
protected:
- KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WFlags f);
+ KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f);
KMainWindowPrivate * const k_ptr;
private:
Q_PRIVATE_SLOT(k_func(), void _k_shuttingDown())
Q_PRIVATE_SLOT(k_func(), void _k_slotSettingsChanged(int))
Q_PRIVATE_SLOT(k_func(), void _k_slotSaveAutoSaveSize())
};
/**
* @def RESTORE
* @ingroup KDEUIMacros
* Restores the last session. (To be used in your main function).
*
* If your client has only one kind of toplevel widgets (which
* should be pretty usual) then you can use this macro,
* which is provided for backwards compatibility with 3.1 and 3.0
* branches:
*
* \code
* if (qApp->isSessionRestored())
* RESTORE(childMW)
* else {
* // create default application as usual
* }
* \endcode
*
* The macro expects the type of your toplevel widget as argument.
*
* Since KDE4, you can also use kRestoreMainWindows(), which
* supports also clients with more than one kind of toplevel
* widgets.
*
* @see KMainWindow::restore()
* @see kRestoreMainWindows()
**/
#define RESTORE(type) { int n = 1;\
while (KMainWindow::canBeRestored(n)){\
(new type)->restore(n);\
n++;}}
/**
* @def KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS
* @ingroup KDEUIMacros
* Returns the maximal number of arguments that are actually
* supported by kRestoreMainWindows().
**/
#define KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS 3
/**
* Restores the last session. (To be used in your main function).
*
* These functions work also if you have more than one kind of toplevel
* widget (each derived from KMainWindow, of course).
*
* Imagine you have three kinds of toplevel widgets: the classes childMW1,
* childMW2 and childMW3. Than you can just do:
*
* \code
* if (qApp->isSessionRestored())
* kRestoreMainWindows< childMW1, childMW2, childMW3 >();
* else {
* // create default application as usual
* }
* \endcode
*
* kRestoreMainWindows<>() will create (on the heap) as many instances
* of your main windows as have existed in the last session and
* call KMainWindow::restore() with the correct arguments. Note that
* also QWidget::show() is called implicitly.
*
* Currently, these functions are provided for up to three
* template arguments. If you need more, tell us. To help you in
* deciding whether or not you can use kRestoreMainWindows, a
* define #KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS is provided.
*
* These global convenience functions (that come with a varying
* number of template arguments) are a replacement for the #RESTORE
* macro provided in earlier versions of KDE. The old #RESTORE macro
* is still provided for backwards compatibility.
*
* @see KMainWindow::restore()
* @see #RESTORE
* @see KMainWindow::classNameOfToplevel()
**/
template <typename T>
inline void kRestoreMainWindows() {
for ( int n = 1 ; KMainWindow::canBeRestored( n ) ; ++n ) {
const QString className = KMainWindow::classNameOfToplevel( n );
if ( className == QLatin1String( T::staticMetaObject.className() ) )
(new T)->restore( n );
}
}
template <typename T0, typename T1>
inline void kRestoreMainWindows() {
const char * classNames[2];
classNames[0] = T0::staticMetaObject.className();
classNames[1] = T1::staticMetaObject.className();
for ( int n = 1 ; KMainWindow::canBeRestored( n ) ; ++n ) {
const QString className = KMainWindow::classNameOfToplevel( n );
if ( className == QLatin1String( classNames[0] ) )
(new T0)->restore( n );
else if ( className == QLatin1String( classNames[1] ) )
(new T1)->restore( n );
}
}
template <typename T0, typename T1, typename T2>
inline void kRestoreMainWindows() {
const char * classNames[3];
classNames[0] = T0::staticMetaObject.className();
classNames[1] = T1::staticMetaObject.className();
classNames[2] = T2::staticMetaObject.className();
for ( int n = 1 ; KMainWindow::canBeRestored( n ) ; ++n ) {
const QString className = KMainWindow::classNameOfToplevel( n );
if ( className == QLatin1String( classNames[0] ) )
(new T0)->restore( n );
else if ( className == QLatin1String( classNames[1] ) )
(new T1)->restore( n );
else if ( className == QLatin1String( classNames[2] ) )
(new T2)->restore( n );
}
}
#endif
diff --git a/kdeui/widgets/ktabwidget.cpp b/kdeui/widgets/ktabwidget.cpp
index 17546b1be8..05d18e3517 100644
--- a/kdeui/widgets/ktabwidget.cpp
+++ b/kdeui/widgets/ktabwidget.cpp
@@ -1,724 +1,724 @@
/* This file is part of the KDE libraries
Copyright (C) 2003 Stephan Binner <binner@kde.org>
Copyright (C) 2003 Zack Rusin <zack@kde.org>
Copyright (C) 2009 Urs Wolfer <uwolfer @ 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.
*/
#include "ktabwidget.h"
#include <QApplication>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMouseEvent>
#include <QStyle>
#include <QStyleOption>
#include <QTextDocument>
#include <QWheelEvent>
#include <QtCore/QList>
#include <ksharedconfig.h>
#include <kiconloader.h>
#include <kstringhandler.h>
#include <kdebug.h>
#include <ktabbar.h>
#include <kconfiggroup.h>
class KTabWidget::Private
{
public:
enum {
ResizeEnabled = 0,
ResizeDisabled,
ResizeLater
} m_resizeSuspend;
Private( KTabWidget *parent )
: m_resizeSuspend(ResizeEnabled),
m_parent( parent ),
m_automaticResizeTabs( false ),
m_tabBarHidden( false )
{
KConfigGroup cg(KSharedConfig::openConfig(), "General");
m_maxLength = cg.readEntry("MaximumTabLength", 30);
m_minLength = cg.readEntry("MinimumTabLength", 3);
Q_ASSERT(m_maxLength >= m_minLength);
m_currentTabLength = m_minLength;
}
KTabWidget *m_parent;
bool m_automaticResizeTabs;
bool m_tabBarHidden;
int m_maxLength;
int m_minLength;
int m_currentTabLength;
//holds the full names of the tab, otherwise all we
//know about is the shortened name
QStringList m_tabNames;
bool isEmptyTabbarSpace( const QPoint & ) const;
void resizeTabs( int changedTabIndex = -1 );
void updateTab( int index );
void removeTab( int index );
void slotTabMoved( int from, int to );
};
bool KTabWidget::Private::isEmptyTabbarSpace( const QPoint &point ) const
{
if (m_parent->count() == 0) {
return true;
}
if (m_parent->tabBar()->isHidden()) {
return false;
}
QSize size( m_parent->tabBar()->sizeHint() );
if ( ( m_parent->tabPosition() == QTabWidget::North && point.y() < size.height() ) ||
( m_parent->tabPosition() == QTabWidget::South && point.y() > (m_parent->height() - size.height() ) ) ) {
QWidget *rightcorner = m_parent->cornerWidget( Qt::TopRightCorner );
if ( rightcorner && rightcorner->isVisible() ) {
if ( point.x() >= m_parent->width()-rightcorner->width() )
return false;
}
QWidget *leftcorner = m_parent->cornerWidget( Qt::TopLeftCorner );
if ( leftcorner && leftcorner->isVisible() ) {
if ( point.x() <= leftcorner->width() )
return false;
}
for ( int i = 0; i < m_parent->count(); ++i )
if ( m_parent->tabBar()->tabRect( i ).contains( m_parent->tabBar()->mapFromParent( point ) ) )
return false;
return true;
}
return false;
}
void KTabWidget::Private::removeTab( int index )
{
// prevent cascading resize slowness, not to mention crashes due to tab count()
// and m_tabNames.count() being out of sync!
m_resizeSuspend = ResizeDisabled;
// Need to do this here, rather than in tabRemoved(). Calling
// QTabWidget::removeTab() below may cause a relayout of the tab bar, which
// will call resizeTabs() immediately. If m_automaticResizeTabs is true,
// that will use the m_tabNames[] list before it has been updated to reflect
// the new tab arrangement. See bug 190528.
m_tabNames.removeAt( index );
m_parent->QTabWidget::removeTab( index );
const bool doResize = (m_resizeSuspend == ResizeLater) || m_automaticResizeTabs;
m_resizeSuspend = ResizeEnabled;
if (doResize) {
resizeTabs();
}
}
void KTabWidget::Private::resizeTabs( int changeTabIndex )
{
if (m_resizeSuspend != ResizeEnabled) {
m_resizeSuspend = ResizeLater;
return;
}
int newTabLength = m_maxLength;
if (m_automaticResizeTabs) {
// Calculate new max length
int lcw = 0, rcw = 0;
const int tabBarHeight = m_parent->tabBar()->sizeHint().height();
if (m_parent->cornerWidget(Qt::TopLeftCorner) &&
m_parent->cornerWidget( Qt::TopLeftCorner )->isVisible()) {
lcw = qMax(m_parent->cornerWidget(Qt::TopLeftCorner)->width(), tabBarHeight);
}
if (m_parent->cornerWidget(Qt::TopRightCorner) &&
m_parent->cornerWidget(Qt::TopRightCorner)->isVisible()) {
rcw = qMax( m_parent->cornerWidget(Qt::TopRightCorner)->width(), tabBarHeight);
}
const int maxTabBarWidth = m_parent->width() - lcw - rcw;
// binary search for the best fitting tab title length; some wiggling was
// required to make this behave in the face of rounding.
int newTabLengthHi = m_maxLength + 1;
int newTabLengthLo = m_minLength;
int prevTabLengthMid = -1;
while (true) {
int newTabLengthMid = (newTabLengthHi + newTabLengthLo) / 2;
if (prevTabLengthMid == newTabLengthMid) {
// no change, we're stuck due to rounding.
break;
}
prevTabLengthMid = newTabLengthMid;
if (m_parent->tabBarWidthForMaxChars(newTabLengthMid) > maxTabBarWidth) {
newTabLengthHi = newTabLengthMid;
} else {
newTabLengthLo = newTabLengthMid;
}
}
newTabLength = qMin(newTabLengthLo, m_maxLength);
}
// Update hinted or all tabs
if (m_currentTabLength != newTabLength) {
m_currentTabLength = newTabLength;
for (int i = 0; i < m_parent->count(); i++) {
updateTab(i);
}
} else if (changeTabIndex != -1) {
updateTab(changeTabIndex);
}
}
void KTabWidget::Private::updateTab( int index )
{
QString title = m_automaticResizeTabs ? m_tabNames[ index ] : m_parent->QTabWidget::tabText( index );
m_parent->setTabToolTip( index, QString() );
if ( title.length() > m_currentTabLength ) {
QString toolTipText = title;
// Remove '&'s, which are indicators for keyboard shortcuts in tab titles. "&&" is replaced by '&'.
for ( int i = toolTipText.indexOf( '&' ); i >= 0 && i < toolTipText.length(); i = toolTipText.indexOf( '&', i + 1 ) )
toolTipText.remove( i, 1 );
if ( Qt::mightBeRichText( toolTipText ) )
m_parent->setTabToolTip( index, Qt::escape( toolTipText ) );
else
m_parent->setTabToolTip( index, toolTipText );
}
title = KStringHandler::rsqueeze( title, m_currentTabLength ).leftJustified( m_minLength, ' ' );
if ( m_parent->QTabWidget::tabText( index ) != title )
m_parent->QTabWidget::setTabText( index, title );
}
void KTabWidget::Private::slotTabMoved(int from, int to)
{
/* called from Qt slot when Qt has moved the tab, so we only
need to adjust the m_tabNames list */
if (m_automaticResizeTabs) {
QString movedName = m_tabNames.takeAt(from);
m_tabNames.insert(to, movedName);
}
}
-KTabWidget::KTabWidget( QWidget *parent, Qt::WFlags flags )
+KTabWidget::KTabWidget( QWidget *parent, Qt::WindowFlags flags )
: QTabWidget( parent ),
d( new Private( this ) )
{
setWindowFlags( flags );
setTabBar( new KTabBar( this ) );
setObjectName( "tabbar" );
setAcceptDrops( true );
connect(tabBar(), SIGNAL(contextMenu(int,QPoint)), SLOT(contextMenu(int,QPoint)));
connect(tabBar(), SIGNAL(tabDoubleClicked(int)), SLOT(mouseDoubleClick(int)));
connect(tabBar(), SIGNAL(newTabRequest()), this, SIGNAL(mouseDoubleClick())); // #185487
connect(tabBar(), SIGNAL(mouseMiddleClick(int)), SLOT(mouseMiddleClick(int)));
connect(tabBar(), SIGNAL(initiateDrag(int)), SLOT(initiateDrag(int)));
connect(tabBar(), SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)), SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)));
connect(tabBar(), SIGNAL(receivedDropEvent(int,QDropEvent*)), SLOT(receivedDropEvent(int,QDropEvent*)));
connect(tabBar(), SIGNAL(moveTab(int,int)), SLOT(moveTab(int,int)));
connect(tabBar(), SIGNAL(tabMoved(int,int)), SLOT(slotTabMoved(int,int)));
connect(tabBar(), SIGNAL(tabCloseRequested(int)), SLOT(closeRequest(int)));
}
KTabWidget::~KTabWidget()
{
delete d;
}
/*void KTabWidget::insertTab( QWidget *child, const QString &label, int index )
{
QTabWidget::insertTab( child, label, index );
}
void KTabWidget::insertTab( QWidget *child, const QIcon& iconset, const QString &label, int index )
{
QTabWidget::insertTab( child, iconset, label, index );
}
void KTabWidget::insertTab( QWidget *child, QTab *tab, int index )
{
QTabWidget::insertTab( child, tab, index);
if ( d->m_automaticResizeTabs ) {
if ( index < 0 || index >= count() ) {
d->m_tabNames.append( tab->text() );
d->resizeTabs( d->m_tabNames.count()-1 );
}
else {
d->m_tabNames.insert( d->m_tabNames.at( index ), tab->text() );
d->resizeTabs( index );
}
}
}*/
void KTabWidget::setTabBarHidden( bool hide )
{
if (hide == isTabBarHidden())
return;
QWidget *rightcorner = cornerWidget( Qt::TopRightCorner );
QWidget *leftcorner = cornerWidget( Qt::TopLeftCorner );
d->m_tabBarHidden = hide;
if ( hide ) {
if ( leftcorner ) leftcorner->hide();
if ( rightcorner ) rightcorner->hide();
tabBar()->hide();
} else {
tabBar()->show();
if ( leftcorner ) leftcorner->show();
if ( rightcorner ) rightcorner->show();
}
}
bool KTabWidget::isTabBarHidden() const
{
return d->m_tabBarHidden;
}
void KTabWidget::setTabTextColor( int index, const QColor& color )
{
tabBar()->setTabTextColor( index, color );
}
QColor KTabWidget::tabTextColor( int index ) const
{
return tabBar()->tabTextColor( index );
}
#ifndef KDE_NO_DEPRECATED
void KTabWidget::setTabReorderingEnabled( bool on)
{
static_cast<KTabBar*>(tabBar())->setTabReorderingEnabled( on );
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KTabWidget::isTabReorderingEnabled() const
{
return static_cast<KTabBar*>(tabBar())->isTabReorderingEnabled();
}
#endif
#ifndef KDE_NO_DEPRECATED
void KTabWidget::setTabCloseActivatePrevious( bool previous)
{
static_cast<KTabBar*>(tabBar())->setTabCloseActivatePrevious( previous );
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KTabWidget::tabCloseActivatePrevious() const
{
return static_cast<KTabBar*>(tabBar())->tabCloseActivatePrevious();
}
#endif
int KTabWidget::tabBarWidthForMaxChars( int maxLength )
{
const int hframe = tabBar()->style()->pixelMetric( QStyle::PM_TabBarTabHSpace, 0L, tabBar() );
const QFontMetrics fm = tabBar()->fontMetrics();
int x = 0;
for ( int i = 0; i < count(); ++i ) {
QString newTitle = d->m_tabNames.value( i );
newTitle = KStringHandler::rsqueeze( newTitle, maxLength ).leftJustified( d->m_minLength, ' ' );
int lw = fm.width( newTitle );
int iw = 0;
if ( !tabBar()->tabIcon( i ).isNull() ) {
iw = tabBar()->tabIcon( i ).pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), QIcon::Normal ).width() + 4;
}
#ifndef KDE_NO_DEPRECATED
if ( isCloseButtonEnabled() ) {
// FIXME: how to get the size of the close button directly from the tabBar()?
iw += KIconLoader::SizeSmall * 3 / 2;
}
#endif
x += ( tabBar()->style()->sizeFromContents( QStyle::CT_TabBarTab, 0L,
QSize( qMax( lw + hframe + iw, QApplication::globalStrut().width() ), 0 ),
this ) ).width();
}
return x;
}
QString KTabWidget::tabText( int index ) const
{
if ( d->m_automaticResizeTabs ) {
if (index >= 0 && index < count()) {
if (index >= d->m_tabNames.count()) {
// Ooops, the tab exists, but tabInserted wasn't called yet.
// This can happen when inserting the first tab,
// and calling tabText from slotCurrentChanged,
// see KTabWidget_UnitTest.
const_cast<KTabWidget*>(this)->tabInserted(index);
}
return d->m_tabNames[ index ];
}
else
return QString();
}
else
return QTabWidget::tabText( index );
}
void KTabWidget::setTabText( int index, const QString &text )
{
if (text == tabText(index))
return;
if ( d->m_automaticResizeTabs ) {
tabBar()->setUpdatesEnabled(false); //no flicker
QTabWidget::setTabText( index, text );
if ( index != -1 ) {
if (index >= d->m_tabNames.count()) {
kWarning(240) << "setTabText(" << index << ") called but d->m_tabNames has only" << d->m_tabNames.count() << "entries";
while (index >= d->m_tabNames.count()) {
d->m_tabNames.append(QString());
}
}
d->m_tabNames[ index ] = text;
d->resizeTabs( index );
}
tabBar()->setUpdatesEnabled(true);
} else {
QTabWidget::setTabText( index, text );
}
}
void KTabWidget::dragEnterEvent( QDragEnterEvent *event )
{
if ( d->isEmptyTabbarSpace( event->pos() ) ) {
bool accept = false;
// The receivers of the testCanDecode() signal has to adjust
// 'accept' accordingly.
emit testCanDecode( event, accept);
event->setAccepted( accept );
return;
}
QTabWidget::dragEnterEvent( event );
}
void KTabWidget::dragMoveEvent( QDragMoveEvent *event )
{
if ( d->isEmptyTabbarSpace( event->pos() ) ) {
bool accept = false;
// The receivers of the testCanDecode() signal has to adjust
// 'accept' accordingly.
emit testCanDecode( event, accept);
event->setAccepted( accept );
return;
}
QTabWidget::dragMoveEvent( event );
}
void KTabWidget::dropEvent( QDropEvent *event )
{
if ( d->isEmptyTabbarSpace( event->pos() ) ) {
emit ( receivedDropEvent( event ) );
return;
}
QTabWidget::dropEvent( event );
}
#ifndef QT_NO_WHEELEVENT
void KTabWidget::wheelEvent( QWheelEvent *event )
{
if ( d->isEmptyTabbarSpace( event->pos() ) )
QCoreApplication::sendEvent( tabBar(), event );
else
QTabWidget::wheelEvent( event );
}
void KTabWidget::wheelDelta( int delta )
{
if ( count() < 2 )
return;
int page = currentIndex();
if ( delta < 0 )
page = (page + 1) % count();
else {
page--;
if ( page < 0 )
page = count() - 1;
}
setCurrentIndex( page );
}
#endif
void KTabWidget::mouseDoubleClickEvent( QMouseEvent *event )
{
if ( event->button() != Qt::LeftButton )
return;
if ( d->isEmptyTabbarSpace( event->pos() ) ) {
emit( mouseDoubleClick() );
return;
}
QTabWidget::mouseDoubleClickEvent( event );
}
void KTabWidget::mousePressEvent( QMouseEvent *event )
{
if ( event->button() == Qt::RightButton ) {
if ( d->isEmptyTabbarSpace( event->pos() ) ) {
emit( contextMenu( mapToGlobal( event->pos() ) ) );
return;
}
}
QTabWidget::mousePressEvent( event );
}
void KTabWidget::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() == Qt::MidButton ) {
if ( d->isEmptyTabbarSpace( event->pos() ) ) {
emit( mouseMiddleClick() );
return;
}
}
QTabWidget::mouseReleaseEvent( event );
}
void KTabWidget::receivedDropEvent( int index, QDropEvent *event )
{
emit( receivedDropEvent( widget( index ), event ) );
}
void KTabWidget::initiateDrag( int index )
{
emit( initiateDrag( widget( index ) ) );
}
void KTabWidget::contextMenu( int index, const QPoint &point )
{
emit( contextMenu( widget( index ), point ) );
}
void KTabWidget::mouseDoubleClick( int index )
{
emit( mouseDoubleClick( widget( index ) ) );
}
void KTabWidget::mouseMiddleClick( int index )
{
emit( mouseMiddleClick( widget( index ) ) );
}
void KTabWidget::moveTab( int from, int to )
{
setUpdatesEnabled(false);
const QString tablabel = tabText( from );
QWidget *w = widget( from );
const QColor color = tabTextColor( from );
const QIcon tabiconset = tabIcon( from );
const QString tabtooltip = tabToolTip( from );
const bool current = ( from == currentIndex() );
const bool enabled = isTabEnabled( from );
const bool blocked = blockSignals( true );
QWidget *fw = QApplication::focusWidget();
removeTab( from );
insertTab( to, w, tablabel );
// Don't lose focus due to moving the tab (#159295)
// (removeTab hides the widget, which gives focus to the "next in chain", could be anything)
if (w->isAncestorOf(fw)) {
fw->setFocus();
}
setTabIcon( to, tabiconset );
setTabText( to, tablabel );
setTabToolTip( to, tabtooltip );
setTabTextColor( to, color );
if ( current )
setCurrentIndex( to );
setTabEnabled( to, enabled );
if ( d->m_automaticResizeTabs ) {
d->resizeTabs( to );
}
blockSignals( blocked );
setUpdatesEnabled(true);
#ifndef KDE_NO_DEPRECATED
emit ( movedTab( from, to ) );
#endif
}
void KTabWidget::removePage( QWidget *widget )
{
// not just calling removeTab() because that one is also virtual.
const int index = indexOf(widget);
if ( d->m_automaticResizeTabs ) {
setUpdatesEnabled(false);
d->removeTab(index);
setUpdatesEnabled(true);
} else {
d->removeTab(index);
}
}
void KTabWidget::removeTab( int index )
{
if ( d->m_automaticResizeTabs ) {
const bool wasUpdatesEnabled = updatesEnabled();
setUpdatesEnabled(false);
d->removeTab( index );
setUpdatesEnabled(wasUpdatesEnabled);
} else {
d->removeTab( index );
}
}
#ifndef KDE_NO_DEPRECATED
void KTabWidget::setHoverCloseButton( bool button )
{
// deprecated
setTabsClosable( button );
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KTabWidget::hoverCloseButton() const
{
// deprecated
return false;
}
#endif
#ifndef KDE_NO_DEPRECATED
void KTabWidget::setHoverCloseButtonDelayed( bool delayed )
{
// deprecated
Q_UNUSED( delayed );
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KTabWidget::hoverCloseButtonDelayed() const
{
// deprecated
return tabsClosable();
}
#endif
#ifndef KDE_NO_DEPRECATED
void KTabWidget::setCloseButtonEnabled( bool enable )
{
static_cast<KTabBar*>( tabBar() )->setTabsClosable( enable );
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KTabWidget::isCloseButtonEnabled() const
{
return static_cast<KTabBar*>( tabBar() )->tabsClosable();
}
#endif
void KTabWidget::setAutomaticResizeTabs( bool enabled )
{
if ( d->m_automaticResizeTabs == enabled )
return;
setUpdatesEnabled(false);
d->m_automaticResizeTabs = enabled;
if ( enabled ) {
d->m_tabNames.clear();
for ( int i = 0; i < count(); ++i )
d->m_tabNames.append( tabBar()->tabText( i ) );
} else
for ( int i = 0; i < count(); ++i )
tabBar()->setTabText( i, d->m_tabNames[ i ] );
d->resizeTabs();
setUpdatesEnabled(true);
}
bool KTabWidget::automaticResizeTabs() const
{
return d->m_automaticResizeTabs;
}
void KTabWidget::closeRequest( int index )
{
emit( closeRequest( widget( index ) ) );
}
void KTabWidget::resizeEvent( QResizeEvent *event )
{
QTabWidget::resizeEvent( event );
d->resizeTabs();
}
void KTabWidget::tabInserted( int idx )
{
d->m_tabNames.insert( idx, tabBar()->tabText( idx ) );
}
void KTabWidget::tabRemoved( int idx )
{
Q_UNUSED(idx)
// d->m_tabNames is now updated in KTabWidget::Private::removeTab()
}
/* This function is kept only for BC reasons, it is not useful anymore */
#ifndef KDE_NO_DEPRECATED
void KTabWidget::currentChanged( int )
{
}
#endif
#include "moc_ktabwidget.cpp"
diff --git a/kdeui/widgets/ktabwidget.h b/kdeui/widgets/ktabwidget.h
index a31be129f4..60c4c2ddac 100644
--- a/kdeui/widgets/ktabwidget.h
+++ b/kdeui/widgets/ktabwidget.h
@@ -1,405 +1,405 @@
/* This file is part of the KDE libraries
Copyright (C) 2003 Stephan Binner <binner@kde.org>
Copyright (C) 2003 Zack Rusin <zack@kde.org>
Copyright (C) 2009 Urs Wolfer <uwolfer @ 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 KTABWIDGET_H
#define KTABWIDGET_H
#include <kdeui_export.h>
#include <QTabWidget>
class QTab;
/**
* \brief A widget containing multiple tabs
*
* It extends the Qt QTabWidget, providing extra optionally features such as close buttons when you hover
* over the icon in the tab, and also adds functionality such as responding to mouse wheel scroll events to switch
* the active tab.
*
* It is recommended to use KTabWidget instead of QTabWidget unless you have a good reason not to.
*
* See also the QTabWidget documentation.
*
* \image html ktabwidget.png "KDE Tab Widget"
*/
class KDEUI_EXPORT KTabWidget : public QTabWidget //krazy:exclude=qclasses
{
Q_OBJECT
#ifndef KDE_NO_DEPRECATED
Q_PROPERTY( bool tabReorderingEnabled READ isTabReorderingEnabled WRITE setTabReorderingEnabled )
Q_PROPERTY( bool hoverCloseButton READ hoverCloseButton WRITE setHoverCloseButton )
Q_PROPERTY( bool hoverCloseButtonDelayed READ hoverCloseButtonDelayed WRITE setHoverCloseButtonDelayed )
Q_PROPERTY( bool closeButtonEnabled READ isCloseButtonEnabled WRITE setCloseButtonEnabled )
Q_PROPERTY( bool tabCloseActivatePrevious READ tabCloseActivatePrevious WRITE setTabCloseActivatePrevious )
#endif
Q_PROPERTY( bool automaticResizeTabs READ automaticResizeTabs WRITE setAutomaticResizeTabs )
public:
/**
* Creates a new tab widget.
*
* @param parent The parent widgets.
* @param flags The Qt window flags @see QWidget.
*/
- explicit KTabWidget( QWidget *parent = 0, Qt::WFlags flags = 0 );
+ explicit KTabWidget( QWidget *parent = 0, Qt::WindowFlags flags = 0 );
/**
* Destroys the tab widget.
*/
virtual ~KTabWidget();
/**
* Set the tab of the given widget to \a color.
* This is simply a convenience method for QTabBar::setTabTextColor.
*/
void setTabTextColor( int index, const QColor& color );
/**
* Returns the tab color for the given widget.
* This is simply a convenience method for QTabBar::tabTextColor.
*/
QColor tabTextColor( int index ) const;
/**
* Returns true if tab ordering with the middle mouse button
* has been enabled.
*
* @deprecated Use QTabWidget::isMovable() instead.
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED bool isTabReorderingEnabled() const;
#endif
/**
* Returns true if the close button is shown on tabs
* when mouse is hovering over them.
*
* @deprecated Use QTabWidget::tabsClosable() instead.
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED bool hoverCloseButton() const;
#endif
/**
* Returns true if the close button is shown on tabs
* after a delay.
*
* @deprecated Use QTabWidget::setTabsClosable() instead.
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED bool hoverCloseButtonDelayed() const;
#endif
/**
* Returns true if the close button is shown on tabs.
*
* @since 4.1
*
* @deprecated Use QTabWidget::tabsClosable() instead.
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED bool isCloseButtonEnabled() const;
#endif
/**
* Returns true if closing the current tab activates the previous
* actice tab instead of the one to the right.
*
* @deprecated Use tabBar()->selectionBehaviorOnRemove() instead.
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED bool tabCloseActivatePrevious() const;
#endif
/**
* Returns true if calling setTitle() will resize tabs
* to the width of the tab bar.
*/
bool automaticResizeTabs() const;
/**
* If \a hide is true, the tabbar is hidden along with any corner
* widgets.
*/
void setTabBarHidden( bool hide );
/**
* Returns true if the tabbar was hidden by a call to setTabBarHidden( true ).
* Returns false if the widget itself is hidden, but no call to setTabBarHidden( true )
* has been made.
*/
bool isTabBarHidden() const;
/**
Reimplemented for internal reasons.
*
virtual void insertTab( QWidget *, const QString &, int index = -1 );
*!
Reimplemented for internal reasons.
*
virtual void insertTab( QWidget *child, const QIcon& iconset,
const QString &label, int index = -1 );
*!
Reimplemented for internal reasons.
*
virtual void insertTab( QWidget *, QTab *, int index = -1 );*/
/**
* Reimplemented for internal reasons.
*/
QString tabText( int ) const; // but it's not virtual...
#ifdef KDE3_SUPPORT
/**
* @deprecated use tabText
*/
#ifndef KDE_NO_DEPRECATED
inline KDEUI_DEPRECATED QString label( int index ) const { return tabText( index ); }
#endif
/**
* @deprecated use tabText
*/
#ifndef KDE_NO_DEPRECATED
inline KDEUI_DEPRECATED QString tabLabel( QWidget *w ) const { return tabText( indexOf( w ) ); }
#endif
/**
* @deprecated use setTabText
*/
#ifndef KDE_NO_DEPRECATED
inline KDEUI_DEPRECATED void setTabLabel( QWidget *w, const QString &l ) { setTabText( indexOf( w ),l ); }
#endif
#endif
/**
* Reimplemented for internal reasons.
*/
void setTabText( int , const QString & );
using QTabWidget::tabBar;
public Q_SLOTS:
/**
* Move a widget's tab from first to second specified index and emit
* signal movedTab( int, int ) afterwards.
*/
virtual void moveTab( int, int );
/**
* Removes the widget, reimplemented for
* internal reasons (keeping labels in sync).
* @deprecated since 4.0
*/
virtual QT_MOC_COMPAT void removePage ( QWidget * w );
/**
* Removes the widget, reimplemented for
* internal reasons (keeping labels in sync).
*/
virtual void removeTab(int index); // but it's not virtual in QTabWidget...
/**
* If \a enable is true, tab reordering with middle button will be enabled.
*
* Note that once enabled you shouldn't rely on previously queried
* currentPageIndex() or indexOf( QWidget * ) values anymore.
*
* You can connect to signal movedTab(int, int) which will notify
* you from which index to which index a tab has been moved.
*
* @deprecated Use QTabWidget::setMovable() instead.
*
* Note, however, that QTabWidget::setMovable(true) disables
* dragging tabs out of the KTabBar (e.g., dragging the tab
* URL from Konqueror to another application)!
*/
#ifndef KDE_NO_DEPRECATED
QT_MOC_COMPAT void setTabReorderingEnabled( bool enable );
#endif
/**
* If \a enable is true, a close button will be shown on mouse hover
* over tab icons which will emit signal closeRequest( QWidget * )
* when pressed.
*
* @deprecated Use QTabWidget::setTabsClosable() instead.
*/
#ifndef KDE_NO_DEPRECATED
QT_MOC_COMPAT void setHoverCloseButton( bool enable );
#endif
/**
* If \a delayed is true, a close button will be shown on mouse hover
* over tab icons after mouse double click delay else immediately.
*
* @deprecated Use QTabWidget::setTabsClosable() instead.
*/
#ifndef KDE_NO_DEPRECATED
QT_MOC_COMPAT void setHoverCloseButtonDelayed( bool delayed );
#endif
/**
* If enabled, a close button is available for each tab. The
* signal KTabWidget::closeRequest() is emitted, if the close button
* has been clicked.
*
* @since 4.1
*
* @deprecated Use QTabWidget::setTabsClosable() instead.
*/
#ifndef KDE_NO_DEPRECATED
QT_MOC_COMPAT void setCloseButtonEnabled( bool );
#endif
/**
* If \a previous is true, closing the current tab activates the
* previous active tab instead of the one to the right.
*
* @deprecated Use tabBar()->setSelectionBehaviorOnRemove() instead.
*/
#ifndef KDE_NO_DEPRECATED
QT_MOC_COMPAT void setTabCloseActivatePrevious( bool previous );
#endif
/**
* If \a enable is true, tabs will be resized to the width of the tab bar.
*
* Does not work reliably with "QTabWidget* foo=new KTabWidget()" and if
* you change tabs via the tabbar or by accessing tabs directly.
*/
void setAutomaticResizeTabs( bool enable );
Q_SIGNALS:
/**
* Connect to this and set accept to true if you can and want to decode the event.
*/
void testCanDecode(const QDragMoveEvent *e, bool &accept /* result */);
/**
* Received an event in the empty space beside tabbar. Usually creates a new tab.
* This signal is only possible after testCanDecode and positive accept result.
*/
void receivedDropEvent( QDropEvent * );
/**
* Received an drop event on given widget's tab.
* This signal is only possible after testCanDecode and positive accept result.
*/
void receivedDropEvent( QWidget *, QDropEvent * );
/**
* Request to start a drag operation on the given tab.
*/
void initiateDrag( QWidget * );
/**
* The right mouse button was pressed over empty space besides tabbar.
*/
void contextMenu( const QPoint & );
/**
* The right mouse button was pressed over a widget.
*/
void contextMenu( QWidget *, const QPoint & );
#ifndef KDE_NO_DEPRECATED
/**
* A tab was moved from first to second index. This signal is only
* possible after you have called setTabReorderingEnabled( true ).
*/
void movedTab( int, int );
#endif
/**
* A double left mouse button click was performed over empty space besides tabbar.
* The signal is emitted on the second press of the mouse button, before the release.
*/
void mouseDoubleClick();
/**
* A double left mouse button click was performed over the widget.
* The signal is emitted on the second press of the mouse button, before the release.
*/
void mouseDoubleClick( QWidget * );
/**
* A middle mouse button click was performed over empty space besides tabbar.
* The signal is emitted on the release of the mouse button.
*/
void mouseMiddleClick();
/**
* A middle mouse button click was performed over the widget.
* The signal is emitted on the release of the mouse button.
*/
void mouseMiddleClick( QWidget * );
/**
* The close button of a widget's tab was clicked. This signal is
* only possible after you have called setCloseButtonEnabled( true ).
*/
void closeRequest( QWidget * );
protected:
virtual void mouseDoubleClickEvent( QMouseEvent* );
virtual void mousePressEvent( QMouseEvent* );
virtual void mouseReleaseEvent( QMouseEvent* );
virtual void dragEnterEvent( QDragEnterEvent* );
virtual void dragMoveEvent( QDragMoveEvent* );
virtual void dropEvent( QDropEvent* );
int tabBarWidthForMaxChars( int );
#ifndef QT_NO_WHEELEVENT
virtual void wheelEvent( QWheelEvent* );
#endif
virtual void resizeEvent( QResizeEvent* );
virtual void tabInserted( int );
virtual void tabRemoved ( int );
/**
* @deprecated This method has no effect and should not be called
*/
#ifndef KDE_NO_DEPRECATED
KDEUI_DEPRECATED void currentChanged( int );
#endif
protected Q_SLOTS:
virtual void receivedDropEvent( int, QDropEvent* );
virtual void initiateDrag( int );
virtual void contextMenu( int, const QPoint& );
virtual void mouseDoubleClick( int );
virtual void mouseMiddleClick( int );
virtual void closeRequest( int );
#ifndef QT_NO_WHEELEVENT
virtual void wheelDelta( int );
#endif
private:
class Private;
Private * const d;
Q_PRIVATE_SLOT(d, void slotTabMoved(int, int))
};
#endif
diff --git a/kdeui/widgets/ktoolbar.cpp b/kdeui/widgets/ktoolbar.cpp
index 7458c3e94b..0116cc1976 100644
--- a/kdeui/widgets/ktoolbar.cpp
+++ b/kdeui/widgets/ktoolbar.cpp
@@ -1,1432 +1,1432 @@
/* This file is part of the KDE libraries
Copyright
(C) 2000 Reginald Stadlbauer (reggie@kde.org)
(C) 1997, 1998 Stephan Kulow (coolo@kde.org)
(C) 1997, 1998 Mark Donohoe (donohoe@kde.org)
(C) 1997, 1998 Sven Radej (radej@kde.org)
(C) 1997, 1998 Matthias Ettrich (ettrich@kde.org)
(C) 1999 Chris Schlaeger (cs@kde.org)
(C) 1999 Kurt Granroth (granroth@kde.org)
(C) 2005-2006 Hamish Rodda (rodda@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 version 2 as published by the Free Software Foundation.
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 "ktoolbar.h"
#include <QtCore/QPointer>
#include <QApplication>
#include <QDesktopWidget>
#include <QFrame>
#include <QLayout>
#include <QMimeData>
#include <QDrag>
#include <QMouseEvent>
#include <QToolButton>
#include <QtXml/QDomElement>
#include <kaction.h>
#include <kactioncollection.h>
#include <kcoreauthorized.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kicontheme.h>
#include <kedittoolbar.h>
#include <kglobalsettings.h>
#include <kguiitem.h>
#include <kiconloader.h>
#include <klocalizedstring.h>
#include <kxmlguiwindow.h>
#include <kmenu.h>
#include <kstandardaction.h>
#include <ktoggleaction.h>
#include <kxmlguifactory.h>
#include <kconfiggroup.h>
/*
Toolbar settings (e.g. icon size or toolButtonStyle)
=====================================================
We have the following stack of settings (in order of priority) :
- user-specified settings (loaded/saved in KConfig)
- developer-specified settings in the XMLGUI file (if using xmlgui) (cannot change at runtime)
- KDE-global default (user-configurable; can change at runtime)
and when switching between kparts, they are saved as xml in memory,
which, in the unlikely case of no-kmainwindow-autosaving, could be
different from the user-specified settings saved in KConfig and would have
priority over it.
So, in summary, without XML:
Global config / User settings (loaded/saved in kconfig)
and with XML:
Global config / App-XML attributes / User settings (loaded/saved in kconfig)
And all those settings (except the KDE-global defaults) have to be stored in memory
since we cannot retrieve them at random points in time, not knowing the xml document
nor config file that holds these settings. Hence the iconSizeSettings and toolButtonStyleSettings arrays.
For instance, if you change the KDE-global default, whether this makes a change
on a given toolbar depends on whether there are settings at Level_AppXML or Level_UserSettings.
Only if there are no settings at those levels, should the change of KDEDefault make a difference.
*/
enum SettingLevel { Level_KDEDefault, Level_AppXML, Level_UserSettings,
NSettingLevels };
enum { Unset = -1 };
class KToolBar::Private
{
public:
Private(KToolBar *qq)
: q(qq),
isMainToolBar(false),
#ifndef KDE_NO_DEPRECATED
enableContext(true),
#endif
unlockedMovable(true),
contextOrient(0),
contextMode(0),
contextSize(0),
contextButtonTitle(0),
contextShowText(0),
contextButtonAction(0),
contextTop(0),
contextLeft(0),
contextRight(0),
contextBottom(0),
contextIcons(0),
contextTextRight(0),
contextText(0),
contextTextUnder(0),
contextLockAction(0),
dropIndicatorAction(0),
context(0),
dragAction(0)
{
}
void slotAppearanceChanged();
void slotContextAboutToShow();
void slotContextAboutToHide();
void slotContextLeft();
void slotContextRight();
void slotContextShowText();
void slotContextTop();
void slotContextBottom();
void slotContextIcons();
void slotContextText();
void slotContextTextRight();
void slotContextTextUnder();
void slotContextIconSize();
void slotLockToolBars(bool lock);
void init(bool readConfig = true, bool isMainToolBar = false);
QString getPositionAsString() const;
KMenu *contextMenu(const QPoint &globalPos);
void setLocked(bool locked);
void adjustSeparatorVisibility();
void loadKDESettings();
void applyCurrentSettings();
QAction *findAction(const QString &actionName, KXMLGUIClient **client = 0) const;
static Qt::ToolButtonStyle toolButtonStyleFromString(const QString& style);
static QString toolButtonStyleToString(Qt::ToolButtonStyle);
static Qt::ToolBarArea positionFromString(const QString& position);
KToolBar *q;
bool isMainToolBar : 1;
#ifndef KDE_NO_DEPRECATED
bool enableContext : 1;
#endif
bool unlockedMovable : 1;
static bool s_editable;
static bool s_locked;
QSet<KXMLGUIClient *> xmlguiClients;
QMenu* contextOrient;
QMenu* contextMode;
QMenu* contextSize;
QAction* contextButtonTitle;
QAction* contextShowText;
QAction* contextButtonAction;
QAction* contextTop;
QAction* contextLeft;
QAction* contextRight;
QAction* contextBottom;
QAction* contextIcons;
QAction* contextTextRight;
QAction* contextText;
QAction* contextTextUnder;
KToggleAction* contextLockAction;
QMap<QAction*,int> contextIconSizes;
class IntSetting
{
public:
IntSetting() {
for (int level = 0; level < NSettingLevels; ++level) {
values[level] = Unset;
}
}
int currentValue() const {
int val = Unset;
for (int level = 0; level < NSettingLevels; ++level) {
if (values[level] != Unset)
val = values[level];
}
return val;
}
// Default value as far as the user is concerned is kde-global + app-xml.
// If currentValue()==defaultValue() then nothing to write into kconfig.
int defaultValue() const {
int val = Unset;
for (int level = 0; level < Level_UserSettings; ++level) {
if (values[level] != Unset)
val = values[level];
}
return val;
}
QString toString() const {
QString str;
for (int level = 0; level < NSettingLevels; ++level) {
str += QString::number(values[level]) + ' ';
}
return str;
}
int& operator[](int index) { return values[index]; }
private:
int values[NSettingLevels];
};
IntSetting iconSizeSettings;
IntSetting toolButtonStyleSettings; // either Qt::ToolButtonStyle or -1, hence "int".
QList<QAction*> actionsBeingDragged;
QAction* dropIndicatorAction;
KMenu* context;
KAction* dragAction;
QPoint dragStartPosition;
};
bool KToolBar::Private::s_editable = false;
bool KToolBar::Private::s_locked = true;
void KToolBar::Private::init(bool readConfig, bool _isMainToolBar)
{
isMainToolBar = _isMainToolBar;
loadKDESettings();
// also read in our configurable settings (for non-xmlgui toolbars)
// KDE5: we can probably remove this, if people save settings then they load them too, e.g. using KMainWindow's autosave.
if (readConfig) {
KConfigGroup cg(KSharedConfig::openConfig(), QString());
q->applySettings(cg);
}
if (q->mainWindow()) {
// Get notified when settings change
connect(q, SIGNAL(allowedAreasChanged(Qt::ToolBarAreas)),
q->mainWindow(), SLOT(setSettingsDirty()));
connect(q, SIGNAL(iconSizeChanged(QSize)),
q->mainWindow(), SLOT(setSettingsDirty()));
connect(q, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
q->mainWindow(), SLOT(setSettingsDirty()));
connect(q, SIGNAL(movableChanged(bool)),
q->mainWindow(), SLOT(setSettingsDirty()));
connect(q, SIGNAL(orientationChanged(Qt::Orientation)),
q->mainWindow(), SLOT(setSettingsDirty()));
}
if (!KAuthorized::authorize("movable_toolbars"))
q->setMovable(false);
else
q->setMovable(!KToolBar::toolBarsLocked());
connect(q, SIGNAL(movableChanged(bool)),
q, SLOT(slotMovableChanged(bool)));
q->setAcceptDrops(true);
connect(KGlobalSettings::self(), SIGNAL(toolbarAppearanceChanged(int)),
q, SLOT(slotAppearanceChanged()));
connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()),
q, SLOT(slotAppearanceChanged()));
}
QString KToolBar::Private::getPositionAsString() const
{
// get all of the stuff to save
switch (q->mainWindow()->toolBarArea(const_cast<KToolBar*>(q))) {
case Qt::BottomToolBarArea:
return "Bottom";
case Qt::LeftToolBarArea:
return "Left";
case Qt::RightToolBarArea:
return "Right";
case Qt::TopToolBarArea:
default:
return "Top";
}
}
KMenu *KToolBar::Private::contextMenu(const QPoint &globalPos)
{
if (!context) {
context = new KMenu(q);
contextButtonTitle = context->addTitle(i18nc("@title:menu", "Show Text"));
contextShowText = context->addAction(QString(), q, SLOT(slotContextShowText()));
context->addTitle(i18nc("@title:menu", "Toolbar Settings"));
contextOrient = new KMenu(i18nc("Toolbar orientation", "Orientation"), context);
contextTop = contextOrient->addAction(i18nc("toolbar position string", "Top"), q, SLOT(slotContextTop()));
contextTop->setChecked(true);
contextLeft = contextOrient->addAction(i18nc("toolbar position string", "Left"), q, SLOT(slotContextLeft()));
contextRight = contextOrient->addAction(i18nc("toolbar position string", "Right"), q, SLOT(slotContextRight()));
contextBottom = contextOrient->addAction(i18nc("toolbar position string", "Bottom"), q, SLOT(slotContextBottom()));
QActionGroup* positionGroup = new QActionGroup(contextOrient);
foreach (QAction* action, contextOrient->actions()) {
action->setActionGroup(positionGroup);
action->setCheckable(true);
}
contextMode = new KMenu(i18n("Text Position"), context);
contextIcons = contextMode->addAction(i18n("Icons Only"), q, SLOT(slotContextIcons()));
contextText = contextMode->addAction(i18n("Text Only"), q, SLOT(slotContextText()));
contextTextRight = contextMode->addAction(i18n("Text Alongside Icons"), q, SLOT(slotContextTextRight()));
contextTextUnder = contextMode->addAction(i18n("Text Under Icons"), q, SLOT(slotContextTextUnder()));
QActionGroup* textGroup = new QActionGroup(contextMode);
foreach (QAction* action, contextMode->actions()) {
action->setActionGroup(textGroup);
action->setCheckable(true);
}
contextSize = new KMenu(i18n("Icon Size"), context);
contextIconSizes.insert(contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), q, SLOT(slotContextIconSize())),
iconSizeSettings.defaultValue());
// Query the current theme for available sizes
KIconTheme *theme = KIconLoader::global()->theme();
QList<int> avSizes;
if (theme) {
avSizes = theme->querySizes(isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
}
qSort(avSizes);
if (avSizes.count() < 10) {
// Fixed or threshold type icons
foreach (int it, avSizes) {
QString text;
if (it < 19)
text = i18n("Small (%1x%2)", it, it);
else if (it < 25)
text = i18n("Medium (%1x%2)", it, it);
else if (it < 35)
text = i18n("Large (%1x%2)", it, it);
else
text = i18n("Huge (%1x%2)", it, it);
// save the size in the contextIconSizes map
contextIconSizes.insert(contextSize->addAction(text, q, SLOT(slotContextIconSize())), it);
}
} else {
// Scalable icons.
const int progression[] = { 16, 22, 32, 48, 64, 96, 128, 192, 256 };
for (uint i = 0; i < 9; i++) {
foreach (int it, avSizes) {
if (it >= progression[ i ]) {
QString text;
if (it < 19)
text = i18n("Small (%1x%2)", it, it);
else if (it < 25)
text = i18n("Medium (%1x%2)", it, it);
else if (it < 35)
text = i18n("Large (%1x%2)", it, it);
else
text = i18n("Huge (%1x%2)", it, it);
// save the size in the contextIconSizes map
contextIconSizes.insert(contextSize->addAction(text, q, SLOT(slotContextIconSize())), it);
break;
}
}
}
}
QActionGroup* sizeGroup = new QActionGroup(contextSize);
foreach (QAction* action, contextSize->actions()) {
action->setActionGroup(sizeGroup);
action->setCheckable(true);
}
if (!q->toolBarsLocked() && !q->isMovable())
unlockedMovable = false;
delete contextLockAction;
contextLockAction = new KToggleAction(KDE::icon("system-lock-screen"), i18n("Lock Toolbar Positions"), q);
contextLockAction->setChecked(q->toolBarsLocked());
connect(contextLockAction, SIGNAL(toggled(bool)), q, SLOT(slotLockToolBars(bool)));
// Now add the actions to the menu
context->addMenu(contextMode);
context->addMenu(contextSize);
context->addMenu(contextOrient);
context->addSeparator();
connect(context, SIGNAL(aboutToShow()), q, SLOT(slotContextAboutToShow()));
}
contextButtonAction = q->actionAt(q->mapFromGlobal(globalPos));
if (contextButtonAction) {
contextShowText->setText(contextButtonAction->text());
contextShowText->setIcon(contextButtonAction->icon());
contextShowText->setCheckable(true);
}
contextOrient->menuAction()->setVisible(!q->toolBarsLocked());
// Unplugging a submenu from abouttohide leads to the popupmenu floating around
// So better simply call that code from after exec() returns (DF)
//connect(context, SIGNAL(aboutToHide()), this, SLOT(slotContextAboutToHide()));
return context;
}
void KToolBar::Private::setLocked(bool locked)
{
if (unlockedMovable)
q->setMovable(!locked);
}
void KToolBar::Private::adjustSeparatorVisibility()
{
bool visibleNonSeparator = false;
int separatorToShow = -1;
for (int index = 0; index < q->actions().count(); ++index) {
QAction* action = q->actions()[ index ];
if (action->isSeparator()) {
if (visibleNonSeparator) {
separatorToShow = index;
visibleNonSeparator = false;
} else {
action->setVisible(false);
}
} else if (!visibleNonSeparator) {
if (action->isVisible()) {
visibleNonSeparator = true;
if (separatorToShow != -1) {
q->actions()[ separatorToShow ]->setVisible(true);
separatorToShow = -1;
}
}
}
}
if (separatorToShow != -1)
q->actions()[ separatorToShow ]->setVisible(false);
}
Qt::ToolButtonStyle KToolBar::Private::toolButtonStyleFromString(const QString & _style)
{
QString style = _style.toLower();
if (style == "textbesideicon" || style == "icontextright")
return Qt::ToolButtonTextBesideIcon;
else if (style == "textundericon" || style == "icontextbottom")
return Qt::ToolButtonTextUnderIcon;
else if (style == "textonly")
return Qt::ToolButtonTextOnly;
else
return Qt::ToolButtonIconOnly;
}
QString KToolBar::Private::toolButtonStyleToString(Qt::ToolButtonStyle style)
{
switch(style)
{
case Qt::ToolButtonIconOnly:
default:
return "IconOnly";
case Qt::ToolButtonTextBesideIcon:
return "TextBesideIcon";
case Qt::ToolButtonTextOnly:
return "TextOnly";
case Qt::ToolButtonTextUnderIcon:
return "TextUnderIcon";
}
}
Qt::ToolBarArea KToolBar::Private::positionFromString(const QString& position)
{
Qt::ToolBarArea newposition = Qt::TopToolBarArea;
if (position == QLatin1String("left")) {
newposition = Qt::LeftToolBarArea;
} else if (position == QLatin1String("bottom")) {
newposition = Qt::BottomToolBarArea;
} else if (position == QLatin1String("right")) {
newposition = Qt::RightToolBarArea;
}
return newposition;
}
// Global setting was changed
void KToolBar::Private::slotAppearanceChanged()
{
loadKDESettings();
applyCurrentSettings();
}
void KToolBar::Private::loadKDESettings()
{
iconSizeSettings[Level_KDEDefault] = q->iconSizeDefault();
if (isMainToolBar) {
toolButtonStyleSettings[Level_KDEDefault] = q->toolButtonStyleSetting();
} else {
const QString fallBack = toolButtonStyleToString(Qt::ToolButtonTextBesideIcon);
/**
TODO: if we get complaints about text beside icons on small screens,
try the following code out on such systems - aseigo.
// if we are on a small screen with a non-landscape ratio, then
// we revert to text under icons since width is probably not our
// friend in such cases
QDesktopWidget *desktop = QApplication::desktop();
QRect screenGeom = desktop->screenGeometry(desktop->primaryScreen());
qreal ratio = screenGeom.width() / qreal(screenGeom.height());
if (screenGeom.width() < 1024 && ratio <= 1.4) {
fallBack = "TextUnderIcon";
}
**/
KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style");
const QString value = group.readEntry("ToolButtonStyleOtherToolbars", fallBack);
toolButtonStyleSettings[Level_KDEDefault] = KToolBar::Private::toolButtonStyleFromString(value);
}
}
// Call this after changing something in d->iconSizeSettings or d->toolButtonStyleSettings
void KToolBar::Private::applyCurrentSettings()
{
//kDebug() << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue();
const int currentIconSize = iconSizeSettings.currentValue();
q->setIconSize(QSize(currentIconSize, currentIconSize));
//kDebug() << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" << toolButtonStyleSettings.currentValue();
q->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(toolButtonStyleSettings.currentValue()));
// And remember to save the new look later
KMainWindow *kmw = q->mainWindow();
if (kmw)
kmw->setSettingsDirty();
}
QAction *KToolBar::Private::findAction(const QString &actionName, KXMLGUIClient **clientOut) const
{
foreach (KXMLGUIClient* client, xmlguiClients) {
QAction* action = client->actionCollection()->action(actionName);
if (action) {
if (clientOut) {
*clientOut = client;
}
return action;
}
}
return 0;
}
void KToolBar::Private::slotContextAboutToShow()
{
/**
* The idea here is to reuse the "static" part of the menu to save time.
* But the "Toolbars" action is dynamic (can be a single action or a submenu)
* and ToolBarHandler::setupActions() deletes it, so better not keep it around.
* So we currently plug/unplug the last two actions of the menu.
* Another way would be to keep around the actions and plug them all into a (new each time) popupmenu.
*/
KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow());
// try to find "configure toolbars" action
QAction *configureAction = 0;
const char* actionName = KStandardAction::name(KStandardAction::ConfigureToolbars);
configureAction = findAction(actionName);
if (!configureAction && kmw) {
configureAction = kmw->actionCollection()->action(actionName);
}
if (configureAction) {
context->addAction(configureAction);
}
context->addAction(contextLockAction);
if (kmw) {
kmw->setupToolbarMenuActions();
// Only allow hiding a toolbar if the action is also plugged somewhere else (e.g. menubar)
QAction *tbAction = kmw->toolBarMenuAction();
if (!q->toolBarsLocked() && tbAction && tbAction->associatedWidgets().count() > 0)
context->addAction(tbAction);
}
KEditToolBar::setGlobalDefaultToolBar(q->QObject::objectName().toLatin1().constData());
// Check the actions that should be checked
switch (q->toolButtonStyle()) {
case Qt::ToolButtonIconOnly:
default:
contextIcons->setChecked(true);
break;
case Qt::ToolButtonTextBesideIcon:
contextTextRight->setChecked(true);
break;
case Qt::ToolButtonTextOnly:
contextText->setChecked(true);
break;
case Qt::ToolButtonTextUnderIcon:
contextTextUnder->setChecked(true);
break;
}
QMapIterator< QAction*, int > it = contextIconSizes;
while (it.hasNext()) {
it.next();
if (it.value() == q->iconSize().width()) {
it.key()->setChecked(true);
break;
}
}
switch (q->mainWindow()->toolBarArea(q)) {
case Qt::BottomToolBarArea:
contextBottom->setChecked(true);
break;
case Qt::LeftToolBarArea:
contextLeft->setChecked(true);
break;
case Qt::RightToolBarArea:
contextRight->setChecked(true);
break;
default:
case Qt::TopToolBarArea:
contextTop->setChecked(true);
break;
}
const bool showButtonSettings = contextButtonAction
&& !contextShowText->text().isEmpty()
&& contextTextRight->isChecked();
contextButtonTitle->setVisible(showButtonSettings);
contextShowText->setVisible(showButtonSettings);
if (showButtonSettings) {
contextShowText->setChecked(contextButtonAction->priority() >= QAction::NormalPriority);
}
}
void KToolBar::Private::slotContextAboutToHide()
{
// We have to unplug whatever slotContextAboutToShow plugged into the menu.
// Unplug the toolbar menu action
KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow());
if (kmw && kmw->toolBarMenuAction()) {
if (kmw->toolBarMenuAction()->associatedWidgets().count() > 1) {
context->removeAction(kmw->toolBarMenuAction());
}
}
// Unplug the configure toolbars action too, since it's afterwards anyway
QAction *configureAction = 0;
const char* actionName = KStandardAction::name(KStandardAction::ConfigureToolbars);
configureAction = findAction(actionName);
if (!configureAction && kmw) {
configureAction = kmw->actionCollection()->action(actionName);
}
if (configureAction) {
context->removeAction(configureAction);
}
context->removeAction(contextLockAction);
}
void KToolBar::Private::slotContextLeft()
{
q->mainWindow()->addToolBar(Qt::LeftToolBarArea, q);
}
void KToolBar::Private::slotContextRight()
{
q->mainWindow()->addToolBar(Qt::RightToolBarArea, q);
}
void KToolBar::Private::slotContextShowText()
{
Q_ASSERT(contextButtonAction);
const QAction::Priority priority = contextShowText->isChecked()
? QAction::NormalPriority : QAction::LowPriority;
contextButtonAction->setPriority(priority);
// Find to which xml file and componentData the action belongs to
KComponentData componentData;
QString filename;
KXMLGUIClient *client;
if (findAction(contextButtonAction->objectName(), &client)) {
componentData = client->componentData();
filename = client->xmlFile();
}
if (filename.isEmpty()) {
componentData = KComponentData::mainComponent();
filename = componentData.componentName() + "ui.rc";
}
// Save the priority state of the action
const QString configFile = KXMLGUIFactory::readConfigFile(filename, componentData);
QDomDocument document;
document.setContent(configFile);
QDomElement elem = KXMLGUIFactory::actionPropertiesElement(document);
QDomElement actionElem = KXMLGUIFactory::findActionByName(elem, contextButtonAction->objectName(), true);
actionElem.setAttribute("priority", priority);
KXMLGUIFactory::saveConfigFile(document, filename, componentData);
}
void KToolBar::Private::slotContextTop()
{
q->mainWindow()->addToolBar(Qt::TopToolBarArea, q);
}
void KToolBar::Private::slotContextBottom()
{
q->mainWindow()->addToolBar(Qt::BottomToolBarArea, q);
}
void KToolBar::Private::slotContextIcons()
{
q->setToolButtonStyle(Qt::ToolButtonIconOnly);
toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
}
void KToolBar::Private::slotContextText()
{
q->setToolButtonStyle(Qt::ToolButtonTextOnly);
toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
}
void KToolBar::Private::slotContextTextUnder()
{
q->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
}
void KToolBar::Private::slotContextTextRight()
{
q->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
}
void KToolBar::Private::slotContextIconSize()
{
QAction* action = qobject_cast<QAction*>(q->sender());
if (action && contextIconSizes.contains(action)) {
const int iconSize = contextIconSizes.value(action);
q->setIconDimensions(iconSize);
}
}
void KToolBar::Private::slotLockToolBars(bool lock)
{
q->setToolBarsLocked(lock);
}
KToolBar::KToolBar(QWidget *parent, bool isMainToolBar, bool readConfig)
: QToolBar(parent),
d(new Private(this))
{
d->init(readConfig, isMainToolBar);
// KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
if (QMainWindow* mw = qobject_cast<QMainWindow*>(parent))
mw->addToolBar(this);
}
KToolBar::KToolBar(const QString& objectName, QWidget *parent, bool readConfig)
: QToolBar(parent),
d(new Private(this))
{
setObjectName(objectName);
// mainToolBar -> isMainToolBar = true -> buttonStyle is configurable
// others -> isMainToolBar = false -> ### hardcoded default for buttonStyle !!! should be configurable? -> hidden key added
d->init(readConfig, objectName == "mainToolBar");
// KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
if (QMainWindow* mw = qobject_cast<QMainWindow*>(parent))
mw->addToolBar(this);
}
KToolBar::KToolBar(const QString& objectName, QMainWindow* parent, Qt::ToolBarArea area,
bool newLine, bool isMainToolBar, bool readConfig)
: QToolBar(parent),
d(new Private(this))
{
setObjectName(objectName);
d->init(readConfig, isMainToolBar);
if (newLine)
mainWindow()->addToolBarBreak(area);
mainWindow()->addToolBar(area, this);
if (newLine)
mainWindow()->addToolBarBreak(area);
}
KToolBar::~KToolBar()
{
delete d->contextLockAction;
delete d;
}
#ifndef KDE_NO_DEPRECATED
void KToolBar::setContextMenuEnabled(bool enable)
{
d->enableContext = enable;
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KToolBar::contextMenuEnabled() const
{
return d->enableContext;
}
#endif
void KToolBar::saveSettings(KConfigGroup &cg)
{
Q_ASSERT(!cg.name().isEmpty());
cg.deleteEntry("Hidden"); // remove old key to avoid bugs from the compat code in applySettings. KDE5: remove.
const int currentIconSize = iconSize().width();
//kDebug() << objectName() << currentIconSize << d->iconSizeSettings.toString() << "defaultValue=" << d->iconSizeSettings.defaultValue();
if (!cg.hasDefault("IconSize") && currentIconSize == d->iconSizeSettings.defaultValue()) {
cg.revertToDefault("IconSize");
d->iconSizeSettings[Level_UserSettings] = Unset;
} else {
cg.writeEntry("IconSize", currentIconSize);
d->iconSizeSettings[Level_UserSettings] = currentIconSize;
}
const Qt::ToolButtonStyle currentToolButtonStyle = toolButtonStyle();
if (!cg.hasDefault("ToolButtonStyle") && currentToolButtonStyle == d->toolButtonStyleSettings.defaultValue()) {
cg.revertToDefault("ToolButtonStyle");
d->toolButtonStyleSettings[Level_UserSettings] = Unset;
} else {
cg.writeEntry("ToolButtonStyle", d->toolButtonStyleToString(currentToolButtonStyle));
d->toolButtonStyleSettings[Level_UserSettings] = currentToolButtonStyle;
}
}
#ifndef KDE_NO_DEPRECATED
void KToolBar::setXMLGUIClient(KXMLGUIClient *client)
{
d->xmlguiClients.clear();
d->xmlguiClients << client;
}
#endif
void KToolBar::addXMLGUIClient( KXMLGUIClient *client )
{
d->xmlguiClients << client;
}
void KToolBar::removeXMLGUIClient( KXMLGUIClient *client )
{
d->xmlguiClients.remove(client);
}
void KToolBar::contextMenuEvent(QContextMenuEvent* event)
{
#ifndef KDE_NO_DEPRECATED
if (mainWindow() && d->enableContext) {
QPointer<KToolBar> guard(this);
const QPoint globalPos = event->globalPos();
d->contextMenu(globalPos)->exec(globalPos);
// "Configure Toolbars" recreates toolbars, so we might not exist anymore.
if (guard) {
d->slotContextAboutToHide();
}
return;
}
#endif
QToolBar::contextMenuEvent(event);
}
Qt::ToolButtonStyle KToolBar::toolButtonStyleSetting()
{
KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style");
const QString fallback = Private::toolButtonStyleToString(Qt::ToolButtonTextBesideIcon);
return KToolBar::Private::toolButtonStyleFromString(group.readEntry("ToolButtonStyle", fallback));
}
void KToolBar::loadState(const QDomElement &element)
{
QMainWindow *mw = mainWindow();
if (!mw)
return;
{
QDomNode textNode = element.namedItem("text");
QByteArray text;
QByteArray context;
if (textNode.isElement())
{
QDomElement textElement = textNode.toElement();
text = textElement.text().toUtf8();
context = textElement.attribute("context").toUtf8();
}
else
{
textNode = element.namedItem("Text");
if (textNode.isElement())
{
QDomElement textElement = textNode.toElement();
text = textElement.text().toUtf8();
context = textElement.attribute("context").toUtf8();
}
}
QString i18nText;
if (!text.isEmpty() && !context.isEmpty())
i18nText = i18nc(context, text);
else if (!text.isEmpty())
i18nText = i18n(text);
if (!i18nText.isEmpty())
setWindowTitle(i18nText);
}
/*
This method is called in order to load toolbar settings from XML.
However this can be used in two rather different cases:
- for the initial loading of the app's XML. In that case the settings
are only the defaults (Level_AppXML), the user's KConfig settings will override them
- for later re-loading when switching between parts in KXMLGUIFactory.
In that case the XML contains the final settings, not the defaults.
We do need the defaults, and the toolbar might have been completely
deleted and recreated meanwhile. So we store the app-default settings
into the XML.
*/
bool loadingAppDefaults = true;
if (element.hasAttribute("tempXml")) {
// this isn't the first time, so the app-xml defaults have been saved into the (in-memory) XML
loadingAppDefaults = false;
const QString iconSizeDefault = element.attribute("iconSizeDefault");
if (!iconSizeDefault.isEmpty()) {
d->iconSizeSettings[Level_AppXML] = iconSizeDefault.toInt();
}
const QString toolButtonStyleDefault = element.attribute("toolButtonStyleDefault");
if (!toolButtonStyleDefault.isEmpty()) {
d->toolButtonStyleSettings[Level_AppXML] = d->toolButtonStyleFromString(toolButtonStyleDefault);
}
} else {
// loading app defaults
bool newLine = false;
QString attrNewLine = element.attribute("newline").toLower();
if (!attrNewLine.isEmpty())
newLine = attrNewLine == "true";
if (newLine && mw)
mw->insertToolBarBreak(this);
}
int newIconSize = -1;
if (element.hasAttribute("iconSize")) {
bool ok;
newIconSize = element.attribute("iconSize").trimmed().toInt(&ok);
if (!ok)
newIconSize = -1;
}
if (newIconSize != -1)
d->iconSizeSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = newIconSize;
const QString newToolButtonStyle = element.attribute("iconText");
if (!newToolButtonStyle.isEmpty())
d->toolButtonStyleSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = d->toolButtonStyleFromString(newToolButtonStyle);
bool hidden = false;
{
QString attrHidden = element.attribute("hidden").toLower();
if (!attrHidden.isEmpty())
hidden = attrHidden == "true";
}
Qt::ToolBarArea pos = Qt::NoToolBarArea;
{
QString attrPosition = element.attribute("position").toLower();
if (!attrPosition.isEmpty())
pos = KToolBar::Private::positionFromString(attrPosition);
}
if (pos != Qt::NoToolBarArea)
mw->addToolBar(pos, this);
setVisible(!hidden);
d->applyCurrentSettings();
}
// Called when switching between xmlgui clients, in order to find any unsaved settings
// again when switching back to the current xmlgui client.
void KToolBar::saveState(QDomElement &current) const
{
Q_ASSERT(!current.isNull());
current.setAttribute("tempXml", "true");
current.setAttribute("noMerge", "1");
current.setAttribute("position", d->getPositionAsString().toLower());
current.setAttribute("hidden", isHidden() ? "true" : "false");
const int currentIconSize = iconSize().width();
if (currentIconSize == d->iconSizeSettings.defaultValue())
current.removeAttribute("iconSize");
else
current.setAttribute("iconSize", iconSize().width());
if (toolButtonStyle() == d->toolButtonStyleSettings.defaultValue())
current.removeAttribute("iconText");
else
current.setAttribute("iconText", d->toolButtonStyleToString(toolButtonStyle()));
// Note: if this method is used by more than KXMLGUIBuilder, e.g. to save XML settings to *disk*,
// then the stuff below shouldn't always be done. This is not the case currently though.
if (d->iconSizeSettings[Level_AppXML] != Unset) {
current.setAttribute("iconSizeDefault", d->iconSizeSettings[Level_AppXML]);
}
if (d->toolButtonStyleSettings[Level_AppXML] != Unset) {
const Qt::ToolButtonStyle bs = static_cast<Qt::ToolButtonStyle>(d->toolButtonStyleSettings[Level_AppXML]);
current.setAttribute("toolButtonStyleDefault", d->toolButtonStyleToString(bs));
}
}
// called by KMainWindow::applyMainWindowSettings to read from the user settings
void KToolBar::applySettings(const KConfigGroup &cg, bool forceGlobal)
{
Q_ASSERT(!cg.name().isEmpty());
Q_UNUSED(forceGlobal); // KDE5: remove
// a small leftover from kde3: separate bool for hidden/shown. But it's also part of saveMainWindowSettings,
// it is not really useful anymore, except in the unlikely case where someone would call this by hand.
// KDE5: remove the block below
if (cg.hasKey("Hidden")) {
const bool hidden = cg.readEntry("Hidden", false);
if (hidden)
hide();
else {
show();
}
}
if (cg.hasKey("IconSize")) {
d->iconSizeSettings[Level_UserSettings] = cg.readEntry("IconSize", 0);
}
if (cg.hasKey("ToolButtonStyle")) {
d->toolButtonStyleSettings[Level_UserSettings] = d->toolButtonStyleFromString(cg.readEntry("ToolButtonStyle", QString()));
}
d->applyCurrentSettings();
}
KMainWindow * KToolBar::mainWindow() const
{
return qobject_cast<KMainWindow*>(const_cast<QObject*>(parent()));
}
void KToolBar::setIconDimensions(int size)
{
QToolBar::setIconSize(QSize(size, size));
d->iconSizeSettings[Level_UserSettings] = size;
}
int KToolBar::iconSizeDefault() const
{
return KIconLoader::global()->currentSize(d->isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
}
void KToolBar::slotMovableChanged(bool movable)
{
if (movable && !KAuthorized::authorize("movable_toolbars"))
setMovable(false);
}
void KToolBar::dragEnterEvent(QDragEnterEvent *event)
{
if (toolBarsEditable() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction) &&
event->mimeData()->hasFormat("application/x-kde-action-list")) {
QByteArray data = event->mimeData()->data("application/x-kde-action-list");
QDataStream stream(data);
QStringList actionNames;
stream >> actionNames;
foreach (const QString& actionName, actionNames) {
foreach (KActionCollection* ac, KActionCollection::allCollections()) {
- QAction* newAction = ac->action(actionName.toAscii().constData());
+ QAction* newAction = ac->action(actionName.toLatin1().constData());
if (newAction) {
d->actionsBeingDragged.append(newAction);
break;
}
}
}
if (d->actionsBeingDragged.count()) {
QAction* overAction = actionAt(event->pos());
QFrame* dropIndicatorWidget = new QFrame(this);
dropIndicatorWidget->resize(8, height() - 4);
dropIndicatorWidget->setFrameShape(QFrame::VLine);
dropIndicatorWidget->setLineWidth(3);
d->dropIndicatorAction = insertWidget(overAction, dropIndicatorWidget);
insertAction(overAction, d->dropIndicatorAction);
event->acceptProposedAction();
return;
}
}
QToolBar::dragEnterEvent(event);
}
void KToolBar::dragMoveEvent(QDragMoveEvent *event)
{
if (toolBarsEditable())
forever {
if (d->dropIndicatorAction) {
QAction* overAction = 0L;
foreach (QAction* action, actions()) {
// want to make it feel that half way across an action you're dropping on the other side of it
QWidget* widget = widgetForAction(action);
if (event->pos().x() < widget->pos().x() + (widget->width() / 2)) {
overAction = action;
break;
}
}
if (overAction != d->dropIndicatorAction) {
// Check to see if the indicator is already in the right spot
int dropIndicatorIndex = actions().indexOf(d->dropIndicatorAction);
if (dropIndicatorIndex + 1 < actions().count()) {
if (actions()[ dropIndicatorIndex + 1 ] == overAction)
break;
} else if (!overAction) {
break;
}
insertAction(overAction, d->dropIndicatorAction);
}
event->accept();
return;
}
break;
}
QToolBar::dragMoveEvent(event);
}
void KToolBar::dragLeaveEvent(QDragLeaveEvent *event)
{
// Want to clear this even if toolBarsEditable was changed mid-drag (unlikey)
delete d->dropIndicatorAction;
d->dropIndicatorAction = 0L;
d->actionsBeingDragged.clear();
if (toolBarsEditable()) {
event->accept();
return;
}
QToolBar::dragLeaveEvent(event);
}
void KToolBar::dropEvent(QDropEvent *event)
{
if (toolBarsEditable()) {
foreach (QAction* action, d->actionsBeingDragged) {
if (actions().contains(action))
removeAction(action);
insertAction(d->dropIndicatorAction, action);
}
}
// Want to clear this even if toolBarsEditable was changed mid-drag (unlikey)
delete d->dropIndicatorAction;
d->dropIndicatorAction = 0L;
d->actionsBeingDragged.clear();
if (toolBarsEditable()) {
event->accept();
return;
}
QToolBar::dropEvent(event);
}
void KToolBar::mousePressEvent(QMouseEvent *event)
{
if (toolBarsEditable() && event->button() == Qt::LeftButton) {
if (KAction* action = qobject_cast<KAction*>(actionAt(event->pos()))) {
d->dragAction = action;
d->dragStartPosition = event->pos();
event->accept();
return;
}
}
QToolBar::mousePressEvent(event);
}
void KToolBar::mouseMoveEvent(QMouseEvent *event)
{
if (!toolBarsEditable() || !d->dragAction)
return QToolBar::mouseMoveEvent(event);
if ((event->pos() - d->dragStartPosition).manhattanLength() < QApplication::startDragDistance()) {
event->accept();
return;
}
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
QByteArray data;
{
QDataStream stream(&data, QIODevice::WriteOnly);
QStringList actionNames;
actionNames << d->dragAction->objectName();
stream << actionNames;
}
mimeData->setData("application/x-kde-action-list", data);
drag->setMimeData(mimeData);
Qt::DropAction dropAction = drag->start(Qt::MoveAction);
if (dropAction == Qt::MoveAction)
// Only remove from this toolbar if it was moved to another toolbar
// Otherwise the receiver moves it.
if (drag->target() != this)
removeAction(d->dragAction);
d->dragAction = 0L;
event->accept();
}
void KToolBar::mouseReleaseEvent(QMouseEvent *event)
{
// Want to clear this even if toolBarsEditable was changed mid-drag (unlikey)
if (d->dragAction) {
d->dragAction = 0L;
event->accept();
return;
}
QToolBar::mouseReleaseEvent(event);
}
bool KToolBar::eventFilter(QObject * watched, QEvent * event)
{
// Generate context menu events for disabled buttons too...
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent* me = static_cast<QMouseEvent*>(event);
if (me->buttons() & Qt::RightButton)
if (QWidget* ww = qobject_cast<QWidget*>(watched))
if (ww->parent() == this && !ww->isEnabled())
QCoreApplication::postEvent(this, new QContextMenuEvent(QContextMenuEvent::Mouse, me->pos(), me->globalPos()));
} else if (event->type() == QEvent::ParentChange) {
// Make sure we're not leaving stale event filters around,
// when a child is reparented somewhere else
if (QWidget* ww = qobject_cast<QWidget*>(watched)) {
if (!this->isAncestorOf(ww)) {
// New parent is not a subwidget - remove event filter
ww->removeEventFilter(this);
foreach (QWidget* child, ww->findChildren<QWidget*>())
child->removeEventFilter(this);
}
}
}
QToolButton* tb;
if ((tb = qobject_cast<QToolButton*>(watched))) {
const QList<QAction*> tbActions = tb->actions();
if (!tbActions.isEmpty()) {
// Handle MMB on toolbar buttons
if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
QMouseEvent* me = static_cast<QMouseEvent*>(event);
if (me->button() == Qt::MidButton /*&&
act->receivers(SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)))*/) {
QAction* act = tbActions.first();
if (me->type() == QEvent::MouseButtonPress)
tb->setDown(act->isEnabled());
else {
tb->setDown(false);
if (act->isEnabled()) {
QMetaObject::invokeMethod(act, "triggered", Qt::DirectConnection,
Q_ARG(Qt::MouseButtons, me->button()),
Q_ARG(Qt::KeyboardModifiers, QApplication::keyboardModifiers()));
}
}
}
}
// CJK languages use more verbose accelerator marker: they add a Latin
// letter in parenthesis, and put accelerator on that. Hence, the default
// removal of ampersand only may not be enough there, instead the whole
// parenthesis construct should be removed. Use KLocalizedString's method to do this.
if (event->type() == QEvent::Show || event->type() == QEvent::Paint || event->type() == QEvent::EnabledChange) {
QAction *act = tb->defaultAction();
if (act) {
const QString text = KLocalizedString::removeAcceleratorMarker(act->iconText().isEmpty() ? act->text() : act->iconText());
const QString toolTip = KLocalizedString::removeAcceleratorMarker(act->toolTip());
// Filtering messages requested by translators (scripting).
tb->setText(i18nc("@action:intoolbar Text label of toolbar button", "%1", text));
tb->setToolTip(i18nc("@info:tooltip Tooltip of toolbar button", "%1", toolTip));
}
}
}
}
// Redirect mouse events to the toolbar when drag + drop editing is enabled
if (toolBarsEditable()) {
if (QWidget* ww = qobject_cast<QWidget*>(watched)) {
switch (event->type()) {
case QEvent::MouseButtonPress: {
QMouseEvent* me = static_cast<QMouseEvent*>(event);
QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(),
me->button(), me->buttons(), me->modifiers());
mousePressEvent(&newEvent);
return true;
}
case QEvent::MouseMove: {
QMouseEvent* me = static_cast<QMouseEvent*>(event);
QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(),
me->button(), me->buttons(), me->modifiers());
mouseMoveEvent(&newEvent);
return true;
}
case QEvent::MouseButtonRelease: {
QMouseEvent* me = static_cast<QMouseEvent*>(event);
QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(),
me->button(), me->buttons(), me->modifiers());
mouseReleaseEvent(&newEvent);
return true;
}
default:
break;
}
}
}
return QToolBar::eventFilter(watched, event);
}
void KToolBar::actionEvent(QActionEvent * event)
{
if (event->type() == QEvent::ActionRemoved) {
QWidget* widget = widgetForAction(event->action());
if (widget) {
widget->removeEventFilter(this);
foreach (QWidget* child, widget->findChildren<QWidget*>())
child->removeEventFilter(this);
}
}
QToolBar::actionEvent(event);
if (event->type() == QEvent::ActionAdded) {
QWidget* widget = widgetForAction(event->action());
if (widget) {
widget->installEventFilter(this);
foreach (QWidget* child, widget->findChildren<QWidget*>())
child->installEventFilter(this);
// Center widgets that do not have any use for more space. See bug 165274
if (!(widget->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag)
// ... but do not center when using text besides icon in vertical toolbar. See bug 243196
&& !(orientation() == Qt::Vertical && toolButtonStyle() == Qt::ToolButtonTextBesideIcon)) {
const int index = layout()->indexOf(widget);
if (index != -1) {
layout()->itemAt(index)->setAlignment(Qt::AlignJustify);
}
}
}
}
d->adjustSeparatorVisibility();
}
bool KToolBar::toolBarsEditable()
{
return KToolBar::Private::s_editable;
}
void KToolBar::setToolBarsEditable(bool editable)
{
if (KToolBar::Private::s_editable != editable) {
KToolBar::Private::s_editable = editable;
}
}
void KToolBar::setToolBarsLocked(bool locked)
{
if (KToolBar::Private::s_locked != locked) {
KToolBar::Private::s_locked = locked;
foreach (KMainWindow* mw, KMainWindow::memberList()) {
foreach (KToolBar* toolbar, mw->findChildren<KToolBar*>()) {
toolbar->d->setLocked(locked);
}
}
}
}
bool KToolBar::toolBarsLocked()
{
return KToolBar::Private::s_locked;
}
#include "moc_ktoolbar.cpp"
diff --git a/kdeui/xmlgui/kxmlguiwindow.cpp b/kdeui/xmlgui/kxmlguiwindow.cpp
index c8bedb7b78..8010b2d2d5 100644
--- a/kdeui/xmlgui/kxmlguiwindow.cpp
+++ b/kdeui/xmlgui/kxmlguiwindow.cpp
@@ -1,347 +1,347 @@
/* This file is part of the KDE libraries
Copyright
(C) 2000 Reginald Stadlbauer (reggie@kde.org)
(C) 1997 Stephan Kulow (coolo@kde.org)
(C) 1997-2000 Sven Radej (radej@kde.org)
(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)
(C) 1999 Chris Schlaeger (cs@kde.org)
(C) 2002 Joseph Wenninger (jowenn@kde.org)
(C) 2005-2006 Hamish Rodda (rodda@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 version 2 as published by the Free Software Foundation.
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 "kxmlguiwindow.h"
#include "kmainwindow_p.h"
#include "kactioncollection.h"
#include "kmainwindowiface_p.h"
#include "ktoolbarhandler_p.h"
#include "kxmlguifactory.h"
#include "ktoggleaction.h"
#include "kstandardaction.h"
#include <QCloseEvent>
#include <QtXml/QDomDocument>
#include <QLayout>
#include <QMenuBar>
#include <QObject>
#include <QStatusBar>
#include <QStyle>
#include <QWidget>
#include <QList>
#include <kaction.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kedittoolbar.h>
#include <khelpmenu.h>
#include <klocalizedstring.h>
#include <ktoolbar.h>
#include <kwindowsystem.h>
#include <kconfiggroup.h>
#include <stdlib.h>
#include <ctype.h>
class KXmlGuiWindowPrivate : public KMainWindowPrivate {
public:
void _k_slotFactoryMakingChanges(bool b)
{
// While the GUI factory is adding/removing clients,
// don't let KMainWindow think those are changes made by the user
// #105525
letDirtySettings = !b;
}
bool showHelpMenu:1;
QSize defaultSize;
KDEPrivate::ToolBarHandler *toolBarHandler;
KToggleAction *showStatusBarAction;
QPointer<KEditToolBar> toolBarEditor;
KXMLGUIFactory *factory;
};
-KXmlGuiWindow::KXmlGuiWindow( QWidget* parent, Qt::WFlags f )
+KXmlGuiWindow::KXmlGuiWindow( QWidget* parent, Qt::WindowFlags f )
: KMainWindow(*new KXmlGuiWindowPrivate, parent, f), KXMLGUIBuilder( this )
{
K_D(KXmlGuiWindow);
d->showHelpMenu = true;
d->toolBarHandler = 0;
d->showStatusBarAction = 0;
d->factory = 0;
new KMainWindowInterface(this);
}
QAction *KXmlGuiWindow::toolBarMenuAction()
{
K_D(KXmlGuiWindow);
if ( !d->toolBarHandler )
return 0;
return d->toolBarHandler->toolBarMenuAction();
}
void KXmlGuiWindow::setupToolbarMenuActions()
{
K_D(KXmlGuiWindow);
if ( d->toolBarHandler )
d->toolBarHandler->setupActions();
}
KXmlGuiWindow::~KXmlGuiWindow()
{
K_D(KXmlGuiWindow);
delete d->factory;
}
bool KXmlGuiWindow::event( QEvent* ev )
{
bool ret = KMainWindow::event(ev);
if (ev->type()==QEvent::Polish) {
QDBusConnection::sessionBus().registerObject(dbusName() + "/actions", actionCollection(),
QDBusConnection::ExportScriptableSlots |
QDBusConnection::ExportScriptableProperties |
QDBusConnection::ExportNonScriptableSlots |
QDBusConnection::ExportNonScriptableProperties |
QDBusConnection::ExportChildObjects);
}
return ret;
}
void KXmlGuiWindow::setHelpMenuEnabled(bool showHelpMenu)
{
K_D(KXmlGuiWindow);
d->showHelpMenu = showHelpMenu;
}
bool KXmlGuiWindow::isHelpMenuEnabled() const
{
K_D(const KXmlGuiWindow);
return d->showHelpMenu;
}
KXMLGUIFactory *KXmlGuiWindow::guiFactory()
{
K_D(KXmlGuiWindow);
if (!d->factory) {
d->factory = new KXMLGUIFactory( this, this );
connect(d->factory, SIGNAL(makingChanges(bool)),
this, SLOT(_k_slotFactoryMakingChanges(bool)));
}
return d->factory;
}
void KXmlGuiWindow::configureToolbars()
{
K_D(KXmlGuiWindow);
KConfigGroup cg(KSharedConfig::openConfig(), QString());
saveMainWindowSettings(cg);
if (!d->toolBarEditor) {
d->toolBarEditor = new KEditToolBar(guiFactory(), this);
d->toolBarEditor->setAttribute(Qt::WA_DeleteOnClose);
connect(d->toolBarEditor, SIGNAL(newToolBarConfig()), SLOT(saveNewToolbarConfig()));
}
d->toolBarEditor->show();
}
void KXmlGuiWindow::saveNewToolbarConfig()
{
// createGUI(xmlFile()); // this loses any plugged-in guiclients, so we use remove+add instead.
guiFactory()->removeClient(this);
guiFactory()->addClient(this);
KConfigGroup cg(KSharedConfig::openConfig(), QString());
applyMainWindowSettings(cg);
}
void KXmlGuiWindow::setupGUI( StandardWindowOptions options, const QString & xmlfile ) {
setupGUI(QSize(), options, xmlfile);
}
void KXmlGuiWindow::setupGUI( const QSize & defaultSize, StandardWindowOptions options, const QString & xmlfile ) {
K_D(KXmlGuiWindow);
if( options & Keys ){
KStandardAction::keyBindings(guiFactory(),
SLOT(configureShortcuts()), actionCollection());
}
if( (options & StatusBar) && statusBar() ){
createStandardStatusBarAction();
}
if( options & ToolBar ){
setStandardToolBarMenuEnabled( true );
KStandardAction::configureToolbars(this,
SLOT(configureToolbars()), actionCollection());
}
d->defaultSize = defaultSize;
if( options & Create ){
createGUI(xmlfile);
}
if (d->defaultSize.isValid()) {
resize(d->defaultSize);
}
else if (isHidden()) {
adjustSize();
}
if( options & Save ){
const KConfigGroup cg(autoSaveConfigGroup());
if (cg.isValid()) {
setAutoSaveSettings(cg);
} else {
setAutoSaveSettings();
}
}
}
void KXmlGuiWindow::createGUI( const QString &xmlfile )
{
K_D(KXmlGuiWindow);
// disabling the updates prevents unnecessary redraws
//setUpdatesEnabled( false );
// just in case we are rebuilding, let's remove our old client
guiFactory()->removeClient( this );
// make sure to have an empty GUI
QMenuBar* mb = menuBar();
if ( mb )
mb->clear();
qDeleteAll( toolBars() ); // delete all toolbars
// don't build a help menu unless the user ask for it
if (d->showHelpMenu) {
delete d->helpMenu;
// we always want a help menu
d->helpMenu = new KHelpMenu(this, componentData().aboutData(), true, actionCollection());
}
const QString windowXmlFile = xmlfile.isNull() ? componentData().componentName() + "ui.rc" : xmlfile;
// Help beginners who call setXMLFile and then setupGUI...
if (!xmlFile().isEmpty() && xmlFile() != windowXmlFile) {
kWarning() << "You called setXMLFile(" << xmlFile() << ") and then createGUI or setupGUI,"
<< "which also calls setXMLFile and will overwrite the file you have previously set.\n"
<< "You should call createGUI("<<xmlFile()<<") or setupGUI(<options>,"<<xmlFile()<<") instead.";
}
// we always want to load in our global standards file
loadStandardsXmlFile();
// now, merge in our local xml file.
setXMLFile(windowXmlFile, true);
// make sure we don't have any state saved already
setXMLGUIBuildDocument( QDomDocument() );
// do the actual GUI building
guiFactory()->addClient( this );
// setUpdatesEnabled( true );
}
void KXmlGuiWindow::slotStateChanged(const QString &newstate)
{
stateChanged(newstate, KXMLGUIClient::StateNoReverse);
}
void KXmlGuiWindow::slotStateChanged(const QString &newstate,
bool reverse)
{
stateChanged(newstate,
reverse ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse);
}
void KXmlGuiWindow::setStandardToolBarMenuEnabled( bool enable )
{
K_D(KXmlGuiWindow);
if ( enable ) {
if ( d->toolBarHandler )
return;
d->toolBarHandler = new KDEPrivate::ToolBarHandler( this );
if ( factory() )
factory()->addClient( d->toolBarHandler );
} else {
if ( !d->toolBarHandler )
return;
if ( factory() )
factory()->removeClient( d->toolBarHandler );
delete d->toolBarHandler;
d->toolBarHandler = 0;
}
}
bool KXmlGuiWindow::isStandardToolBarMenuEnabled() const
{
K_D(const KXmlGuiWindow);
return ( d->toolBarHandler );
}
void KXmlGuiWindow::createStandardStatusBarAction(){
K_D(KXmlGuiWindow);
if(!d->showStatusBarAction){
d->showStatusBarAction = KStandardAction::showStatusbar(this, SLOT(setSettingsDirty()), actionCollection());
QStatusBar *sb = statusBar(); // Creates statusbar if it doesn't exist already.
connect(d->showStatusBarAction, SIGNAL(toggled(bool)), sb, SLOT(setVisible(bool)));
d->showStatusBarAction->setChecked(sb->isHidden());
} else {
// If the language has changed, we'll need to grab the new text and whatsThis
KAction *tmpStatusBar = KStandardAction::showStatusbar(NULL, NULL, NULL);
d->showStatusBarAction->setText(tmpStatusBar->text());
d->showStatusBarAction->setWhatsThis(tmpStatusBar->whatsThis());
delete tmpStatusBar;
}
}
void KXmlGuiWindow::finalizeGUI( bool /*force*/ )
{
// FIXME: this really needs to be removed with a code more like the one we had on KDE3.
// what we need to do here is to position correctly toolbars so they don't overlap.
// Also, take in count plugins could provide their own toolbars and those also need to
// be restored.
if (autoSaveSettings() && autoSaveConfigGroup().isValid()) {
applyMainWindowSettings(autoSaveConfigGroup());
}
}
void KXmlGuiWindow::applyMainWindowSettings(const KConfigGroup &config, bool force)
{
K_D(KXmlGuiWindow);
KMainWindow::applyMainWindowSettings(config, force);
QStatusBar *sb = findChild<QStatusBar *>();
if (sb && d->showStatusBarAction)
d->showStatusBarAction->setChecked(!sb->isHidden());
}
// KDE5 TODO: change it to "using KXMLGUIBuilder::finalizeGUI;" in the header
// and remove the reimplementation
void KXmlGuiWindow::finalizeGUI( KXMLGUIClient *client )
{ KXMLGUIBuilder::finalizeGUI( client ); }
#include "moc_kxmlguiwindow.cpp"
diff --git a/kfile/kfilewidget.cpp b/kfile/kfilewidget.cpp
index 4b86dc307c..5ad95c379b 100644
--- a/kfile/kfilewidget.cpp
+++ b/kfile/kfilewidget.cpp
@@ -1,2784 +1,2784 @@
// -*- c++ -*-
/* This file is part of the KDE libraries
Copyright (C) 1997, 1998 Richard Moore <rich@kde.org>
1998 Stephan Kulow <coolo@kde.org>
1998 Daniel Grana <grana@ie.iwi.unibe.ch>
1999,2000,2001,2002,2003 Carsten Pfeiffer <pfeiffer@kde.org>
2003 Clarence Dang <dang@kde.org>
2007 David Faure <faure@kde.org>
2008 Rafael Fernández López <ereslibre@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.
*/
#include "kfilewidget.h"
#include "kfileplacesview.h"
#include "kfileplacesmodel.h"
#include "kfilebookmarkhandler_p.h"
#include "kurlcombobox.h"
#include "kurlnavigator.h"
#include "kfilepreviewgenerator.h"
#include <config-kfile.h>
#include <kactioncollection.h>
#include <kdiroperator.h>
#include <kdirselectdialog.h>
#include <kfilefiltercombo.h>
#include <kimagefilepreview.h>
#include <kmenu.h>
#include <krecentdocument.h>
#include <ktoolbar.h>
#include <kurlcompletion.h>
#include <kuser.h>
#include <kprotocolmanager.h>
#include <kio/pixmaploader.h>
#include <kio/job.h>
#include <kio/jobuidelegate.h>
#include <kio/netaccess.h>
#include <kio/scheduler.h>
#include <krecentdirs.h>
#include <kdebug.h>
#include <klocalizedstring.h>
#include <kio/kfileitemdelegate.h>
#include <kde_file.h>
#include <QCheckBox>
#include <QDockWidget>
#include <QLayout>
#include <QLabel>
#include <QLineEdit>
#include <QSplitter>
#include <QAbstractProxyModel>
#include <QHelpEvent>
#include <QApplication>
#include <QPushButton>
#include <qurlpathinfo.h>
#include <qmimedatabase.h>
#include <kshell.h>
#include <kmessagebox.h>
#include <kauthorized.h>
#include <qstandardpaths.h>
class KFileWidgetPrivate
{
public:
KFileWidgetPrivate(KFileWidget *widget)
: q(widget),
boxLayout(0),
placesDock(0),
placesView(0),
placesViewSplitter(0),
placesViewWidth(-1),
labeledCustomWidget(0),
bottomCustomWidget(0),
autoSelectExtCheckBox(0),
operationMode(KFileWidget::Opening),
bookmarkHandler(0),
toolbar(0),
locationEdit(0),
ops(0),
filterWidget(0),
autoSelectExtChecked(false),
keepLocation(false),
hasView(false),
hasDefaultFilter(false),
inAccept(false),
dummyAdded(false),
confirmOverwrite(false),
differentHierarchyLevelItemsEntered(false),
previewGenerator(0),
iconSizeSlider(0)
{
}
~KFileWidgetPrivate()
{
delete bookmarkHandler; // Should be deleted before ops!
delete ops;
}
void updateLocationWhatsThis();
void updateAutoSelectExtension();
void initSpeedbar();
void initGUI();
void readConfig(KConfigGroup &configGroup);
void writeConfig(KConfigGroup &configGroup);
void setNonExtSelection();
void setLocationText(const QUrl&);
void setLocationText(const QList<QUrl>&);
void appendExtension(QUrl &url);
void updateLocationEditExtension(const QString &);
void updateFilter();
QList<QUrl>& parseSelectedUrls();
/**
* Parses the string "line" for files. If line doesn't contain any ", the
* whole line will be interpreted as one file. If the number of " is odd,
* an empty list will be returned. Otherwise, all items enclosed in " "
* will be returned as correct urls.
*/
QList<QUrl> tokenize(const QString& line) const;
/**
* Reads the recent used files and inserts them into the location combobox
*/
void readRecentFiles(KConfigGroup &cg);
/**
* Saves the entries from the location combobox.
*/
void saveRecentFiles(KConfigGroup &cg);
/**
* called when an item is highlighted/selected in multiselection mode.
* handles setting the locationEdit.
*/
void multiSelectionChanged();
/**
* Returns the absolute version of the URL specified in locationEdit.
*/
QUrl getCompleteUrl(const QString&) const;
/**
* Sets the dummy entry on the history combo box. If the dummy entry
* already exists, it is overwritten with this information.
*/
void setDummyHistoryEntry(const QString& text, const QPixmap& icon = QPixmap(),
bool usePreviousPixmapIfNull = true);
/**
* Removes the dummy entry of the history combo box.
*/
void removeDummyHistoryEntry();
/**
* Asks for overwrite confirmation using a KMessageBox and returns
* true if the user accepts.
*
* @since 4.2
*/
bool toOverwrite(const QUrl&);
// private slots
void _k_slotLocationChanged( const QString& );
void _k_urlEntered(const QUrl&);
void _k_enterUrl(const QUrl&);
void _k_enterUrl(const QString&);
void _k_locationAccepted( const QString& );
void _k_slotFilterChanged();
void _k_fileHighlighted( const KFileItem& );
void _k_fileSelected( const KFileItem& );
void _k_slotLoadingFinished();
void _k_fileCompletion( const QString& );
void _k_toggleSpeedbar( bool );
void _k_toggleBookmarks( bool );
void _k_slotAutoSelectExtClicked();
void _k_placesViewSplitterMoved(int, int);
void _k_activateUrlNavigator();
void _k_zoomOutIconsSize();
void _k_zoomInIconsSize();
void _k_slotIconSizeSliderMoved(int);
void _k_slotIconSizeChanged(int);
void addToRecentDocuments();
QString locationEditCurrentText() const;
/**
* KIO::NetAccess::mostLocalUrl local replacement.
* This method won't show any progress dialogs for stating, since
* they are very annoying when stating.
*/
QUrl mostLocalUrl(const QUrl &url);
void setInlinePreviewShown(bool show);
KFileWidget* q;
// the last selected url
KUrl url;
// the selected filenames in multiselection mode -- FIXME
QString filenames;
// now following all kind of widgets, that I need to rebuild
// the geometry management
QBoxLayout *boxLayout;
QGridLayout *lafBox;
QVBoxLayout *vbox;
QLabel *locationLabel;
QWidget *opsWidget;
QWidget *pathSpacer;
QLabel *filterLabel;
KUrlNavigator *urlNavigator;
QPushButton *okButton, *cancelButton;
QDockWidget *placesDock;
KFilePlacesView *placesView;
QSplitter *placesViewSplitter;
// caches the places view width. This value will be updated when the splitter
// is moved. This allows us to properly set a value when the dialog itself
// is resized
int placesViewWidth;
QWidget *labeledCustomWidget;
QWidget *bottomCustomWidget;
// Automatically Select Extension stuff
QCheckBox *autoSelectExtCheckBox;
QString extension; // current extension for this filter
QList<KIO::StatJob*> statJobs;
QList<QUrl> urlList; //the list of selected urls
KFileWidget::OperationMode operationMode;
// The file class used for KRecentDirs
QString fileClass;
KFileBookmarkHandler *bookmarkHandler;
KActionMenu* bookmarkButton;
KToolBar *toolbar;
KUrlComboBox *locationEdit;
KDirOperator *ops;
KFileFilterCombo *filterWidget;
QTimer filterDelayTimer;
KFilePlacesModel *model;
// whether or not the _user_ has checked the above box
bool autoSelectExtChecked : 1;
// indicates if the location edit should be kept or cleared when changing
// directories
bool keepLocation : 1;
// the KDirOperators view is set in KFileWidget::show(), so to avoid
// setting it again and again, we have this nice little boolean :)
bool hasView : 1;
bool hasDefaultFilter : 1; // necessary for the operationMode
bool autoDirectoryFollowing : 1;
bool inAccept : 1; // true between beginning and end of accept()
bool dummyAdded : 1; // if the dummy item has been added. This prevents the combo from having a
// blank item added when loaded
bool confirmOverwrite : 1;
bool differentHierarchyLevelItemsEntered;
KFilePreviewGenerator *previewGenerator;
QSlider *iconSizeSlider;
};
Q_GLOBAL_STATIC(KUrl, lastDirectory) // to set the start path
static const char autocompletionWhatsThisText[] = I18N_NOOP("<qt>While typing in the text area, you may be presented "
"with possible matches. "
"This feature can be controlled by clicking with the right mouse button "
"and selecting a preferred mode from the <b>Text Completion</b> menu.</qt>");
// returns true if the string contains "<a>:/" sequence, where <a> is at least 2 alpha chars
static bool containsProtocolSection( const QString& string )
{
int len = string.length();
static const char prot[] = ":/";
for (int i=0; i < len;) {
i = string.indexOf( QLatin1String(prot), i );
if (i == -1)
return false;
int j=i-1;
for (; j >= 0; j--) {
const QChar& ch( string[j] );
- if (ch.toAscii() == 0 || !ch.isLetter())
+ if (ch.toLatin1() == 0 || !ch.isLetter())
break;
if (ch.isSpace() && (i-j-1) >= 2)
return true;
}
if (j < 0 && i >= 2)
return true; // at least two letters before ":/"
i += 3; // skip : and / and one char
}
return false;
}
KFileWidget::KFileWidget( const QUrl& _startDir, QWidget *parent )
: QWidget(parent), KAbstractFileWidget(), d(new KFileWidgetPrivate(this))
{
QUrl startDir(_startDir);
kDebug(kfile_area) << "startDir" << startDir;
QString filename;
d->okButton = new QPushButton(this);
KGuiItem::assign(d->okButton, KStandardGuiItem::ok());
d->okButton->setDefault(true);
d->cancelButton = new QPushButton(this);
KGuiItem::assign(d->cancelButton, KStandardGuiItem::cancel());
// The dialog shows them
d->okButton->hide();
d->cancelButton->hide();
d->opsWidget = new QWidget(this);
QVBoxLayout *opsWidgetLayout = new QVBoxLayout(d->opsWidget);
opsWidgetLayout->setMargin(0);
opsWidgetLayout->setSpacing(0);
//d->toolbar = new KToolBar(this, true);
d->toolbar = new KToolBar(d->opsWidget, true);
d->toolbar->setObjectName("KFileWidget::toolbar");
d->toolbar->setMovable(false);
opsWidgetLayout->addWidget(d->toolbar);
d->model = new KFilePlacesModel(this);
// Resolve this now so that a 'kfiledialog:' URL, if specified,
// does not get inserted into the urlNavigator history.
d->url = getStartUrl( startDir, d->fileClass, filename );
startDir = d->url;
// Don't pass startDir to the KUrlNavigator at this stage: as well as
// the above, it may also contain a file name which should not get
// inserted in that form into the old-style navigation bar history.
// Wait until the KIO::stat has been done later.
//
// The stat cannot be done before this point, bug 172678.
d->urlNavigator = new KUrlNavigator(d->model, QUrl(), d->opsWidget); //d->toolbar);
d->urlNavigator->setPlacesSelectorVisible(false);
opsWidgetLayout->addWidget(d->urlNavigator);
QUrl u;
KUrlComboBox *pathCombo = d->urlNavigator->editor();
#ifdef Q_OS_WIN
#if 0
foreach( const QFileInfo &drive,QFSFileEngine::drives() )
{
u = QUrl::fromLocalFile(drive.filePath());
pathCombo->addDefaultUrl(u,
KIO::pixmapForUrl( u, 0, KIconLoader::Small ),
i18n("Drive: %1", u.toLocalFile()));
}
#else
#pragma message(QT5 PORT)
#endif
#else
u = QUrl::fromLocalFile(QDir::rootPath());
pathCombo->addDefaultUrl(u,
KIO::pixmapForUrl(u, 0, KIconLoader::Small),
u.toLocalFile());
#endif
u = QUrl::fromLocalFile(QDir::homePath());
pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small),
u.toLocalFile());
QUrl docPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
if ( (QUrlPathInfo(u).path(QUrlPathInfo::AppendTrailingSlash) !=
QUrlPathInfo(docPath).path(QUrlPathInfo::AppendTrailingSlash)) &&
QDir(docPath.toLocalFile()).exists() )
{
pathCombo->addDefaultUrl(docPath,
KIO::pixmapForUrl(docPath, 0, KIconLoader::Small),
docPath.toLocalFile());
}
u = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
pathCombo->addDefaultUrl(u,
KIO::pixmapForUrl(u, 0, KIconLoader::Small),
u.toLocalFile());
d->ops = new KDirOperator(QUrl(), d->opsWidget);
d->ops->setObjectName( "KFileWidget::ops" );
d->ops->setIsSaving(d->operationMode == Saving);
opsWidgetLayout->addWidget(d->ops);
connect(d->ops, SIGNAL(urlEntered(QUrl)),
SLOT(_k_urlEntered(QUrl)));
connect(d->ops, SIGNAL(fileHighlighted(KFileItem)),
SLOT(_k_fileHighlighted(KFileItem)));
connect(d->ops, SIGNAL(fileSelected(KFileItem)),
SLOT(_k_fileSelected(KFileItem)));
connect(d->ops, SIGNAL(finishedLoading()),
SLOT(_k_slotLoadingFinished()));
d->ops->setupMenu(KDirOperator::SortActions |
KDirOperator::FileActions |
KDirOperator::ViewActions);
KActionCollection *coll = d->ops->actionCollection();
coll->addAssociatedWidget(this);
// add nav items to the toolbar
//
// NOTE: The order of the button icons here differs from that
// found in the file manager and web browser, but has been discussed
// and agreed upon on the kde-core-devel mailing list:
//
// http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2
coll->action( "up" )->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<br /><br />"
"For instance, if the current location is file:/home/%1 clicking this "
"button will take you to file:/home.</qt>", KUser().loginName() ));
coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history."));
coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history."));
coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location."));
coll->action( "mkdir" )->setShortcut( QKeySequence(Qt::Key_F10) );
coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder."));
KAction *goToNavigatorAction = coll->addAction( "gotonavigator", this, SLOT(_k_activateUrlNavigator()) );
goToNavigatorAction->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L) );
KToggleAction *showSidebarAction =
new KToggleAction(i18n("Show Places Navigation Panel"), this);
coll->addAction("toggleSpeedbar", showSidebarAction);
showSidebarAction->setShortcut( QKeySequence(Qt::Key_F9) );
connect( showSidebarAction, SIGNAL(toggled(bool)),
SLOT(_k_toggleSpeedbar(bool)) );
KToggleAction *showBookmarksAction =
new KToggleAction(i18n("Show Bookmarks"), this);
coll->addAction("toggleBookmarks", showBookmarksAction);
connect( showBookmarksAction, SIGNAL(toggled(bool)),
SLOT(_k_toggleBookmarks(bool)) );
KActionMenu *menu = new KActionMenu( KDE::icon("configure"), i18n("Options"), this);
coll->addAction("extra menu", menu);
menu->setWhatsThis(i18n("<qt>This is the preferences menu for the file dialog. "
"Various options can be accessed from this menu including: <ul>"
"<li>how files are sorted in the list</li>"
"<li>types of view, including icon and list</li>"
"<li>showing of hidden files</li>"
"<li>the Places navigation panel</li>"
"<li>file previews</li>"
"<li>separating folders from files</li></ul></qt>"));
menu->addAction(coll->action("sorting menu"));
menu->addAction(coll->action("view menu"));
menu->addSeparator();
menu->addAction(coll->action("decoration menu"));
menu->addSeparator();
KAction * showHidden = qobject_cast<KAction*>(coll->action( "show hidden" ));
if (showHidden) {
showHidden->setShortcut(
KShortcut( QKeySequence(Qt::ALT + Qt::Key_Period), QKeySequence(Qt::Key_F8) ) );
}
menu->addAction( showHidden );
menu->addAction( showSidebarAction );
menu->addAction( showBookmarksAction );
coll->action( "inline preview" )->setShortcut( QKeySequence(Qt::Key_F11) );
menu->addAction( coll->action( "preview" ));
menu->setDelayed( false );
connect( menu->menu(), SIGNAL(aboutToShow()),
d->ops, SLOT(updateSelectionDependentActions()));
d->iconSizeSlider = new QSlider(this);
d->iconSizeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
d->iconSizeSlider->setOrientation(Qt::Horizontal);
d->iconSizeSlider->setMinimum(0);
d->iconSizeSlider->setMaximum(100);
d->iconSizeSlider->installEventFilter(this);
connect(d->iconSizeSlider, SIGNAL(valueChanged(int)),
d->ops, SLOT(setIconsZoom(int)));
connect(d->iconSizeSlider, SIGNAL(valueChanged(int)),
this, SLOT(_k_slotIconSizeChanged(int)));
connect(d->iconSizeSlider, SIGNAL(sliderMoved(int)),
this, SLOT(_k_slotIconSizeSliderMoved(int)));
connect(d->ops, SIGNAL(currentIconSizeChanged(int)),
d->iconSizeSlider, SLOT(setValue(int)));
KAction *furtherAction = new KAction(KDE::icon("file-zoom-out"), i18n("Zoom out"), this);
connect(furtherAction, SIGNAL(triggered()), SLOT(_k_zoomOutIconsSize()));
KAction *closerAction = new KAction(KDE::icon("file-zoom-in"), i18n("Zoom in"), this);
connect(closerAction, SIGNAL(triggered()), SLOT(_k_zoomInIconsSize()));
QWidget *midSpacer = new QWidget(this);
midSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QAction *separator = new QAction(this);
separator->setSeparator(true);
QAction *separator2 = new QAction(this);
separator2->setSeparator(true);
d->toolbar->addAction(coll->action("back" ));
d->toolbar->addAction(coll->action("forward"));
d->toolbar->addAction(coll->action("up"));
d->toolbar->addAction(coll->action("reload"));
d->toolbar->addAction(separator);
d->toolbar->addAction(coll->action("inline preview"));
d->toolbar->addWidget(midSpacer);
d->toolbar->addAction(furtherAction);
d->toolbar->addWidget(d->iconSizeSlider);
d->toolbar->addAction(closerAction);
d->toolbar->addAction(separator2);
d->toolbar->addAction(coll->action("mkdir"));
d->toolbar->addAction(menu);
d->toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
d->toolbar->setMovable(false);
KUrlCompletion *pathCompletionObj = new KUrlCompletion( KUrlCompletion::DirCompletion );
pathCombo->setCompletionObject( pathCompletionObj );
pathCombo->setAutoDeleteCompletionObject( true );
connect( d->urlNavigator, SIGNAL(urlChanged(QUrl)),
this, SLOT(_k_enterUrl(QUrl)));
connect( d->urlNavigator, SIGNAL(returnPressed()),
d->ops, SLOT(setFocus()));
QString whatsThisText;
// the Location label/edit
d->locationLabel = new QLabel(i18n("&Name:"), this);
d->locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, this);
d->locationEdit->installEventFilter(this);
// Properly let the dialog be resized (to smaller). Otherwise we could have
// huge dialogs that can't be resized to smaller (it would be as big as the longest
// item in this combo box). (ereslibre)
d->locationEdit->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
connect( d->locationEdit, SIGNAL(editTextChanged(QString)),
SLOT(_k_slotLocationChanged(QString)) );
d->updateLocationWhatsThis();
d->locationLabel->setBuddy(d->locationEdit);
KUrlCompletion *fileCompletionObj = new KUrlCompletion( KUrlCompletion::FileCompletion );
d->locationEdit->setCompletionObject( fileCompletionObj );
d->locationEdit->setAutoDeleteCompletionObject( true );
connect( fileCompletionObj, SIGNAL(match(QString)),
SLOT(_k_fileCompletion(QString)) );
connect(d->locationEdit, SIGNAL(returnPressed(QString)),
this, SLOT(_k_locationAccepted(QString)));
// the Filter label/edit
whatsThisText = i18n("<qt>This is the filter to apply to the file list. "
"File names that do not match the filter will not be shown.<p>"
"You may select from one of the preset filters in the "
"drop down menu, or you may enter a custom filter "
"directly into the text area.</p><p>"
"Wildcards such as * and ? are allowed.</p></qt>");
d->filterLabel = new QLabel(i18n("&Filter:"), this);
d->filterLabel->setWhatsThis(whatsThisText);
d->filterWidget = new KFileFilterCombo(this);
// Properly let the dialog be resized (to smaller). Otherwise we could have
// huge dialogs that can't be resized to smaller (it would be as big as the longest
// item in this combo box). (ereslibre)
d->filterWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
d->filterWidget->setWhatsThis(whatsThisText);
d->filterLabel->setBuddy(d->filterWidget);
connect(d->filterWidget, SIGNAL(filterChanged()), SLOT(_k_slotFilterChanged()));
d->filterDelayTimer.setSingleShot(true);
d->filterDelayTimer.setInterval(300);
connect(d->filterWidget, SIGNAL(editTextChanged(QString)), &d->filterDelayTimer, SLOT(start()));
connect(&d->filterDelayTimer, SIGNAL(timeout()), SLOT(_k_slotFilterChanged()));
// the Automatically Select Extension checkbox
// (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig())
d->autoSelectExtCheckBox = new QCheckBox (this);
d->autoSelectExtCheckBox->setStyleSheet(QString("QCheckBox { padding-top: %1px; }").arg(KDialog::spacingHint()));
connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(_k_slotAutoSelectExtClicked()));
d->initGUI(); // activate GM
// read our configuration
KSharedConfig::Ptr config = KSharedConfig::openConfig();
KConfigGroup viewConfigGroup(config, ConfigGroup);
d->readConfig(viewConfigGroup);
coll->action("inline preview")->setChecked(d->ops->isInlinePreviewShown());
d->iconSizeSlider->setValue(d->ops->iconsZoom());
KFilePreviewGenerator *pg = d->ops->previewGenerator();
if (pg) {
coll->action("inline preview")->setChecked(pg->isPreviewShown());
}
// getStartUrl() above will have resolved the startDir parameter into
// a directory and file name in the two cases: (a) where it is a
// special "kfiledialog:" URL, or (b) where it is a plain file name
// only without directory or protocol. For any other startDir
// specified, it is not possible to resolve whether there is a file name
// present just by looking at the URL; the only way to be sure is
// to stat it.
bool statRes = false;
if ( filename.isEmpty() )
{
KIO::StatJob *statJob = KIO::stat(startDir, KIO::HideProgressInfo);
statRes = KIO::NetAccess::synchronousRun(statJob, this);
kDebug(kfile_area) << "stat of" << startDir << "-> statRes" << statRes << "isDir" << statJob->statResult().isDir();
if (!statRes || !statJob->statResult().isDir()) {
QUrlPathInfo startDirInfo(startDir);
filename = startDirInfo.fileName();
startDir.setPath(startDirInfo.directory());
kDebug(kfile_area) << "statJob -> startDir" << startDir << "filename" << filename;
}
}
d->ops->setUrl(startDir, true);
d->urlNavigator->setLocationUrl(startDir);
if (d->placesView) {
d->placesView->setUrl(startDir);
}
// We have a file name either explicitly specified, or have checked that
// we could stat it and it is not a directory. Set it.
if (!filename.isEmpty()) {
QLineEdit* lineEdit = d->locationEdit->lineEdit();
kDebug(kfile_area) << "selecting filename" << filename;
if (statRes) {
d->setLocationText(QUrl(filename));
} else {
lineEdit->setText(filename);
// Preserve this filename when clicking on the view (cf _k_fileHighlighted)
lineEdit->setModified(true);
}
lineEdit->selectAll();
}
d->locationEdit->setFocus();
}
KFileWidget::~KFileWidget()
{
KSharedConfig::Ptr config = KSharedConfig::openConfig();
config->sync();
delete d;
}
void KFileWidget::setLocationLabel(const QString& text)
{
d->locationLabel->setText(text);
}
void KFileWidget::setFilter(const QString& filter)
{
int pos = filter.indexOf('/');
// Check for an un-escaped '/', if found
// interpret as a MIME filter.
if (pos > 0 && filter[pos - 1] != '\\') {
QStringList filters = filter.split(' ', QString::SkipEmptyParts);
setMimeFilter( filters );
return;
}
// Strip the escape characters from
// escaped '/' characters.
QString copy (filter);
for (pos = 0; (pos = copy.indexOf("\\/", pos)) != -1; ++pos)
copy.remove(pos, 1);
d->ops->clearFilter();
d->filterWidget->setFilter(copy);
d->ops->setNameFilter(d->filterWidget->currentFilter());
d->ops->updateDir();
d->hasDefaultFilter = false;
d->filterWidget->setEditable( true );
d->updateAutoSelectExtension ();
}
QString KFileWidget::currentFilter() const
{
return d->filterWidget->currentFilter();
}
void KFileWidget::setMimeFilter( const QStringList& mimeTypes,
const QString& defaultType )
{
d->filterWidget->setMimeFilter( mimeTypes, defaultType );
QStringList types = d->filterWidget->currentFilter().split(' ', QString::SkipEmptyParts); //QStringList::split(" ", d->filterWidget->currentFilter());
types.append( QLatin1String( "inode/directory" ));
d->ops->clearFilter();
d->ops->setMimeFilter( types );
d->hasDefaultFilter = !defaultType.isEmpty();
d->filterWidget->setEditable( !d->hasDefaultFilter ||
d->operationMode != Saving );
d->updateAutoSelectExtension ();
}
void KFileWidget::clearFilter()
{
d->filterWidget->setFilter( QString() );
d->ops->clearFilter();
d->hasDefaultFilter = false;
d->filterWidget->setEditable( true );
d->updateAutoSelectExtension ();
}
QString KFileWidget::currentMimeFilter() const
{
int i = d->filterWidget->currentIndex();
if (d->filterWidget->showsAllTypes() && i == 0)
return QString(); // The "all types" item has no mimetype
return d->filterWidget->filters()[i];
}
QMimeType KFileWidget::currentFilterMimeType()
{
QMimeDatabase db;
return db.mimeTypeForName(currentMimeFilter());
}
void KFileWidget::setPreviewWidget(KPreviewWidgetBase *w) {
d->ops->setPreviewWidget(w);
d->ops->clearHistory();
d->hasView = true;
}
QUrl KFileWidgetPrivate::getCompleteUrl(const QString &_url) const
{
// kDebug(kfile_area) << "got url " << _url;
const QString url = KShell::tildeExpand(_url);
QUrl u;
if (QDir::isAbsolutePath(url)) {
u = QUrl::fromLocalFile(url);
} else {
QUrlPathInfo relativeUrlTest(ops->url());
relativeUrlTest.addPath(url);
if (!ops->dirLister()->findByUrl(relativeUrlTest.url()).isNull() ||
!KProtocolInfo::isKnownProtocol(relativeUrlTest.url())) {
u = relativeUrlTest.url();
} else {
u = QUrl(url); // keep it relative
}
}
return u;
}
// Called by KFileDialog
void KFileWidget::slotOk()
{
// kDebug(kfile_area) << "slotOk\n";
const KFileItemList items = d->ops->selectedItems();
const QString locationEditCurrentText(KShell::tildeExpand(d->locationEditCurrentText()));
QList<QUrl> locationEditCurrentTextList(d->tokenize(locationEditCurrentText));
KFile::Modes mode = d->ops->mode();
// if there is nothing to do, just return from here
if (!locationEditCurrentTextList.count()) {
return;
}
// Make sure that one of the modes was provided
if (!((mode & KFile::File) || (mode & KFile::Directory) || (mode & KFile::Files))) {
mode |= KFile::File;
kDebug(kfile_area) << "No mode() provided";
}
// if we are on file mode, and the list of provided files/folder is greater than one, inform
// the user about it
if (locationEditCurrentTextList.count() > 1) {
if (mode & KFile::File) {
KMessageBox::sorry(this,
i18n("You can only select one file"),
i18n("More than one file provided"));
return;
}
/**
* Logic of the next part of code (ends at "end multi relative urls").
*
* We allow for instance to be at "/" and insert '"home/foo/bar.txt" "boot/grub/menu.lst"'.
* Why we need to support this ? Because we provide tree views, which aren't plain.
*
* Now, how does this logic work. It will get the first element on the list (with no filename),
* following the previous example say "/home/foo" and set it as the top most url.
*
* After this, it will iterate over the rest of items and check if this URL (topmost url)
* contains the url being iterated.
*
* As you might have guessed it will do "/home/foo" against "/boot/grub" (again stripping
* filename), and a false will be returned. Then we upUrl the top most url, resulting in
* "/home" against "/boot/grub", what will again return false, so we upUrl again. Now we
* have "/" against "/boot/grub", what returns true for us, so we can say that the closest
* common ancestor of both is "/".
*
* This example has been written for 2 urls, but this works for any number of urls.
*/
if (!d->differentHierarchyLevelItemsEntered) { // avoid infinite recursion. running this
QList<QUrl> urlList; // one time is always enough.
int start = 0;
KUrl topMostUrl;
KIO::StatJob *statJob = 0;
bool res = false;
// we need to check for a valid first url, so in theory we only iterate one time over
// this loop. However it can happen that the user did
// "home/foo/nonexistantfile" "boot/grub/menu.lst", so we look for a good first
// candidate.
while (!res && start < locationEditCurrentTextList.count()) {
topMostUrl = locationEditCurrentTextList.at(start);
statJob = KIO::stat(topMostUrl, KIO::HideProgressInfo);
res = KIO::NetAccess::synchronousRun(statJob, this);
start++;
}
Q_ASSERT(statJob);
// if this is not a dir, strip the filename. after this we have an existent and valid
// dir (if we stated correctly the file, setting a null filename won't make any bad).
if (!statJob->statResult().isDir()) {
topMostUrl.setFileName(QString());
}
// now the funny part. for the rest of filenames, go and look for the closest ancestor
// of all them.
for (int i = start; i < locationEditCurrentTextList.count(); ++i) {
KUrl currUrl = locationEditCurrentTextList.at(i);
KIO::StatJob *statJob = KIO::stat(currUrl, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, this);
if (res) {
// again, we don't care about filenames
if (!statJob->statResult().isDir()) {
currUrl.setFileName(QString());
}
// iterate while this item is contained on the top most url
while (!QUrlPathInfo(topMostUrl).isParentOfOrEqual(currUrl)) {
topMostUrl = topMostUrl.upUrl();
}
}
}
// now recalculate all paths for them being relative in base of the top most url
for (int i = 0; i < locationEditCurrentTextList.count(); ++i) {
locationEditCurrentTextList[i] = QUrl(KUrl::relativeUrl(topMostUrl, locationEditCurrentTextList[i]));
}
d->ops->setUrl(topMostUrl, true);
const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true);
QStringList stringList;
foreach (const KUrl &url, locationEditCurrentTextList) {
stringList << url.prettyUrl();
}
d->locationEdit->lineEdit()->setText(QString("\"%1\"").arg(stringList.join("\" \"")));
d->locationEdit->lineEdit()->blockSignals(signalsBlocked);
d->differentHierarchyLevelItemsEntered = true;
slotOk();
return;
}
/**
* end multi relative urls
*/
} else if (locationEditCurrentTextList.count()) {
// if we are on file or files mode, and we have an absolute url written by
// the user, convert it to relative
if (!locationEditCurrentText.isEmpty() && !(mode & KFile::Directory) &&
(QDir::isAbsolutePath(locationEditCurrentText) ||
containsProtocolSection(locationEditCurrentText))) {
QString fileName;
KUrl url(locationEditCurrentText);
if (d->operationMode == Opening) {
KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, this);
if (res) {
if (!statJob->statResult().isDir()) {
url.adjustPath(KUrl::RemoveTrailingSlash);
fileName = url.fileName();
url.setFileName(QString());
} else {
url.adjustPath(KUrl::AddTrailingSlash);
}
}
} else {
KUrl directory = url;
directory.setFileName(QString());
//Check if the folder exists
KIO::StatJob * statJob = KIO::stat(directory, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, this);
if (res) {
if (statJob->statResult().isDir()) {
url.adjustPath(KUrl::RemoveTrailingSlash);
fileName = url.fileName();
url.setFileName(QString());
}
}
}
d->ops->setUrl(url, true);
const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true);
d->locationEdit->lineEdit()->setText(fileName);
d->locationEdit->lineEdit()->blockSignals(signalsBlocked);
slotOk();
return;
}
}
// restore it
d->differentHierarchyLevelItemsEntered = false;
// locationEditCurrentTextList contains absolute paths
// this is the general loop for the File and Files mode. Obviously we know
// that the File mode will iterate only one time here
bool directoryMode = (mode & KFile::Directory);
bool onlyDirectoryMode = directoryMode && !(mode & KFile::File) && !(mode & KFile::Files);
QList<QUrl>::ConstIterator it = locationEditCurrentTextList.constBegin();
bool filesInList = false;
while (it != locationEditCurrentTextList.constEnd()) {
KUrl url(*it);
if (d->operationMode == Saving && !directoryMode) {
d->appendExtension(url);
}
d->url = url;
KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, this);
if (!KAuthorized::authorizeUrlAction("open", QUrl(), url)) {
QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyUrl());
KMessageBox::error(this, msg);
return;
}
// if we are on local mode, make sure we haven't got a remote base url
if ((mode & KFile::LocalOnly) && !d->mostLocalUrl(d->url).isLocalFile()) {
KMessageBox::sorry(this,
i18n("You can only select local files"),
i18n("Remote files not accepted"));
return;
}
if ((d->operationMode == Saving) && d->confirmOverwrite && !d->toOverwrite(url)) {
return;
}
// if we are given a folder when not on directory mode, let's get into it
if (res && !directoryMode && statJob->statResult().isDir()) {
// check if we were given more than one folder, in that case we don't know to which one
// cd
++it;
while (it != locationEditCurrentTextList.constEnd()) {
KUrl checkUrl(*it);
KIO::StatJob *checkStatJob = KIO::stat(checkUrl, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(checkStatJob, this);
if (res && checkStatJob->statResult().isDir()) {
KMessageBox::sorry(this, i18n("More than one folder has been selected and this dialog does not accept folders, so it is not possible to decide which one to enter. Please select only one folder to list it."), i18n("More than one folder provided"));
return;
} else if (res) {
filesInList = true;
}
++it;
}
if (filesInList) {
KMessageBox::information(this, i18n("At least one folder and one file has been selected. Selected files will be ignored and the selected folder will be listed"), i18n("Files and folders selected"));
}
d->ops->setUrl(url, true);
const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true);
d->locationEdit->lineEdit()->setText(QString());
d->locationEdit->lineEdit()->blockSignals(signalsBlocked);
return;
} else if (!(mode & KFile::ExistingOnly) || res) {
// if we don't care about ExistingOnly flag, add the file even if
// it doesn't exist. If we care about it, don't add it to the list
if (!onlyDirectoryMode || (res && statJob->statResult().isDir())) {
d->urlList << url;
}
filesInList = true;
} else {
KMessageBox::sorry(this, i18n("The file \"%1\" could not be found", url.pathOrUrl()), i18n("Cannot open file"));
return; // do not emit accepted() if we had ExistingOnly flag and stat failed
}
++it;
}
// if we have reached this point and we didn't return before, that is because
// we want this dialog to be accepted
emit accepted();
}
void KFileWidget::accept()
{
d->inAccept = true; // parseSelectedUrls() checks that
*lastDirectory() = d->ops->url();
if (!d->fileClass.isEmpty())
KRecentDirs::add(d->fileClass, d->ops->url().toString());
// clear the topmost item, we insert it as full path later on as item 1
d->locationEdit->setItemText( 0, QString() );
const QList<QUrl> list = selectedUrls();
QList<QUrl>::const_iterator it = list.begin();
int atmost = d->locationEdit->maxItems(); //don't add more items than necessary
for ( ; it != list.end() && atmost > 0; ++it ) {
const KUrl& url = *it;
// we strip the last slash (-1) because KUrlComboBox does that as well
// when operating in file-mode. If we wouldn't , dupe-finding wouldn't
// work.
QString file = url.isLocalFile() ? url.toLocalFile(KUrl::RemoveTrailingSlash) : url.prettyUrl(KUrl::RemoveTrailingSlash);
// remove dupes
for ( int i = 1; i < d->locationEdit->count(); i++ ) {
if ( d->locationEdit->itemText( i ) == file ) {
d->locationEdit->removeItem( i-- );
break;
}
}
//FIXME I don't think this works correctly when the KUrlComboBox has some default urls.
//KUrlComboBox should provide a function to add an url and rotate the existing ones, keeping
//track of maxItems, and we shouldn't be able to insert items as we please.
d->locationEdit->insertItem( 1,file);
atmost--;
}
KSharedConfig::Ptr config = KSharedConfig::openConfig();
KConfigGroup grp(config,ConfigGroup);
d->writeConfig(grp);
d->saveRecentFiles(grp);
d->addToRecentDocuments();
if (!(mode() & KFile::Files)) { // single selection
emit fileSelected(d->url);
}
d->ops->close();
}
void KFileWidgetPrivate::_k_fileHighlighted(const KFileItem &i)
{
if ((!i.isNull() && i.isDir() ) ||
(locationEdit->hasFocus() && !locationEdit->currentText().isEmpty())) // don't disturb
return;
const bool modified = locationEdit->lineEdit()->isModified();
if (!(ops->mode() & KFile::Files)) {
if (i.isNull()) {
if (!modified) {
setLocationText(QUrl());
}
return;
}
url = i.url();
if (!locationEdit->hasFocus()) { // don't disturb while editing
setLocationText( url );
}
emit q->fileHighlighted(url);
} else {
multiSelectionChanged();
emit q->selectionChanged();
}
locationEdit->lineEdit()->setModified( false );
locationEdit->lineEdit()->selectAll();
}
void KFileWidgetPrivate::_k_fileSelected(const KFileItem &i)
{
if (!i.isNull() && i.isDir()) {
return;
}
if (!(ops->mode() & KFile::Files)) {
if (i.isNull()) {
setLocationText(QUrl());
return;
}
setLocationText(i.url());
} else {
multiSelectionChanged();
emit q->selectionChanged();
}
// if we are saving, let another chance to the user before accepting the dialog (or trying to
// accept). This way the user can choose a file and add a "_2" for instance to the filename
if (operationMode == KFileWidget::Saving) {
locationEdit->setFocus();
} else {
q->slotOk();
}
}
// I know it's slow to always iterate thru the whole filelist
// (d->ops->selectedItems()), but what can we do?
void KFileWidgetPrivate::multiSelectionChanged()
{
if (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty()) { // don't disturb
return;
}
const KFileItemList list = ops->selectedItems();
if (list.isEmpty()) {
setLocationText(QUrl());
return;
}
QList<QUrl> urlList;
foreach (const KFileItem &fileItem, list) {
urlList << fileItem.url();
}
setLocationText(urlList);
}
void KFileWidgetPrivate::setDummyHistoryEntry( const QString& text, const QPixmap& icon,
bool usePreviousPixmapIfNull )
{
// setCurrentItem() will cause textChanged() being emitted,
// so slotLocationChanged() will be called. Make sure we don't clear
// the KDirOperator's view-selection in there
QObject::disconnect( locationEdit, SIGNAL(editTextChanged(QString)),
q, SLOT(_k_slotLocationChanged(QString)) );
bool dummyExists = dummyAdded;
int cursorPosition = locationEdit->lineEdit()->cursorPosition();
if ( dummyAdded ) {
if ( !icon.isNull() ) {
locationEdit->setItemIcon( 0, icon );
locationEdit->setItemText( 0, text );
} else {
if ( !usePreviousPixmapIfNull ) {
locationEdit->setItemIcon( 0, QPixmap() );
}
locationEdit->setItemText( 0, text );
}
} else {
if ( !text.isEmpty() ) {
if ( !icon.isNull() ) {
locationEdit->insertItem( 0, icon, text );
} else {
if ( !usePreviousPixmapIfNull ) {
locationEdit->insertItem( 0, QPixmap(), text );
} else {
locationEdit->insertItem( 0, text );
}
}
dummyAdded = true;
dummyExists = true;
}
}
if ( dummyExists && !text.isEmpty() ) {
locationEdit->setCurrentIndex( 0 );
}
locationEdit->lineEdit()->setCursorPosition( cursorPosition );
QObject::connect( locationEdit, SIGNAL(editTextChanged(QString)),
q, SLOT(_k_slotLocationChanged(QString)) );
}
void KFileWidgetPrivate::removeDummyHistoryEntry()
{
if ( !dummyAdded ) {
return;
}
// setCurrentItem() will cause textChanged() being emitted,
// so slotLocationChanged() will be called. Make sure we don't clear
// the KDirOperator's view-selection in there
QObject::disconnect( locationEdit, SIGNAL(editTextChanged(QString)),
q, SLOT(_k_slotLocationChanged(QString)) );
if (locationEdit->count()) {
locationEdit->removeItem( 0 );
}
locationEdit->setCurrentIndex( -1 );
dummyAdded = false;
QObject::connect( locationEdit, SIGNAL(editTextChanged(QString)),
q, SLOT(_k_slotLocationChanged(QString)) );
}
void KFileWidgetPrivate::setLocationText(const QUrl& url)
{
if (!url.isEmpty()) {
QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KIO::iconNameForUrl( url ), KIconLoader::Small );
QUrlPathInfo urlPathInfo(url);
const QString path = url.path();
if (!path.isEmpty()) {
if (!urlPathInfo.directory().isEmpty()) {
KUrl u(url);
u.setPath(urlPathInfo.directory());
q->setUrl(u, false);
} else {
q->setUrl(url, false);
}
}
setDummyHistoryEntry(urlPathInfo.fileName() , mimeTypeIcon);
} else {
removeDummyHistoryEntry();
}
// don't change selection when user has clicked on an item
if (operationMode == KFileWidget::Saving && !locationEdit->isVisible()) {
setNonExtSelection();
}
}
void KFileWidgetPrivate::setLocationText( const QList<QUrl>& urlList )
{
const QUrl currUrl = ops->url();
if ( urlList.count() > 1 ) {
QString urls;
foreach (const QUrl &url, urlList) {
urls += QString( "\"%1\"" ).arg( KUrl::relativeUrl(currUrl, url) ) + ' ';
}
urls = urls.left( urls.size() - 1 );
setDummyHistoryEntry( urls, QPixmap(), false );
} else if ( urlList.count() ) {
const QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KIO::iconNameForUrl( urlList[0] ), KIconLoader::Small );
setDummyHistoryEntry( KUrl::relativeUrl(currUrl, urlList[0]), mimeTypeIcon );
} else {
removeDummyHistoryEntry();
}
// don't change selection when user has clicked on an item
if ( operationMode == KFileWidget::Saving && !locationEdit->isVisible())
setNonExtSelection();
}
void KFileWidgetPrivate::updateLocationWhatsThis()
{
QString whatsThisText;
if (operationMode == KFileWidget::Saving)
{
whatsThisText = "<qt>" + i18n("This is the name to save the file as.") +
i18n (autocompletionWhatsThisText);
}
else if (ops->mode() & KFile::Files)
{
whatsThisText = "<qt>" + i18n("This is the list of files to open. More than "
"one file can be specified by listing several "
"files, separated by spaces.") +
i18n (autocompletionWhatsThisText);
}
else
{
whatsThisText = "<qt>" + i18n("This is the name of the file to open.") +
i18n (autocompletionWhatsThisText);
}
locationLabel->setWhatsThis(whatsThisText);
locationEdit->setWhatsThis(whatsThisText);
}
void KFileWidgetPrivate::initSpeedbar()
{
if (placesDock) {
return;
}
placesDock = new QDockWidget(i18nc("@title:window", "Places"), q);
placesDock->setFeatures(QDockWidget::DockWidgetClosable);
placesView = new KFilePlacesView(placesDock);
placesView->setModel(model);
placesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
placesView->setObjectName(QLatin1String("url bar"));
QObject::connect(placesView, SIGNAL(urlChanged(QUrl)),
q, SLOT(_k_enterUrl(QUrl)));
// need to set the current url of the urlbar manually (not via urlEntered()
// here, because the initial url of KDirOperator might be the same as the
// one that will be set later (and then urlEntered() won't be emitted).
// TODO: KDE5 ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone.
placesView->setUrl(url);
placesDock->setWidget(placesView);
placesViewSplitter->insertWidget(0, placesDock);
// initialize the size of the splitter
KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroup);
placesViewWidth = configGroup.readEntry(SpeedbarWidth, placesView->sizeHint().width());
QList<int> sizes = placesViewSplitter->sizes();
if (placesViewWidth > 0) {
sizes[0] = placesViewWidth + 1;
sizes[1] = q->width() - placesViewWidth -1;
placesViewSplitter->setSizes(sizes);
}
QObject::connect(placesDock, SIGNAL(visibilityChanged(bool)),
q, SLOT(_k_toggleSpeedbar(bool)));
}
void KFileWidgetPrivate::initGUI()
{
delete boxLayout; // deletes all sub layouts
boxLayout = new QVBoxLayout( q);
boxLayout->setMargin(0); // no additional margin to the already existing
placesViewSplitter = new QSplitter(q);
placesViewSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
placesViewSplitter->setChildrenCollapsible(false);
boxLayout->addWidget(placesViewSplitter);
QObject::connect(placesViewSplitter, SIGNAL(splitterMoved(int,int)),
q, SLOT(_k_placesViewSplitterMoved(int,int)));
placesViewSplitter->insertWidget(0, opsWidget);
vbox = new QVBoxLayout();
vbox->setMargin(0);
boxLayout->addLayout(vbox);
lafBox = new QGridLayout();
lafBox->addWidget(locationLabel, 0, 0, Qt::AlignVCenter | Qt::AlignRight);
lafBox->addWidget(locationEdit, 0, 1, Qt::AlignVCenter);
lafBox->addWidget(okButton, 0, 2, Qt::AlignVCenter);
lafBox->addWidget(filterLabel, 1, 0, Qt::AlignVCenter | Qt::AlignRight);
lafBox->addWidget(filterWidget, 1, 1, Qt::AlignVCenter);
lafBox->addWidget(cancelButton, 1, 2, Qt::AlignVCenter);
lafBox->setColumnStretch(1, 4);
vbox->addLayout(lafBox);
// add the Automatically Select Extension checkbox
vbox->addWidget(autoSelectExtCheckBox);
q->setTabOrder(ops, autoSelectExtCheckBox);
q->setTabOrder(autoSelectExtCheckBox, locationEdit);
q->setTabOrder(locationEdit, filterWidget);
q->setTabOrder(filterWidget, okButton);
q->setTabOrder(okButton, cancelButton);
q->setTabOrder(cancelButton, urlNavigator);
q->setTabOrder(urlNavigator, ops);
q->setTabOrder(cancelButton, urlNavigator);
q->setTabOrder(urlNavigator, ops);
}
void KFileWidgetPrivate::_k_slotFilterChanged()
{
// kDebug(kfile_area);
filterDelayTimer.stop();
QString filter = filterWidget->currentFilter();
ops->clearFilter();
if ( filter.contains('/') ) {
QStringList types = filter.split(' ', QString::SkipEmptyParts);
types.prepend("inode/directory");
ops->setMimeFilter( types );
}
else if ( filter.contains('*') || filter.contains('?') || filter.contains('[') ) {
ops->setNameFilter( filter );
}
else {
ops->setNameFilter('*' + filter.replace(' ', '*') + '*');
}
ops->updateDir();
updateAutoSelectExtension();
emit q->filterChanged(filter);
}
void KFileWidget::setUrl(const QUrl& url, bool clearforward)
{
// kDebug(kfile_area);
d->ops->setUrl(url, clearforward);
}
// Protected
void KFileWidgetPrivate::_k_urlEntered(const QUrl& url)
{
// kDebug(kfile_area);
QString filename = locationEditCurrentText();
KUrlComboBox* pathCombo = urlNavigator->editor();
if (pathCombo->count() != 0) { // little hack
pathCombo->setUrl(url);
}
bool blocked = locationEdit->blockSignals(true);
if (keepLocation) {
QUrl currentUrl = QUrl::fromUserInput(filename);
locationEdit->changeUrl(0, KDE::icon(KIO::iconNameForUrl(currentUrl)), currentUrl);
locationEdit->lineEdit()->setModified(true);
}
locationEdit->blockSignals( blocked );
urlNavigator->setLocationUrl(url);
// is trigged in ctor before completion object is set
KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>(locationEdit->completionObject());
if (completion) {
completion->setDir(url);
}
if (placesView) {
placesView->setUrl( url );
}
}
void KFileWidgetPrivate::_k_locationAccepted(const QString &url)
{
Q_UNUSED(url);
// kDebug(kfile_area);
q->slotOk();
}
void KFileWidgetPrivate::_k_enterUrl(const QUrl& url)
{
// kDebug(kfile_area);
KUrl fixedUrl( url );
// append '/' if needed: url combo does not add it
// tokenize() expects it because uses KUrl::setFileName()
fixedUrl.adjustPath( KUrl::AddTrailingSlash );
q->setUrl( fixedUrl );
if (!locationEdit->hasFocus())
ops->setFocus();
}
void KFileWidgetPrivate::_k_enterUrl( const QString& url )
{
// kDebug(kfile_area);
_k_enterUrl(QUrl::fromUserInput(KUrlCompletion::replacedPath(url, true, true)));
}
bool KFileWidgetPrivate::toOverwrite(const QUrl &url)
{
// kDebug(kfile_area);
KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, q);
QUrlPathInfo urlPathInfo(url);
if (res) {
int ret = KMessageBox::warningContinueCancel( q,
i18n( "The file \"%1\" already exists. Do you wish to overwrite it?" ,
urlPathInfo.fileName() ), i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(),
KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
if (ret != KMessageBox::Continue) {
return false;
}
return true;
}
return true;
}
void KFileWidget::setSelection(const QString& url)
{
// kDebug(kfile_area) << "setSelection " << url;
if (url.isEmpty()) {
return;
}
QUrl u = d->getCompleteUrl(url);
if (!u.isValid()) { // if it still is
kWarning() << url << " is not a correct argument for setSelection!";
return;
}
// Honor protocols that do not support directory listing
if (!u.isRelative() && !KProtocolManager::supportsListing(u))
return;
d->setLocationText(QUrl(url));
}
void KFileWidgetPrivate::_k_slotLoadingFinished()
{
if (locationEdit->currentText().isEmpty()) {
return;
}
ops->blockSignals(true);
KUrl url = ops->url();
url.adjustPath(KUrl::AddTrailingSlash);
url.setFileName(locationEdit->currentText());
ops->setCurrentItem(url);
ops->blockSignals(false);
}
void KFileWidgetPrivate::_k_fileCompletion( const QString& match )
{
// kDebug(kfile_area);
if (match.isEmpty() || locationEdit->currentText().contains('"')) {
return;
}
const QPixmap pix = KIconLoader::global()->loadMimeTypeIcon(KIO::iconNameForUrl(QUrl(match)), KIconLoader::Small);
setDummyHistoryEntry(locationEdit->currentText(), pix, !locationEdit->currentText().isEmpty());
}
void KFileWidgetPrivate::_k_slotLocationChanged( const QString& text )
{
// kDebug(kfile_area);
locationEdit->lineEdit()->setModified(true);
if (text.isEmpty() && ops->view()) {
ops->view()->clearSelection();
}
if (text.isEmpty()) {
removeDummyHistoryEntry();
} else {
setDummyHistoryEntry( text );
}
if (!locationEdit->lineEdit()->text().isEmpty()) {
const QList<QUrl> urlList(tokenize(text));
ops->setCurrentItems(urlList);
}
updateFilter();
}
QUrl KFileWidget::selectedUrl() const
{
// kDebug(kfile_area);
if ( d->inAccept )
return d->url;
else
return QUrl();
}
QList<QUrl> KFileWidget::selectedUrls() const
{
// kDebug(kfile_area);
QList<QUrl> list;
if ( d->inAccept ) {
if (d->ops->mode() & KFile::Files)
list = d->parseSelectedUrls();
else
list.append( d->url );
}
return list;
}
QList<QUrl>& KFileWidgetPrivate::parseSelectedUrls()
{
// kDebug(kfile_area);
if ( filenames.isEmpty() ) {
return urlList;
}
urlList.clear();
if ( filenames.contains( '/' )) { // assume _one_ absolute filename
KUrl u;
if ( containsProtocolSection( filenames ) )
u = filenames;
else
u.setPath( filenames );
if ( u.isValid() )
urlList.append( u );
else
KMessageBox::error( q,
i18n("The chosen filenames do not\n"
"appear to be valid."),
i18n("Invalid Filenames") );
}
else
urlList = tokenize( filenames );
filenames.clear(); // indicate that we parsed that one
return urlList;
}
// FIXME: current implementation drawback: a filename can't contain quotes
QList<QUrl> KFileWidgetPrivate::tokenize( const QString& line ) const
{
// kDebug(kfile_area);
QList<QUrl> urls;
KUrl u( ops->url() );
u.adjustPath(KUrl::AddTrailingSlash);
QString name;
const int count = line.count( QLatin1Char( '"' ) );
if ( count == 0 ) { // no " " -> assume one single file
if (!QDir::isAbsolutePath(line)) {
u.setFileName( line );
if ( u.isValid() )
urls.append( u );
} else {
urls << QUrl::fromUserInput(line);
}
return urls;
}
int start = 0;
int index1 = -1, index2 = -1;
while ( true ) {
index1 = line.indexOf( '"', start );
index2 = line.indexOf( '"', index1 + 1 );
if ( index1 < 0 || index2 < 0 )
break;
// get everything between the " "
name = line.mid( index1 + 1, index2 - index1 - 1 );
// since we use setFileName we need to do this under a temporary url
KUrl _u( u );
KUrl currUrl( name );
if ( !QDir::isAbsolutePath(currUrl.url()) ) {
_u.setFileName( name );
} else {
// we allow to insert various absolute paths like:
// "/home/foo/bar.txt" "/boot/grub/menu.lst"
_u = currUrl;
}
if ( _u.isValid() ) {
urls.append( _u );
}
start = index2 + 1;
}
return urls;
}
QString KFileWidget::selectedFile() const
{
// kDebug(kfile_area);
if ( d->inAccept ) {
const KUrl url = d->mostLocalUrl(d->url);
if (url.isLocalFile())
return url.toLocalFile();
else {
KMessageBox::sorry( const_cast<KFileWidget*>(this),
i18n("You can only select local files."),
i18n("Remote Files Not Accepted") );
}
}
return QString();
}
QStringList KFileWidget::selectedFiles() const
{
// kDebug(kfile_area);
QStringList list;
if (d->inAccept) {
if (d->ops->mode() & KFile::Files) {
const QList<QUrl> urls = d->parseSelectedUrls();
QList<QUrl>::const_iterator it = urls.begin();
while (it != urls.end()) {
KUrl url = d->mostLocalUrl(*it);
if (url.isLocalFile())
list.append(url.toLocalFile());
++it;
}
}
else { // single-selection mode
if ( d->url.isLocalFile() )
list.append( d->url.toLocalFile() );
}
}
return list;
}
QUrl KFileWidget::baseUrl() const
{
return d->ops->url();
}
void KFileWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
if (d->placesDock) {
// we don't want our places dock actually changing size when we resize
// and qt doesn't make it easy to enforce such a thing with QSplitter
QList<int> sizes = d->placesViewSplitter->sizes();
sizes[0] = d->placesViewWidth + 1; // without this pixel, our places view is reduced 1 pixel each time is shown.
sizes[1] = width() - d->placesViewWidth - 1;
d->placesViewSplitter->setSizes( sizes );
}
}
void KFileWidget::showEvent(QShowEvent* event)
{
if ( !d->hasView ) { // delayed view-creation
Q_ASSERT( d );
Q_ASSERT( d->ops );
d->ops->setView( KFile::Default );
d->ops->view()->setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) );
d->hasView = true;
}
d->ops->clearHistory();
QWidget::showEvent(event);
}
bool KFileWidget::eventFilter(QObject* watched, QEvent* event)
{
const bool res = QWidget::eventFilter(watched, event);
QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
if (watched == d->iconSizeSlider && keyEvent) {
if (keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Up ||
keyEvent->key() == Qt::Key_Right || keyEvent->key() == Qt::Key_Down) {
d->_k_slotIconSizeSliderMoved(d->iconSizeSlider->value());
}
} else if (watched == d->locationEdit && event->type() == QEvent::KeyPress) {
if (keyEvent->modifiers() & Qt::AltModifier) {
switch (keyEvent->key()) {
case Qt::Key_Up:
d->ops->actionCollection()->action("up")->trigger();
break;
case Qt::Key_Left:
d->ops->actionCollection()->action("back")->trigger();
break;
case Qt::Key_Right:
d->ops->actionCollection()->action("forward")->trigger();
break;
default:
break;
}
}
}
return res;
}
void KFileWidget::setMode( KFile::Modes m )
{
// kDebug(kfile_area);
d->ops->setMode(m);
if ( d->ops->dirOnlyMode() ) {
d->filterWidget->setDefaultFilter( i18n("*|All Folders") );
}
else {
d->filterWidget->setDefaultFilter( i18n("*|All Files") );
}
d->updateAutoSelectExtension();
}
KFile::Modes KFileWidget::mode() const
{
return d->ops->mode();
}
void KFileWidgetPrivate::readConfig(KConfigGroup &configGroup)
{
// kDebug(kfile_area);
readRecentFiles(configGroup);
ops->setViewConfig(configGroup);
ops->readConfig(configGroup);
KUrlComboBox *combo = urlNavigator->editor();
combo->setUrls( configGroup.readPathEntry( RecentURLs, QStringList() ), KUrlComboBox::RemoveTop );
combo->setMaxItems( configGroup.readEntry( RecentURLsNumber,
DefaultRecentURLsNumber ) );
combo->setUrl( ops->url() );
autoDirectoryFollowing = configGroup.readEntry(AutoDirectoryFollowing,
DefaultDirectoryFollowing);
KGlobalSettings::Completion cm = (KGlobalSettings::Completion)
configGroup.readEntry( PathComboCompletionMode,
static_cast<int>( KGlobalSettings::completionMode() ) );
if ( cm != KGlobalSettings::completionMode() )
combo->setCompletionMode( cm );
cm = (KGlobalSettings::Completion)
configGroup.readEntry( LocationComboCompletionMode,
static_cast<int>( KGlobalSettings::completionMode() ) );
if ( cm != KGlobalSettings::completionMode() )
locationEdit->setCompletionMode( cm );
// since we delayed this moment, initialize the directory of the completion object to
// our current directory (that was very probably set on the constructor)
KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>(locationEdit->completionObject());
if (completion) {
completion->setDir(ops->url());
}
// show or don't show the speedbar
_k_toggleSpeedbar( configGroup.readEntry( ShowSpeedbar, true ) );
// show or don't show the bookmarks
_k_toggleBookmarks( configGroup.readEntry(ShowBookmarks, false) );
// does the user want Automatically Select Extension?
autoSelectExtChecked = configGroup.readEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked);
updateAutoSelectExtension();
// should the URL navigator use the breadcrumb navigation?
urlNavigator->setUrlEditable( !configGroup.readEntry(BreadcrumbNavigation, true) );
// should the URL navigator show the full path?
urlNavigator->setShowFullPath( configGroup.readEntry(ShowFullPath, false) );
int w1 = q->minimumSize().width();
int w2 = toolbar->sizeHint().width();
if (w1 < w2)
q->setMinimumWidth(w2);
}
void KFileWidgetPrivate::writeConfig(KConfigGroup &configGroup)
{
// kDebug(kfile_area);
// these settings are global settings; ALL instances of the file dialog
// should reflect them
KConfig config("kdeglobals");
KConfigGroup group(&config, configGroup.name());
KUrlComboBox *pathCombo = urlNavigator->editor();
group.writePathEntry( RecentURLs, pathCombo->urls() );
//saveDialogSize( group, KConfigGroup::Persistent | KConfigGroup::Global );
group.writeEntry( PathComboCompletionMode, static_cast<int>(pathCombo->completionMode()) );
group.writeEntry( LocationComboCompletionMode, static_cast<int>(locationEdit->completionMode()) );
const bool showSpeedbar = placesDock && !placesDock->isHidden();
group.writeEntry( ShowSpeedbar, showSpeedbar );
if (showSpeedbar) {
const QList<int> sizes = placesViewSplitter->sizes();
Q_ASSERT( sizes.count() > 0 );
group.writeEntry( SpeedbarWidth, sizes[0] );
}
group.writeEntry( ShowBookmarks, bookmarkHandler != 0 );
group.writeEntry( AutoSelectExtChecked, autoSelectExtChecked );
group.writeEntry( BreadcrumbNavigation, !urlNavigator->isUrlEditable() );
group.writeEntry( ShowFullPath, urlNavigator->showFullPath() );
ops->writeConfig(group);
}
void KFileWidgetPrivate::readRecentFiles(KConfigGroup &cg)
{
// kDebug(kfile_area);
QObject::disconnect(locationEdit, SIGNAL(editTextChanged(QString)),
q, SLOT(_k_slotLocationChanged(QString)));
locationEdit->setMaxItems(cg.readEntry(RecentFilesNumber, DefaultRecentURLsNumber));
locationEdit->setUrls(cg.readPathEntry(RecentFiles, QStringList()),
KUrlComboBox::RemoveBottom);
locationEdit->setCurrentIndex(-1);
QObject::connect(locationEdit, SIGNAL(editTextChanged(QString)),
q, SLOT(_k_slotLocationChanged(QString)));
}
void KFileWidgetPrivate::saveRecentFiles(KConfigGroup &cg)
{
// kDebug(kfile_area);
cg.writePathEntry(RecentFiles, locationEdit->urls());
}
QPushButton * KFileWidget::okButton() const
{
return d->okButton;
}
QPushButton * KFileWidget::cancelButton() const
{
return d->cancelButton;
}
// Called by KFileDialog
void KFileWidget::slotCancel()
{
// kDebug(kfile_area);
d->ops->close();
KConfigGroup grp(KSharedConfig::openConfig(), ConfigGroup);
d->writeConfig(grp);
}
void KFileWidget::setKeepLocation( bool keep )
{
d->keepLocation = keep;
}
bool KFileWidget::keepsLocation() const
{
return d->keepLocation;
}
void KFileWidget::setOperationMode( OperationMode mode )
{
// kDebug(kfile_area);
d->operationMode = mode;
d->keepLocation = (mode == Saving);
d->filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving );
if ( mode == Opening ) {
// don't use KStandardGuiItem::open() here which has trailing ellipsis!
d->okButton->setText(i18n("&Open"));
d->okButton->setIcon(KDE::icon("document-open"));
// hide the new folder actions...usability team says they shouldn't be in open file dialog
actionCollection()->removeAction( actionCollection()->action("mkdir" ) );
} else if ( mode == Saving ) {
KGuiItem::assign(d->okButton, KStandardGuiItem::save());
d->setNonExtSelection();
} else {
KGuiItem::assign(d->okButton, KStandardGuiItem::ok());
}
d->updateLocationWhatsThis();
d->updateAutoSelectExtension();
if (d->ops) {
d->ops->setIsSaving(mode == Saving);
}
}
KFileWidget::OperationMode KFileWidget::operationMode() const
{
return d->operationMode;
}
void KFileWidgetPrivate::_k_slotAutoSelectExtClicked()
{
// kDebug (kfile_area) << "slotAutoSelectExtClicked(): "
// << autoSelectExtCheckBox->isChecked() << endl;
// whether the _user_ wants it on/off
autoSelectExtChecked = autoSelectExtCheckBox->isChecked();
// update the current filename's extension
updateLocationEditExtension (extension /* extension hasn't changed */);
}
void KFileWidgetPrivate::_k_placesViewSplitterMoved(int pos, int index)
{
// kDebug(kfile_area);
// we need to record the size of the splitter when the splitter changes size
// so we can keep the places box the right size!
if (placesDock && index == 1) {
placesViewWidth = pos;
// kDebug() << "setting lafBox minwidth to" << placesViewWidth;
lafBox->setColumnMinimumWidth(0, placesViewWidth);
}
}
void KFileWidgetPrivate::_k_activateUrlNavigator()
{
// kDebug(kfile_area);
urlNavigator->setUrlEditable(!urlNavigator->isUrlEditable());
if(urlNavigator->isUrlEditable()) {
urlNavigator->setFocus();
urlNavigator->editor()->lineEdit()->selectAll();
}
}
void KFileWidgetPrivate::_k_zoomOutIconsSize()
{
const int currValue = ops->iconsZoom();
const int futValue = qMax(0, currValue - 10);
iconSizeSlider->setValue(futValue);
_k_slotIconSizeSliderMoved(futValue);
}
void KFileWidgetPrivate::_k_zoomInIconsSize()
{
const int currValue = ops->iconsZoom();
const int futValue = qMin(100, currValue + 10);
iconSizeSlider->setValue(futValue);
_k_slotIconSizeSliderMoved(futValue);
}
void KFileWidgetPrivate::_k_slotIconSizeChanged(int _value)
{
int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall;
int value = (maxSize * _value / 100) + KIconLoader::SizeSmall;
switch (value) {
case KIconLoader::SizeSmall:
case KIconLoader::SizeSmallMedium:
case KIconLoader::SizeMedium:
case KIconLoader::SizeLarge:
case KIconLoader::SizeHuge:
case KIconLoader::SizeEnormous:
iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels (standard size)", value));
break;
default:
iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels", value));
break;
}
}
void KFileWidgetPrivate::_k_slotIconSizeSliderMoved(int _value)
{
// Force this to be called in case this slot is called first on the
// slider move.
_k_slotIconSizeChanged(_value);
QPoint global(iconSizeSlider->rect().topLeft());
global.ry() += iconSizeSlider->height() / 2;
QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), iconSizeSlider->mapToGlobal(global));
QApplication::sendEvent(iconSizeSlider, &toolTipEvent);
}
static QString getExtensionFromPatternList(const QStringList &patternList)
{
// kDebug(kfile_area);
QString ret;
// kDebug (kfile_area) << "\tgetExtension " << patternList;
QStringList::ConstIterator patternListEnd = patternList.end();
for (QStringList::ConstIterator it = patternList.begin();
it != patternListEnd;
++it)
{
// kDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'";
// is this pattern like "*.BMP" rather than useless things like:
//
// README
// *.
// *.*
// *.JP*G
// *.JP?
if ((*it).startsWith (QLatin1String("*.")) &&
(*it).length() > 2 &&
(*it).indexOf('*', 2) < 0 && (*it).indexOf ('?', 2) < 0)
{
ret = (*it).mid (1);
break;
}
}
return ret;
}
static QString stripUndisplayable (const QString &string)
{
QString ret = string;
ret.remove (':');
ret = KLocalizedString::removeAcceleratorMarker (ret);
return ret;
}
//QString KFileWidget::currentFilterExtension()
//{
// return d->extension;
//}
void KFileWidgetPrivate::updateAutoSelectExtension()
{
if (!autoSelectExtCheckBox) return;
QMimeDatabase db;
//
// Figure out an extension for the Automatically Select Extension thing
// (some Windows users apparently don't know what to do when confronted
// with a text file called "COPYING" but do know what to do with
// COPYING.txt ...)
//
// kDebug (kfile_area) << "Figure out an extension: ";
QString lastExtension = extension;
extension.clear();
// Automatically Select Extension is only valid if the user is _saving_ a _file_
if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File))
{
//
// Get an extension from the filter
//
QString filter = filterWidget->currentFilter();
if (!filter.isEmpty())
{
// if the currently selected filename already has an extension which
// is also included in the currently allowed extensions, keep it
// otherwise use the default extension
QString currentExtension = db.suffixForFileName(locationEditCurrentText());
if ( currentExtension.isEmpty() )
currentExtension = locationEditCurrentText().section(QLatin1Char('.'), -1, -1);
kDebug (kfile_area) << "filter:" << filter << "locationEdit:" << locationEditCurrentText()
<< "currentExtension:" << currentExtension;
QString defaultExtension;
QStringList extensionList;
// e.g. "*.cpp"
if (filter.indexOf ('/') < 0)
{
extensionList = filter.split(' ', QString::SkipEmptyParts);
defaultExtension = getExtensionFromPatternList(extensionList);
}
// e.g. "text/html"
else
{
QMimeType mime = db.mimeTypeForName(filter);
if (mime.isValid()) {
extensionList = mime.globPatterns();
defaultExtension = mime.preferredSuffix();
if (!defaultExtension.isEmpty())
defaultExtension.prepend(QLatin1Char('.'));
}
}
if ( !currentExtension.isEmpty() && extensionList.contains(QLatin1String("*.") + currentExtension) )
extension = QLatin1Char('.') + currentExtension;
else
extension = defaultExtension;
kDebug (kfile_area) << "List:" << extensionList << "auto-selected extension:" << extension;
}
//
// GUI: checkbox
//
QString whatsThisExtension;
if (!extension.isEmpty())
{
// remember: sync any changes to the string with below
autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)", extension));
whatsThisExtension = i18n ("the extension <b>%1</b>", extension);
autoSelectExtCheckBox->setEnabled (true);
autoSelectExtCheckBox->setChecked (autoSelectExtChecked);
}
else
{
// remember: sync any changes to the string with above
autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension"));
whatsThisExtension = i18n ("a suitable extension");
autoSelectExtCheckBox->setChecked (false);
autoSelectExtCheckBox->setEnabled (false);
}
const QString locationLabelText = stripUndisplayable (locationLabel->text());
const QString filterLabelText = stripUndisplayable (filterLabel->text());
autoSelectExtCheckBox->setWhatsThis( "<qt>" +
i18n (
"This option enables some convenient features for "
"saving files with extensions:<br />"
"<ol>"
"<li>Any extension specified in the <b>%1</b> text "
"area will be updated if you change the file type "
"to save in.<br />"
"<br /></li>"
"<li>If no extension is specified in the <b>%2</b> "
"text area when you click "
"<b>Save</b>, %3 will be added to the end of the "
"filename (if the filename does not already exist). "
"This extension is based on the file type that you "
"have chosen to save in.<br />"
"<br />"
"If you do not want KDE to supply an extension for the "
"filename, you can either turn this option off or you "
"can suppress it by adding a period (.) to the end of "
"the filename (the period will be automatically "
"removed)."
"</li>"
"</ol>"
"If unsure, keep this option enabled as it makes your "
"files more manageable."
,
locationLabelText,
locationLabelText,
whatsThisExtension)
+ "</qt>"
);
autoSelectExtCheckBox->show();
// update the current filename's extension
updateLocationEditExtension (lastExtension);
}
// Automatically Select Extension not valid
else
{
autoSelectExtCheckBox->setChecked (false);
autoSelectExtCheckBox->hide();
}
}
// Updates the extension of the filename specified in d->locationEdit if the
// Automatically Select Extension feature is enabled.
// (this prevents you from accidently saving "file.kwd" as RTF, for example)
void KFileWidgetPrivate::updateLocationEditExtension (const QString &lastExtension)
{
if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty())
return;
QString urlStr = locationEditCurrentText();
if (urlStr.isEmpty())
return;
KUrl url = getCompleteUrl(urlStr);
// kDebug (kfile_area) << "updateLocationEditExtension (" << url << ")";
const int fileNameOffset = urlStr.lastIndexOf ('/') + 1;
QString fileName = urlStr.mid (fileNameOffset);
const int dot = fileName.lastIndexOf ('.');
const int len = fileName.length();
if (dot > 0 && // has an extension already and it's not a hidden file
// like ".hidden" (but we do accept ".hidden.ext")
dot != len - 1 // and not deliberately suppressing extension
)
{
// exists?
KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
bool result = KIO::NetAccess::synchronousRun(statJob, q);
if (result)
{
// kDebug (kfile_area) << "\tfile exists";
if (statJob->statResult().isDir())
{
// kDebug (kfile_area) << "\tisDir - won't alter extension";
return;
}
// --- fall through ---
}
//
// try to get rid of the current extension
//
// catch "double extensions" like ".tar.gz"
if (lastExtension.length() && fileName.endsWith (lastExtension))
fileName.truncate (len - lastExtension.length());
else if (extension.length() && fileName.endsWith (extension))
fileName.truncate (len - extension.length());
// can only handle "single extensions"
else
fileName.truncate (dot);
// add extension
const QString newText = urlStr.left (fileNameOffset) + fileName + extension;
if ( newText != locationEditCurrentText() )
{
locationEdit->setItemText(locationEdit->currentIndex(),urlStr.left (fileNameOffset) + fileName + extension);
locationEdit->lineEdit()->setModified (true);
}
}
}
// Updates the filter if the extension of the filename specified in d->locationEdit is changed
// (this prevents you from accidently saving "file.kwd" as RTF, for example)
void KFileWidgetPrivate::updateFilter()
{
// kDebug(kfile_area);
if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File) ) {
QString urlStr = locationEditCurrentText();
if (urlStr.isEmpty())
return;
if( filterWidget->isMimeFilter()) {
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(urlStr, QMimeDatabase::MatchExtension);
if (mime.isValid() && !mime.isDefault()) {
if (filterWidget->currentFilter() != mime.name() &&
filterWidget->filters().indexOf(mime.name()) != -1)
filterWidget->setCurrentFilter(mime.name());
}
} else {
QString filename = urlStr.mid( urlStr.lastIndexOf( '/' ) + 1 ); // only filename
foreach( const QString& filter, filterWidget->filters()) {
QStringList patterns = filter.left( filter.indexOf( '|' )).split ( ' ', QString::SkipEmptyParts ); // '*.foo *.bar|Foo type' -> '*.foo', '*.bar'
foreach ( const QString& p, patterns ) {
QRegExp rx(p);
rx.setPatternSyntax(QRegExp::Wildcard);
if (rx.exactMatch(filename)) {
if ( p != "*" ) { // never match the catch-all filter
filterWidget->setCurrentFilter( filter );
}
return; // do not repeat, could match a later filter
}
}
}
}
}
}
// applies only to a file that doesn't already exist
void KFileWidgetPrivate::appendExtension (QUrl &url)
{
// kDebug(kfile_area);
if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty())
return;
QUrlPathInfo urlPathInfo(url);
QString fileName = urlPathInfo.fileName();
if (fileName.isEmpty())
return;
// kDebug (kfile_area) << "appendExtension(" << url << ")";
const int len = fileName.length();
const int dot = fileName.lastIndexOf ('.');
const bool suppressExtension = (dot == len - 1);
const bool unspecifiedExtension = (dot <= 0);
// don't KIO::Stat if unnecessary
if (!(suppressExtension || unspecifiedExtension))
return;
// exists?
KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, q);
if (res)
{
// kDebug (kfile_area) << "\tfile exists - won't append extension";
return;
}
// suppress automatically append extension?
if (suppressExtension)
{
//
// Strip trailing dot
// This allows lazy people to have autoSelectExtCheckBox->isChecked
// but don't want a file extension to be appended
// e.g. "README." will make a file called "README"
//
// If you really want a name like "README.", then type "README.."
// and the trailing dot will be removed (or just stop being lazy and
// turn off this feature so that you can type "README.")
//
// kDebug (kfile_area) << "\tstrip trailing dot";
urlPathInfo.setFileName (fileName.left (len - 1));
}
// evilmatically append extension :) if the user hasn't specified one
else if (unspecifiedExtension)
{
// kDebug (kfile_area) << "\tappending extension \'" << extension << "\'...";
urlPathInfo.setFileName (fileName + extension);
// kDebug (kfile_area) << "\tsaving as \'" << url << "\'";
}
url = urlPathInfo.url();
}
// adds the selected files/urls to 'recent documents'
void KFileWidgetPrivate::addToRecentDocuments()
{
int m = ops->mode();
int atmost = KRecentDocument::maximumItems();
//don't add more than we need. KRecentDocument::add() is pretty slow
if (m & KFile::LocalOnly) {
const QStringList files = q->selectedFiles();
QStringList::ConstIterator it = files.begin();
for ( ; it != files.end() && atmost > 0; ++it ) {
KRecentDocument::add( *it );
atmost--;
}
}
else { // urls
const QList<QUrl> urls = q->selectedUrls();
QList<QUrl>::ConstIterator it = urls.begin();
for ( ; it != urls.end() && atmost > 0; ++it ) {
if ( (*it).isValid() ) {
KRecentDocument::add( *it );
atmost--;
}
}
}
}
KUrlComboBox* KFileWidget::locationEdit() const
{
return d->locationEdit;
}
KFileFilterCombo* KFileWidget::filterWidget() const
{
return d->filterWidget;
}
KActionCollection * KFileWidget::actionCollection() const
{
return d->ops->actionCollection();
}
void KFileWidgetPrivate::_k_toggleSpeedbar(bool show)
{
if (show) {
initSpeedbar();
placesDock->show();
lafBox->setColumnMinimumWidth(0, placesViewWidth);
// check to see if they have a home item defined, if not show the home button
KUrl homeURL;
homeURL.setPath( QDir::homePath() );
KFilePlacesModel *model = static_cast<KFilePlacesModel*>(placesView->model());
for (int rowIndex = 0 ; rowIndex < model->rowCount() ; rowIndex++) {
QModelIndex index = model->index(rowIndex, 0);
KUrl url = model->url(index);
if ( homeURL.equals( url, KUrl::CompareWithoutTrailingSlash ) ) {
toolbar->removeAction( ops->actionCollection()->action( "home" ) );
break;
}
}
} else {
if (q->sender() == placesDock && placesDock && placesDock->isVisibleTo(q)) {
// we didn't *really* go away! the dialog was simply hidden or
// we changed virtual desktops or ...
return;
}
if (placesDock) {
placesDock->hide();
}
QAction* homeAction = ops->actionCollection()->action("home");
QAction* reloadAction = ops->actionCollection()->action("reload");
if (!toolbar->actions().contains(homeAction)) {
toolbar->insertAction(reloadAction, homeAction);
}
// reset the lafbox to not follow the width of the splitter
lafBox->setColumnMinimumWidth(0, 0);
}
static_cast<KToggleAction *>(q->actionCollection()->action("toggleSpeedbar"))->setChecked(show);
// if we don't show the places panel, at least show the places menu
urlNavigator->setPlacesSelectorVisible(!show);
}
void KFileWidgetPrivate::_k_toggleBookmarks(bool show)
{
if (show)
{
if (bookmarkHandler)
{
return;
}
bookmarkHandler = new KFileBookmarkHandler( q );
q->connect( bookmarkHandler, SIGNAL(openUrl(QString)),
SLOT(_k_enterUrl(QString)));
bookmarkButton = new KActionMenu(KDE::icon("bookmarks"),i18n("Bookmarks"), q);
bookmarkButton->setDelayed(false);
q->actionCollection()->addAction("bookmark", bookmarkButton);
bookmarkButton->setMenu(bookmarkHandler->menu());
bookmarkButton->setWhatsThis(i18n("<qt>This button allows you to bookmark specific locations. "
"Click on this button to open the bookmark menu where you may add, "
"edit or select a bookmark.<br /><br />"
"These bookmarks are specific to the file dialog, but otherwise operate "
"like bookmarks elsewhere in KDE.</qt>"));
toolbar->addAction(bookmarkButton);
}
else if (bookmarkHandler)
{
delete bookmarkHandler;
bookmarkHandler = 0;
delete bookmarkButton;
bookmarkButton = 0;
}
static_cast<KToggleAction *>(q->actionCollection()->action("toggleBookmarks"))->setChecked( show );
}
// static, overloaded
QUrl KFileWidget::getStartUrl(const QUrl& startDir,
QString& recentDirClass)
{
QString fileName; // result discarded
return getStartUrl( startDir, recentDirClass, fileName );
}
// static, overloaded
QUrl KFileWidget::getStartUrl(const QUrl& startDir,
QString& recentDirClass,
QString& fileName)
{
recentDirClass.clear();
fileName.clear();
KUrl ret;
QUrlPathInfo startDirInfo(startDir);
bool useDefaultStartDir = startDir.isEmpty();
if ( !useDefaultStartDir )
{
if ( startDir.scheme() == "kfiledialog" )
{
// The startDir URL with this protocol may be in the format:
// directory() fileName()
// 1. kfiledialog:///keyword "/" keyword
// 2. kfiledialog:///keyword?global "/" keyword
// 3. kfiledialog:///keyword/ "/" keyword
// 4. kfiledialog:///keyword/?global "/" keyword
// 5. kfiledialog:///keyword/filename /keyword filename
// 6. kfiledialog:///keyword/filename?global /keyword filename
QString keyword;
QString urlDir = startDirInfo.directory();
QString urlFile = startDirInfo.fileName();
if ( urlDir == "/" ) // '1'..'4' above
{
keyword = urlFile;
fileName.clear();
}
else // '5' or '6' above
{
keyword = urlDir.mid( 1 );
fileName = urlFile;
}
if (startDir.encodedQuery() == "global")
recentDirClass = QString( "::%1" ).arg( keyword );
else
recentDirClass = QString( ":%1" ).arg( keyword );
ret = QUrl::fromLocalFile(KRecentDirs::dir(recentDirClass));
}
else // not special "kfiledialog" URL
{
// We can use startDir as the starting directory if either:
// (a) it has a directory part, or
// (b) there is a scheme (protocol), and it is not just "file".
if (!QUrlPathInfo(startDir).directory().isEmpty() ||
(!startDir.scheme().isEmpty() && !startDir.isLocalFile()))
{ // can use start directory
ret = startDir; // will be checked by stat later
// If we won't be able to list it (e.g. http), then use default
if ( !KProtocolManager::supportsListing( ret ) ) {
useDefaultStartDir = true;
fileName = startDirInfo.fileName();
}
}
else // file name only
{
fileName = startDirInfo.fileName();
useDefaultStartDir = true;
}
}
}
if ( useDefaultStartDir )
{
if (lastDirectory()->isEmpty()) {
lastDirectory()->setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
const QUrlPathInfo home(QUrl::fromLocalFile(QDir::homePath()));
// if there is no docpath set (== home dir), we prefer the current
// directory over it. We also prefer the homedir when our CWD is
// different from our homedirectory or when the document dir
// does not exist
if (QUrlPathInfo(*lastDirectory()).path(QUrlPathInfo::AppendTrailingSlash) == home.path(QUrlPathInfo::AppendTrailingSlash) ||
QDir::currentPath() != QDir::homePath() ||
!QDir(lastDirectory()->toLocalFile()).exists() )
*lastDirectory() = QUrl::fromLocalFile(QDir::currentPath());
}
ret = *lastDirectory();
}
kDebug(kfile_area) << "for" << startDir << "->" << ret << "recentDirClass" << recentDirClass << "fileName" << fileName;
return ret;
}
void KFileWidget::setStartDir(const QUrl& directory)
{
if ( directory.isValid() )
*lastDirectory() = directory;
}
void KFileWidgetPrivate::setNonExtSelection()
{
// Enhanced rename: Don't highlight the file extension.
QString filename = locationEditCurrentText();
QMimeDatabase db;
QString extension = db.suffixForFileName( filename );
if ( !extension.isEmpty() )
locationEdit->lineEdit()->setSelection( 0, filename.length() - extension.length() - 1 );
else
{
int lastDot = filename.lastIndexOf( '.' );
if ( lastDot > 0 )
locationEdit->lineEdit()->setSelection( 0, lastDot );
}
}
KToolBar * KFileWidget::toolBar() const
{
return d->toolbar;
}
void KFileWidget::setCustomWidget(QWidget* widget)
{
delete d->bottomCustomWidget;
d->bottomCustomWidget = widget;
// add it to the dialog, below the filter list box.
// Change the parent so that this widget is a child of the main widget
d->bottomCustomWidget->setParent( this );
d->vbox->addWidget( d->bottomCustomWidget );
//d->vbox->addSpacing(3); // can't do this every time...
// FIXME: This should adjust the tab orders so that the custom widget
// comes after the Cancel button. The code appears to do this, but the result
// somehow screws up the tab order of the file path combo box. Not a major
// problem, but ideally the tab order with a custom widget should be
// the same as the order without one.
setTabOrder(d->cancelButton, d->bottomCustomWidget);
setTabOrder(d->bottomCustomWidget, d->urlNavigator);
}
void KFileWidget::setCustomWidget(const QString& text, QWidget* widget)
{
delete d->labeledCustomWidget;
d->labeledCustomWidget = widget;
QLabel* label = new QLabel(text, this);
label->setAlignment(Qt::AlignRight);
d->lafBox->addWidget(label, 2, 0, Qt::AlignVCenter);
d->lafBox->addWidget(widget, 2, 1, Qt::AlignVCenter);
}
void KFileWidget::virtual_hook( int id, void* data )
{
// this is a workaround to avoid binary compatibility breakage
// since setConfirmOverwrite in kabstractfilewidget.h is a new function
// introduced for 4.2. As stated in kabstractfilewidget.h this workaround
// is going to become a virtual function for KDE5
switch (id) {
case 0: { // setConfirmOverwrite(bool)
bool *enable = static_cast<bool*>(data);
d->confirmOverwrite = *enable;
}
break;
case 1: { // setInlinePreviewShown(bool)
bool *show = static_cast<bool*>(data);
d->setInlinePreviewShown(*show);
}
break;
default:
break;
}
}
KDirOperator* KFileWidget::dirOperator()
{
return d->ops;
}
void KFileWidget::readConfig( KConfigGroup& group )
{
d->readConfig(group);
}
QString KFileWidgetPrivate::locationEditCurrentText() const
{
return QDir::fromNativeSeparators(locationEdit->currentText());
}
QUrl KFileWidgetPrivate::mostLocalUrl(const QUrl &url)
{
if (url.isLocalFile()) {
return url;
}
KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
bool res = KIO::NetAccess::synchronousRun(statJob, q);
if (!res) {
return url;
}
const QString path = statJob->statResult().stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
if (!path.isEmpty()) {
KUrl newUrl;
newUrl.setPath(path);
return newUrl;
}
return url;
}
void KFileWidgetPrivate::setInlinePreviewShown(bool show)
{
ops->setInlinePreviewShown(show);
}
#include "moc_kfilewidget.cpp"
diff --git a/khtml/dom/html_element.cpp b/khtml/dom/html_element.cpp
index ec9047b7cb..36cb3b82e6 100644
--- a/khtml/dom/html_element.cpp
+++ b/khtml/dom/html_element.cpp
@@ -1,201 +1,201 @@
/**
* This file is part of the DOM implementation for KDE.
*
* Copyright 1999 Lars Knoll (knoll@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.
*
*/
#include "dom/dom_exception.h"
#include "dom/html_misc.h"
#include "css/css_base.h"
#include "html/html_miscimpl.h" // HTMLCollectionImpl
using namespace DOM;
HTMLElement::HTMLElement() : Element()
{
}
HTMLElement::HTMLElement(const HTMLElement &other) : Element(other)
{
}
HTMLElement::HTMLElement(HTMLElementImpl *impl) : Element(impl)
{
}
HTMLElement &HTMLElement::operator = (const HTMLElement &other)
{
Element::operator = (other);
return *this;
}
HTMLElement &HTMLElement::operator = (const Node &other)
{
NodeImpl* ohandle = other.handle();
if (!ohandle || !ohandle->isHTMLElement()) {
if (impl) impl->deref();
impl = 0;
return *this;
}
Node::operator = (other);
return *this;
}
HTMLElement::~HTMLElement()
{
}
DOMString HTMLElement::id() const
{
if(!impl) return DOMString();
return ((ElementImpl *)impl)->getAttribute(ATTR_ID);
}
void HTMLElement::setId( const DOMString &value )
{
if(impl) ((ElementImpl *)impl)->setAttribute(ATTR_ID, value);
}
DOMString HTMLElement::title() const
{
if(!impl) return DOMString();
return ((ElementImpl *)impl)->getAttribute(ATTR_TITLE);
}
void HTMLElement::setTitle( const DOMString &value )
{
if(impl) ((ElementImpl *)impl)->setAttribute(ATTR_TITLE, value);
}
DOMString HTMLElement::lang() const
{
if(!impl) return DOMString();
return ((ElementImpl *)impl)->getAttribute(ATTR_LANG);
}
void HTMLElement::setLang( const DOMString &value )
{
if(impl) ((ElementImpl *)impl)->setAttribute(ATTR_LANG, value);
}
DOMString HTMLElement::dir() const
{
if(!impl) return DOMString();
return ((ElementImpl *)impl)->getAttribute(ATTR_DIR);
}
void HTMLElement::setDir( const DOMString &value )
{
if(impl) ((ElementImpl *)impl)->setAttribute(ATTR_DIR, value);
}
DOMString HTMLElement::className() const
{
if(!impl) return DOMString();
return ((ElementImpl *)impl)->getAttribute(ATTR_CLASS);
}
void HTMLElement::setClassName( const DOMString &value )
{
if(impl) ((ElementImpl *)impl)->setAttribute(ATTR_CLASS, value);
}
void HTMLElement::removeCSSProperty( const DOMString &property )
{
- int id = getPropertyID(property.string().toLower().toAscii().constData(), property.length());
+ int id = getPropertyID(property.string().toLower().toLatin1().constData(), property.length());
if(id && impl)
static_cast<HTMLElementImpl*>(impl)->removeCSSProperty(id);
}
void HTMLElement::addCSSProperty( const DOMString &property, const DOMString &value )
{
- int id = getPropertyID(property.string().toLower().toAscii().constData(), property.length());
+ int id = getPropertyID(property.string().toLower().toLatin1().constData(), property.length());
if(id && impl)
static_cast<HTMLElementImpl*>(impl)->addCSSProperty(id, value);
}
DOMString HTMLElement::innerHTML() const
{
if ( !impl ) return DOMString();
return ((HTMLElementImpl *)impl)->innerHTML();
}
void HTMLElement::setInnerHTML( const DOMString &html )
{
if( !impl )
return;
int exceptioncode = 0;
((HTMLElementImpl *)impl)->setInnerHTML( html, exceptioncode );
if ( exceptioncode )
throw DOMException( exceptioncode );
}
DOMString HTMLElement::innerText() const
{
if ( !impl ) return DOMString();
return ((HTMLElementImpl *)impl)->innerText();
}
void HTMLElement::setInnerText( const DOMString &text )
{
if( !impl )
return;
int exceptioncode = 0;
((HTMLElementImpl *)impl)->setInnerText( text, exceptioncode );
if ( exceptioncode )
throw DOMException( exceptioncode );
}
HTMLCollection HTMLElement::children() const
{
if(!impl) return HTMLCollection();
return HTMLCollection(impl, HTMLCollectionImpl::NODE_CHILDREN);
}
HTMLCollection HTMLElement::all() const
{
if(!impl) return HTMLCollection();
return HTMLCollection(impl, HTMLCollectionImpl::DOC_ALL /*it's called "doc" but it works from any node */);
}
void HTMLElement::assignOther( const Node &other, int elementId )
{
if (other.elementId() != static_cast<quint32>(elementId)) {
if ( impl ) impl->deref();
impl = 0;
} else {
Node::operator = (other);
}
}
bool HTMLElement::isContentEditable() const
{
if(!impl) return false;
return static_cast<HTMLElementImpl *>(impl)->isContentEditable();
}
DOMString HTMLElement::contentEditable() const {
if(!impl) return "inherit";
return static_cast<HTMLElementImpl *>(impl)->contentEditable();
}
void HTMLElement::setContentEditable(const DOMString &enabled) {
if(!impl)
throw DOMException(DOMException::INVALID_STATE_ERR);
static_cast<HTMLElementImpl *>(impl)->setContentEditable(enabled);
}
diff --git a/khtml/html/html_formimpl.cpp b/khtml/html/html_formimpl.cpp
index 396b95754f..37838a183a 100644
--- a/khtml/html/html_formimpl.cpp
+++ b/khtml/html/html_formimpl.cpp
@@ -1,3217 +1,3217 @@
/*
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* (C) 2004, 2005, 2006 Apple Computer, Inc.
*
* 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.
*
*/
#undef FORMS_DEBUG
//#define FORMS_DEBUG
#include "html/html_formimpl.h"
#include "khtmlview.h"
#include "khtml_part.h"
#include "html/html_documentimpl.h"
#include "khtml_settings.h"
#include "css/cssstyleselector.h"
#include "css/cssproperties.h"
#include "css/cssvalues.h"
#include "css/csshelper.h"
#include "xml/dom_textimpl.h"
#include "xml/dom_docimpl.h"
#include "xml/dom2_eventsimpl.h"
#include "xml/dom_restyler.h"
#include "khtml_ext.h"
#include "rendering/render_form.h"
#include <kcharsets.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <krandom.h>
#include <klocalizedstring.h>
#ifndef KHTML_NO_WALLET
#include <kwallet.h>
#endif
#include <netaccess.h>
#include <kfileitem.h>
#include <qmimedatabase.h>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QTextCodec>
// for keygen
#include <ksslkeygen.h>
#include <assert.h>
using namespace DOM;
using namespace khtml;
using namespace WTF;
HTMLFormElementImpl::HTMLFormElementImpl(DocumentImpl *doc, bool implicit)
: HTMLElementImpl(doc)
{
m_implicit = implicit;
m_post = false;
m_multipart = false;
m_autocomplete = true;
m_insubmit = false;
m_doingsubmit = false;
m_inreset = false;
m_enctype = "application/x-www-form-urlencoded";
m_boundary = "----------" + KRandom::randomString( 42 + 13 );
m_acceptcharset = "UNKNOWN";
m_malformed = false;
}
HTMLFormElementImpl::~HTMLFormElementImpl()
{
if (document() && document()->view() && document()->view()->part()) {
document()->view()->part()->dequeueWallet(this);
}
QListIterator<HTMLGenericFormElementImpl*> it(formElements);
while (it.hasNext())
it.next()->m_form = 0;
QListIterator<HTMLImageElementImpl*> it2(imgElements);
while (it2.hasNext())
it2.next()->m_form = 0;
}
NodeImpl::Id HTMLFormElementImpl::id() const
{
return ID_FORM;
}
HTMLCollectionImpl* HTMLFormElementImpl::elements()
{
return new HTMLFormCollectionImpl(this);
}
DOMString HTMLFormElementImpl::target() const
{
return getAttribute(ATTR_TARGET);
}
DOMString HTMLFormElementImpl::action() const
{
return getAttribute(ATTR_ACTION);
}
long HTMLFormElementImpl::length() const
{
int len = 0;
QListIterator<HTMLGenericFormElementImpl*> it(formElements);
while (it.hasNext())
if (it.next()->isEnumerable())
++len;
return len;
}
static QByteArray encodeCString(const QByteArray& e)
{
// http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
// safe characters like NS handles them for compatibility
static const char safe[] = "-._*";
QByteArray encoded(( e.length()+e.count( '\n' ) )*3
+e.count('\r') * 3 + 1, 0);
int enclen = 0;
bool crmissing = false;
unsigned char oldc;
unsigned char c ='\0';
//QCString orig(e.data(), e.size());
unsigned len = e.length();
for(unsigned pos = 0; pos < len; pos++) {
oldc = c;
c = e[pos];
if (crmissing && c != '\n') {
encoded[enclen++] = '%';
encoded[enclen++] = '0';
encoded[enclen++] = 'D';
crmissing = false;
}
if ( (( c >= 'A') && ( c <= 'Z')) ||
(( c >= 'a') && ( c <= 'z')) ||
(( c >= '0') && ( c <= '9')) ||
(strchr(safe, c))
)
encoded[enclen++] = c;
else if ( c == ' ' )
encoded[enclen++] = '+';
else if ( c == '\n' )
{
encoded[enclen++] = '%';
encoded[enclen++] = '0';
encoded[enclen++] = 'D';
encoded[enclen++] = '%';
encoded[enclen++] = '0';
encoded[enclen++] = 'A';
crmissing = false;
}
else if (c == '\r' && oldc != '\n') {
crmissing = true;
}
else if ( c != '\r' )
{
encoded[enclen++] = '%';
unsigned int h = c / 16;
h += (h > 9) ? ('A' - 10) : '0';
encoded[enclen++] = h;
unsigned int l = c % 16;
l += (l > 9) ? ('A' - 10) : '0';
encoded[enclen++] = l;
}
}
encoded.truncate(enclen);
return encoded;
}
inline static QString ampersandEscape(unsigned val)
{
QString out;
out.sprintf("&#%u;", val);
return out;
}
inline static QString escapeUnencodeable(const QTextCodec* codec, const QString& s) {
QString encString;
const int len = s.length();
for (int i = 0; i < len; ++i) {
QChar c = s[i];
// We need to hand surrogate pairs to the codec at once;
// also do validity checking on those
if (c.isLowSurrogate()) {
c = QChar::ReplacementCharacter;
} else if (c.isHighSurrogate()) {
if ((i + 1) < len && s[i + 1].isLowSurrogate()) {
// A valid SP..
++i;
QString pair = QString(c) + s[i];
if (codec->canEncode(pair))
encString += pair;
else
encString += ampersandEscape(QChar::surrogateToUcs4(c, s[i]));
continue;
} else {
c = QChar::ReplacementCharacter;
}
}
// Normal characters.
if (codec->canEncode(c))
encString.append(c);
else
encString.append(ampersandEscape(c.unicode()));
}
return encString;
}
inline static QByteArray fixUpfromUnicode(const QTextCodec* codec, const QString& s)
{
QByteArray str = codec->fromUnicode(escapeUnencodeable(codec,s));
str.truncate(str.length());
return str;
}
Vector<HTMLGenericFormElementImpl*> HTMLFormElementImpl::gatherInTreeOrder(NodeImpl* root,
const HashSet<NodeImpl*>& toGather)
{
Vector<HTMLGenericFormElementImpl*> out;
out.reserveCapacity(toGather.size());
for (NodeImpl* cur = root; cur; cur = cur->traverseNextNode(root)) {
if (toGather.contains(cur))
out.append(static_cast<HTMLGenericFormElementImpl*>(cur));
}
return out;
}
QByteArray HTMLFormElementImpl::formData(bool& ok)
{
#ifdef FORMS_DEBUG
kDebug( 6030 ) << "form: formData()";
#endif
QByteArray form_data;
form_data.resize(0);
QByteArray enc_string = ""; // used for non-multipart data
bool useMultipart = m_multipart && m_post; // as multipart is ignored for GET
// find out the QTextcodec to use
QString str = m_acceptcharset.string();
const QChar space(' ');
const unsigned int strLength = str.length();
for(unsigned int i=0; i < strLength; ++i) if(str[i].toLatin1() == ',') str[i] = space;
const QStringList charsets = str.split(' ');
QTextCodec* codec = 0;
KHTMLView *view = document()->view();
{
QStringList::ConstIterator it = charsets.begin();
const QStringList::ConstIterator itEnd = charsets.end();
for ( ; it != itEnd; ++it )
{
QString enc = (*it);
if(enc.contains("UNKNOWN"))
{
// use standard document encoding
enc = "ISO 8859-1";
if(view && view->part())
enc = view->part()->encoding();
}
if((codec = KCharsets::charsets()->codecForName(enc.toLatin1().constData())))
break;
}
}
if(!codec)
codec = QTextCodec::codecForLocale();
// we need to map visual hebrew to logical hebrew, as the web
// server alsways expects responses in logical ordering
if ( codec->mibEnum() == 11 )
codec = QTextCodec::codecForMib( 85 );
m_encCharset = codec->name();
const unsigned int m_encCharsetLength = m_encCharset.length();
for(unsigned int i=0; i < m_encCharsetLength; ++i)
m_encCharset[i] = m_encCharset[i].toLatin1() == ' ' ? QChar('-') : m_encCharset[i].toLower();
QStringList fileUploads, fileNotUploads;
/**
Frameworks such as mootools Sortables expect form element values to be submitted in
tree order (and HTML5 specifies this behavior); however formElements need not be
ordered thus. Hence we walk through the tree and the formElements as we go ---
first in our kids, then if needed in the parent
*/
HashSet<NodeImpl*> formElementsSet;
foreach (HTMLGenericFormElementImpl* fe, formElements)
formElementsSet.add(fe);
Vector<HTMLGenericFormElementImpl*> ordered = gatherInTreeOrder(this, formElementsSet);
if (ordered.size() < (unsigned)formElements.size()) {
// Some of our elements not contained within us due to parsing hijinks -- scan
// the entire document.
ordered = gatherInTreeOrder(document()->documentElement(), formElementsSet);
}
for (unsigned i = 0; i < ordered.size(); ++i) {
HTMLGenericFormElementImpl* const current = ordered[i];
khtml::encodingList lst;
if (!current->disabled() && current->encoding(codec, lst, useMultipart))
{
//kDebug(6030) << "adding name '" << current->name().string() << "'";
khtml::encodingList::ConstIterator it = lst.constBegin();
const khtml::encodingList::ConstIterator itEnd = lst.constEnd();
for( ; it != itEnd; ++it )
{
if (!useMultipart)
{
// handle ISINDEX / <input name=isindex> special
// but only if its the first entry
if ( enc_string.isEmpty() && *it == "isindex" ) {
++it;
enc_string += encodeCString( *it );
}
else {
if(!enc_string.isEmpty())
enc_string += '&';
enc_string += encodeCString(*it);
enc_string += '=';
++it;
enc_string += encodeCString(*it);
}
}
else
{
QByteArray hstr("--");
hstr += m_boundary.toLatin1();
hstr += "\r\n";
hstr += "Content-Disposition: form-data; name=\"";
hstr += (*it).data();
hstr += "\"";
// if the current type is FILE, then we also need to
// include the filename
if (current->id() == ID_INPUT &&
static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::FILE)
{
KUrl path;
QString val = static_cast<HTMLInputElementImpl*>(current)->value().string().trimmed();
if (!val.isEmpty() &&
QDir::isRelativePath(val) &&
QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val)) {
path.setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val);
} else {
path = KUrl(val);
}
hstr += fixUpfromUnicode(codec, "; filename=\"" + path.fileName() + "\"");
if (path.isValid()) {
fileUploads << path.pathOrUrl();
QMimeDatabase db;
const QMimeType mime = db.mimeTypeForUrl(path);
if (!mime.name().isEmpty()) {
hstr += "\r\nContent-Type: ";
hstr += mime.name().toLatin1().constData();
}
} else if (!val.isEmpty()) {
fileNotUploads << path.pathOrUrl();
}
}
hstr += "\r\n\r\n";
++it;
// append body
form_data.append(hstr);
form_data.append(*it);
form_data.append("\r\n");
// reset unsubmittedFormChange flag
if (current->id() == ID_INPUT &&
static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::TEXT)
static_cast<HTMLInputElementImpl*>(current)->setUnsubmittedFormChange(false);
if (current->id() == ID_TEXTAREA)
static_cast<HTMLTextAreaElementImpl*>(current)->setUnsubmittedFormChange(false);
}
}
}
}
if (fileNotUploads.count()) {
const int result = KMessageBox::warningContinueCancelList( 0,
i18n("The following files will not be uploaded"
" because they could not be found.\n"
"Do you want to continue?"),
fileNotUploads,
i18n("Submit Confirmation"),KGuiItem(i18n("&Submit Anyway"), "dialog-ok"));
if (result == KMessageBox::Cancel) {
ok = false;
return QByteArray();
}
}
if (fileUploads.count()) {
const int result = KMessageBox::warningContinueCancelList( 0,
i18n("You are about to transfer the following files from "
"your local computer to the Internet.\n"
"Do you really want to continue?"),
fileUploads,
i18n("Send Confirmation"),KGuiItem(i18np("&Send File", "&Send Files", fileUploads.count()), "document-export"));
if (result == KMessageBox::Cancel) {
ok = false;
return QByteArray();
}
}
if (useMultipart)
- enc_string = QString("--" + m_boundary + "--\r\n").toAscii().constData();
+ enc_string = QString("--" + m_boundary + "--\r\n").toLatin1().constData();
const int old_size = form_data.size();
form_data.resize( form_data.size() + enc_string.length() );
memcpy(form_data.data() + old_size, enc_string.data(), enc_string.length() );
ok = true;
return form_data;
}
void HTMLFormElementImpl::setEnctype( const DOMString& type )
{
if(type.string().indexOf("multipart", 0, Qt::CaseInsensitive) != -1 || type.string().indexOf("form-data", 0, Qt::CaseInsensitive) != -1)
{
m_enctype = "multipart/form-data";
m_multipart = true;
} else if (type.string().indexOf("text", 0, Qt::CaseInsensitive) != -1 || type.string().indexOf("plain", 0, Qt::CaseInsensitive) != -1)
{
m_enctype = "text/plain";
m_multipart = false;
}
else
{
m_enctype = "application/x-www-form-urlencoded";
m_multipart = false;
}
m_encCharset.clear();
}
static QString calculateAutoFillKey(const HTMLFormElementImpl& e)
{
KUrl k(e.document()->URL());
k.setRef(QString());
k.setQuery(QString());
// ensure that we have the user / password inside the url
// otherwise we might have a potential security problem
// by saving passwords under wrong lookup key.
const QString name = e.getAttribute(ATTR_NAME).string().trimmed();
const QRegExp re("[;,!]");
const QStringList url = k.url().split(re);
return url[0] + '#' + name;
}
void HTMLFormElementImpl::doAutoFill()
{
#ifndef KHTML_NO_WALLET
const QString key = calculateAutoFillKey(*this);
if (KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
KWallet::Wallet::FormDataFolder(),
key))
return;
// assert(view())
document()->view()->part()->openWallet(this);
#endif // KHTML_NO_WALLET
}
void HTMLFormElementImpl::walletOpened(KWallet::Wallet *w) {
#ifndef KHTML_NO_WALLET
assert(w);
const QString key = calculateAutoFillKey(*this);
if (!w->hasFolder(KWallet::Wallet::FormDataFolder())) {
return; // failed
}
w->setFolder(KWallet::Wallet::FormDataFolder());
QMap<QString, QString> map;
if (w->readMap(key, map))
return; // failed, abort
if(document()->view())
{
document()->view()->part()->addWalletFormKey(key);
}
for (QListIterator<HTMLGenericFormElementImpl*> it(formElements); it.hasNext();) {
HTMLGenericFormElementImpl* const cur = it.next();
if (cur->id() == ID_INPUT) {
HTMLInputElementImpl* const current = static_cast<HTMLInputElementImpl*>(cur);
if ((current->inputType() == HTMLInputElementImpl::PASSWORD ||
current->inputType() == HTMLInputElementImpl::TEXT) &&
!current->readOnly() &&
map.contains(current->name().string())) {
document()->setFocusNode(current);
current->setValue(map[current->name().string()]);
}
}
}
#endif // KHTML_NO_WALLET
}
void HTMLFormElementImpl::submitFromKeyboard()
{
// Activate the first nondisabled submit button
// if there is none, do a submit anyway if not more
// than one <input type=text> or <input type=password>
unsigned int inputtext = 0;
for (QListIterator<HTMLGenericFormElementImpl*> it(formElements); it.hasNext();) {
HTMLGenericFormElementImpl* const cur = it.next();
if (cur->id() == ID_BUTTON) {
HTMLButtonElementImpl* const current = static_cast<HTMLButtonElementImpl *>(cur);
if (current->buttonType() == HTMLButtonElementImpl::SUBMIT && !current->disabled()) {
current->click();
return;
}
} else if (cur->id() == ID_INPUT) {
HTMLInputElementImpl* const current = static_cast<HTMLInputElementImpl *>(cur);
switch(current->inputType()) {
case HTMLInputElementImpl::SUBMIT:
case HTMLInputElementImpl::IMAGE:
if(!current->disabled()) {
current->click();
return;
}
break;
case HTMLInputElementImpl::TEXT:
case HTMLInputElementImpl::PASSWORD:
++inputtext;
default:
break;
}
}
}
if (inputtext <= 1)
prepareSubmit();
}
void HTMLFormElementImpl::gatherWalletData()
{
#ifndef KHTML_NO_WALLET
KHTMLView* const view = document()->view();
// check if we have any password input's
m_walletMap.clear();
m_havePassword = false;
m_haveTextarea = false;
const KUrl formUrl(document()->URL());
if (view && !view->nonPasswordStorableSite(formUrl.host())) {
for (QListIterator<HTMLGenericFormElementImpl*> it(formElements); it.hasNext();) {
HTMLGenericFormElementImpl* const cur = it.next();
if (cur->id() == ID_INPUT) {
HTMLInputElementImpl* const c = static_cast<HTMLInputElementImpl*> (cur);
if ((c->inputType() == HTMLInputElementImpl::TEXT ||
c->inputType() == HTMLInputElementImpl::PASSWORD) &&
!c->readOnly()) {
m_walletMap.insert(c->name().string(), c->value().string());
if (c->inputType() == HTMLInputElementImpl::PASSWORD &&
!c->value().isEmpty())
m_havePassword = true;
}
}
else if (cur->id() == ID_TEXTAREA)
m_haveTextarea = true;
}
}
#endif // KHTML_NO_WALLET
}
bool HTMLFormElementImpl::prepareSubmit()
{
KHTMLView* const view = document()->view();
if(m_insubmit || !view || !view->part() || view->part()->onlyLocalReferences())
return m_insubmit;
gatherWalletData();
m_insubmit = true;
m_doingsubmit = false;
if ( dispatchHTMLEvent(EventImpl::SUBMIT_EVENT,true,true) && !m_doingsubmit )
m_doingsubmit = true;
m_insubmit = false;
if ( m_doingsubmit )
submit();
return m_doingsubmit;
}
void HTMLFormElementImpl::submit( )
{
if ( m_insubmit ) {
m_doingsubmit = true;
return;
}
m_insubmit = true;
#ifdef FORMS_DEBUG
kDebug( 6030 ) << "submitting!";
#endif
bool ok;
KHTMLView* const view = document()->view();
const QByteArray form_data = formData(ok);
const KUrl formUrl(document()->URL());
if (ok && view) {
if (m_walletMap.isEmpty()) {
gatherWalletData();
}
#ifndef KHTML_NO_WALLET
if (m_havePassword && !m_haveTextarea && KWallet::Wallet::isEnabled()) {
const QString key = calculateAutoFillKey(*this);
const bool doesnotexist = KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::FormDataFolder(), key);
KWallet::Wallet* const w = view->part()->wallet();
bool login_changed = false;
if (!doesnotexist && w) {
// check if the login information changed from what
// we had so far.
if (w->hasFolder(KWallet::Wallet::FormDataFolder())) {
w->setFolder(KWallet::Wallet::FormDataFolder());
QMap<QString, QString> map;
if (!w->readMap(key, map)) {
QMap<QString, QString>::const_iterator it = map.constBegin();
const QMap<QString, QString>::const_iterator itEnd = map.constEnd();
for ( ; it != itEnd; ++it )
if ( map[it.key()] != m_walletMap[it.key()] ) {
login_changed = true;
break;
}
} else {
login_changed = true;
}
}
}
if ( doesnotexist || !w || login_changed ) {
if (view->part())
view->part()->saveLoginInformation(formUrl.host(), key, m_walletMap);
}
}
#endif // KHTML_NO_WALLET
DOMString value = getAttribute(ATTR_ACTION);
DOMString url = khtml::parseURL(value);
// ignore base url if 'action' attribute is empty.
if (value.isEmpty())
url = formUrl.url();
if(m_post) {
view->part()->submitForm( "post", url.string(), form_data,
m_target.string(),
enctype().string(),
m_boundary );
}
else {
view->part()->submitForm( "get", url.string(), form_data,
m_target.string() );
}
}
m_walletMap.clear(); // done with it
m_havePassword = m_haveTextarea= false;
m_doingsubmit = m_insubmit = false;
}
void HTMLFormElementImpl::reset( )
{
KHTMLView* const view = document()->view();
if(m_inreset || !view || !view->part()) return;
m_inreset = true;
#ifdef FORMS_DEBUG
kDebug( 6030 ) << "reset pressed!";
#endif
// ### DOM2 labels this event as not cancelable, however
// common browsers( sick! ) allow it be canceled.
if ( !dispatchHTMLEvent(EventImpl::RESET_EVENT,true, true) ) {
m_inreset = false;
return;
}
for (QListIterator<HTMLGenericFormElementImpl*> it(formElements); it.hasNext();)
it.next()->reset();
m_inreset = false;
}
void HTMLFormElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_ACTION:
break;
case ATTR_TARGET:
m_target = attr->value();
break;
case ATTR_METHOD:
m_post = ( strcasecmp( attr->value(), "post" ) == 0 );
break;
case ATTR_ENCTYPE:
setEnctype( attr->value() );
break;
case ATTR_ACCEPT_CHARSET:
// space separated list of charsets the server
// accepts - see rfc2045
m_acceptcharset = attr->value();
break;
case ATTR_ACCEPT:
// ignore this one for the moment...
break;
case ATTR_AUTOCOMPLETE:
m_autocomplete = strcasecmp( attr->value(), "off" );
break;
case ATTR_ONSUBMIT:
setHTMLEventListener(EventImpl::SUBMIT_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onsubmit", this));
break;
case ATTR_ONRESET:
setHTMLEventListener(EventImpl::RESET_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onreset", this));
break;
case ATTR_NAME:
if (inDocument() && m_name != attr->value()) {
document()->underDocNamedCache().remove(m_name, this);
document()->underDocNamedCache().add (attr->value(), this);
}
m_name = attr->value();
//Fallthrough intentional
default:
HTMLElementImpl::parseAttribute(attr);
}
}
void HTMLFormElementImpl::removedFromDocument()
{
document()->underDocNamedCache().remove(m_name, this);
HTMLElementImpl::removedFromDocument();
}
void HTMLFormElementImpl::insertedIntoDocument()
{
document()->underDocNamedCache().add(m_name, this);
HTMLElementImpl::insertedIntoDocument();
}
void HTMLFormElementImpl::removeId(const DOMString& id)
{
document()->underDocNamedCache().remove(id, this);
HTMLElementImpl::removeId(id);
}
void HTMLFormElementImpl::addId (const DOMString& id)
{
document()->underDocNamedCache().add(id, this);
HTMLElementImpl::addId(id);
}
void HTMLFormElementImpl::uncheckOtherRadioButtonsInGroup( HTMLGenericFormElementImpl *caller, bool setDefaultChecked )
{
for (QListIterator<HTMLGenericFormElementImpl*> it(formElements); it.hasNext();) {
HTMLGenericFormElementImpl* const current = it.next();
if (current->id() == ID_INPUT &&
static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::RADIO &&
current != caller && current->form() == caller->form() && current->name() == caller->name())
static_cast<HTMLInputElementImpl*>(current)->setChecked(false, setDefaultChecked);
}
}
void HTMLFormElementImpl::registerFormElement(HTMLGenericFormElementImpl *e)
{
formElements.append(e);
}
void HTMLFormElementImpl::removeFormElement(HTMLGenericFormElementImpl *e)
{
int i = formElements.indexOf(e);
if (i != -1)
formElements.removeAt(i);
if (e->hasPastNames()) {
QMutableHashIterator<DOMString, HTMLGenericFormElementImpl*> it(m_pastNamesMap);
while (it.hasNext()) {
it.next();
if (it.value() == e)
it.remove();
}
}
}
void HTMLFormElementImpl::registerImgElement(HTMLImageElementImpl *e)
{
imgElements.append(e);
}
void HTMLFormElementImpl::removeImgElement(HTMLImageElementImpl *e)
{
int i = imgElements.indexOf(e);
if (i != -1)
imgElements.removeAt(i);
}
HTMLGenericFormElementImpl* HTMLFormElementImpl::lookupByPastName(const DOMString& id)
{
return m_pastNamesMap.value(id);
}
void HTMLFormElementImpl::bindPastName(HTMLGenericFormElementImpl* e)
{
DOMString id = e->getAttribute(ATTR_ID);
if (!id.isEmpty())
m_pastNamesMap.insert(id, e);
DOMString nm = e->getAttribute(ATTR_NAME);
if (!nm.isEmpty())
m_pastNamesMap.insert(nm, e);
e->setHasPastNames();
}
// -------------------------------------------------------------------------
HTMLGenericFormElementImpl::HTMLGenericFormElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLElementImpl(doc)
{
m_disabled = m_readOnly = m_hasPastNames = false;
m_name = 0;
if (f)
m_form = f;
else
m_form = getForm();
if (m_form)
m_form->registerFormElement(this);
}
void HTMLGenericFormElementImpl::insertedIntoDocument()
{
HTMLElementImpl::insertedIntoDocument();
if (!m_form) {
HTMLFormElementImpl* const newform = getForm();
if (newform) {
m_form = newform;
m_form->registerFormElement(this);
}
}
}
void HTMLGenericFormElementImpl::removedFromDocument()
{
HTMLElementImpl::removedFromDocument();
if (m_form)
m_form->removeFormElement(this);
m_form = 0;
}
HTMLGenericFormElementImpl::~HTMLGenericFormElementImpl()
{
if (m_form)
m_form->removeFormElement(this);
if (m_name) m_name->deref();
}
void HTMLGenericFormElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_DISABLED:
setDisabled( attr->val() != 0 );
break;
case ATTR_READONLY:
{
const bool m_oldreadOnly = m_readOnly;
m_readOnly = attr->val() != 0;
if (m_oldreadOnly != m_readOnly) setChanged();
break;
}
default:
HTMLElementImpl::parseAttribute(attr);
}
}
void HTMLGenericFormElementImpl::attach()
{
assert(!attached());
if (m_render) {
assert(m_render->style());
parentNode()->renderer()->addChild(m_render, nextRenderer());
}
// FIXME: This handles the case of a new form element being created by
// JavaScript and inserted inside a form. What it does not handle is
// a form element being moved from inside a form to outside, or from one
// inside one form to another. The reason this other case is hard to fix
// is that during parsing, we may have been passed a form that we are not
// inside, DOM-tree-wise. If so, it's hard for us to know when we should
// be removed from that form's element list.
if (!m_form) {
m_form = getForm();
if (m_form)
m_form->registerFormElement(this);
}
NodeBaseImpl::attach();
// The call to updateFromElement() needs to go after the call through
// to the base class's attach() because that can sometimes do a close
// on the renderer.
if (m_render)
m_render->updateFromElement();
}
HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const
{
NodeImpl *p = parentNode();
while(p)
{
if( p->id() == ID_FORM )
return static_cast<HTMLFormElementImpl *>(p);
if( p->parentNode() && p->parentNode()->id() == ID_TABLE && p->previousSibling() )
{
p = p->previousSibling();
continue;
}
p = p->parentNode();
}
#ifdef FORMS_DEBUG
kDebug( 6030 ) << "couldn't find form!";
kDebug( 6030 ) << kBacktrace();
#endif
return 0;
}
DOMString HTMLGenericFormElementImpl::name() const
{
if (m_name) return m_name;
// ###
// DOMString n = document()->htmlMode() != DocumentImpl::XHtml ?
// getAttribute(ATTR_NAME) : getAttribute(ATTR_ID);
const DOMString n = getAttribute(ATTR_NAME);
if (n.isNull())
return new DOMStringImpl("");
return n;
}
void HTMLGenericFormElementImpl::setName(const DOMString& name)
{
if (m_name) m_name->deref();
m_name = name.implementation();
setAttribute( ATTR_NAME, name );
if (m_name) m_name->ref();
}
void HTMLGenericFormElementImpl::onSelect()
{
// ### make this work with new form events architecture
dispatchHTMLEvent(EventImpl::SELECT_EVENT,true,false);
}
void HTMLGenericFormElementImpl::onChange()
{
// ### make this work with new form events architecture
dispatchHTMLEvent(EventImpl::CHANGE_EVENT,true,false);
}
void HTMLGenericFormElementImpl::setDisabled( bool _disabled )
{
if ( m_disabled != _disabled ) {
m_disabled = _disabled;
// Trigger dynamic restyles
document()->dynamicDomRestyler().restyleDependent(this, OtherStateDependency);
// We need to update rendering under all circumstances
if (!changed() && m_render) {
m_render->updateFromElement();
}
}
}
bool HTMLGenericFormElementImpl::isFocusableImpl(FocusType ft) const
{
if (hasTabIndex())
return HTMLElementImpl::isFocusableImpl(ft);
if (disabled())
return false;
//Non-widget INPUT TYPE="image" and <BUTTON> support focus, too.
if (id() == ID_INPUT && static_cast<const HTMLInputElementImpl *>(this)->inputType() == HTMLInputElementImpl::IMAGE)
return true;
if (id() == ID_BUTTON)
return true;
if (!m_render || !m_render->isWidget())
return false;
QWidget* widget = static_cast<RenderWidget*>(m_render)->widget();
return widget && widget->focusPolicy() >= Qt::TabFocus;
}
class FocusHandleWidget : public QWidget
{
public:
void focusNextPrev(bool n) {
if (!focusNextPrevChild(n) && inherits("QTextEdit"))
QWidget::focusNextPrevChild(n);
}
};
void HTMLGenericFormElementImpl::defaultEventHandler(EventImpl *evt)
{
if (evt->target() == this && renderer() && renderer()->isWidget()) {
switch(evt->id()) {
case EventImpl::MOUSEDOWN_EVENT:
case EventImpl::MOUSEUP_EVENT:
case EventImpl::MOUSEMOVE_EVENT:
case EventImpl::MOUSEOUT_EVENT:
case EventImpl::MOUSEOVER_EVENT:
case EventImpl::KHTML_MOUSEWHEEL_EVENT:
case EventImpl::KEYDOWN_EVENT:
case EventImpl::KEYUP_EVENT:
case EventImpl::KEYPRESS_EVENT:
case EventImpl::DOMFOCUSIN_EVENT:
case EventImpl::DOMFOCUSOUT_EVENT:
if (static_cast<RenderWidget*>(renderer())->handleEvent(*evt))
evt->setDefaultHandled();
default:
break;
}
}
if (evt->target()==this && !m_disabled)
{
// Report focus in/out changes to the browser extension (editable widgets only)
KHTMLView* const view = document()->view();
if (view && evt->id() == EventImpl::DOMFOCUSIN_EVENT && isEditable() && m_render && m_render->isWidget()) {
KHTMLPartBrowserExtension *ext = static_cast<KHTMLPartBrowserExtension *>(view->part()->browserExtension());
QWidget* const widget = static_cast<RenderWidget*>(m_render)->widget();
if (ext)
ext->editableWidgetFocused(widget);
}
if (evt->id()==EventImpl::MOUSEDOWN_EVENT || evt->id()==EventImpl::KEYDOWN_EVENT)
{
setActive();
if (renderer() && renderer()->isWidget())
static_cast<RenderWidget*>(renderer())->widget()->setFocus(); // ### mmh..
}
else if (evt->id() == EventImpl::MOUSEUP_EVENT || evt->id()==EventImpl::KEYUP_EVENT)
{
if (m_active)
{
setActive(false);
setFocus();
}
else {
setActive(false);
}
}
if (!evt->defaultHandled() && m_render && m_render->isWidget()) {
// handle tabbing out, either from a single or repeated key event.
if ( evt->id() == EventImpl::KEYPRESS_EVENT && evt->isKeyRelatedEvent() ) {
QKeyEvent* const k = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
if ( k && (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) &&
(k->modifiers() & Qt::ControlModifier) == 0 ) {
QWidget* const widget = static_cast<RenderWidget*>(m_render)->widget();
if (widget)
{
static_cast<FocusHandleWidget *>(widget)
->focusNextPrev(k->key() == Qt::Key_Tab);
}
evt->setDefaultHandled();
}
}
}
if (view && evt->id() == EventImpl::DOMFOCUSOUT_EVENT && isEditable() && m_render && m_render->isWidget()) {
KHTMLPartBrowserExtension* const ext = static_cast<KHTMLPartBrowserExtension *>(view->part()->browserExtension());
QWidget* const widget = static_cast<RenderWidget*>(m_render)->widget();
if (ext)
ext->editableWidgetBlurred(widget);
// ### Don't count popup as a valid reason for losing the focus (example: opening the options of a select
// combobox shouldn't emit onblur)
}
}
if (evt->target() == this && evt->isMouseEvent() && evt->id() != EventImpl::KHTML_MOUSEWHEEL_EVENT && renderer())
evt->setDefaultHandled();
HTMLElementImpl::defaultEventHandler(evt);
}
bool HTMLGenericFormElementImpl::isEditable()
{
return false;
}
// -------------------------------------------------------------------------
HTMLButtonElementImpl::HTMLButtonElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
m_clicked = false;
m_type = SUBMIT;
m_dirty = true;
m_activeSubmit = false;
}
HTMLButtonElementImpl::~HTMLButtonElementImpl()
{
}
NodeImpl::Id HTMLButtonElementImpl::id() const
{
return ID_BUTTON;
}
DOMString HTMLButtonElementImpl::type() const
{
switch (m_type) {
case SUBMIT:
return "submit";
case RESET:
return "reset";
case BUTTON:
return "button";
}
return "";
}
void HTMLButtonElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_TYPE:
// Per WF2.0, any invalid values are to be ignored -- and hence
// handled as the default, SUBMIT.
m_type = SUBMIT;
if ( strcasecmp( attr->value(), "reset" ) == 0 )
m_type = RESET;
else if ( strcasecmp( attr->value(), "button" ) == 0 )
m_type = BUTTON;
break;
case ATTR_VALUE:
m_value = attr->value();
m_currValue = m_value.string();
break;
case ATTR_ACCESSKEY:
break;
case ATTR_ALIGN:
break;
default:
HTMLGenericFormElementImpl::parseAttribute(attr);
}
}
void HTMLButtonElementImpl::defaultEventHandler(EventImpl *evt)
{
if (m_type != BUTTON && !m_disabled) {
bool act = (evt->id() == EventImpl::DOMACTIVATE_EVENT);
if (!act && evt->id()==EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) {
QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space))
act = true;
}
if (act)
activate();
}
HTMLGenericFormElementImpl::defaultEventHandler(evt);
}
void HTMLButtonElementImpl::activate()
{
m_clicked = true;
if(m_form && m_type == SUBMIT) {
m_activeSubmit = true;
m_form->prepareSubmit();
m_activeSubmit = false; // in case we were canceled
}
if(m_form && m_type == RESET)
m_form->reset();
}
void HTMLButtonElementImpl::click()
{
QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0,0), Qt::LeftButton, Qt::LeftButton, 0);
dispatchMouseEvent(&me,EventImpl::CLICK_EVENT, 1);
}
bool HTMLButtonElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoding, bool /*multipart*/)
{
if (m_type != SUBMIT || name().isEmpty() || !m_activeSubmit)
return false;
encoding += fixUpfromUnicode(codec, name().string());
const QString enc_str = m_currValue.isNull() ? QString("") : m_currValue;
encoding += fixUpfromUnicode(codec, enc_str);
return true;
}
void HTMLButtonElementImpl::attach()
{
// skip the generic handler
HTMLElementImpl::attach();
}
// -------------------------------------------------------------------------
HTMLFieldSetElementImpl::HTMLFieldSetElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
}
HTMLFieldSetElementImpl::~HTMLFieldSetElementImpl()
{
}
NodeImpl::Id HTMLFieldSetElementImpl::id() const
{
return ID_FIELDSET;
}
void HTMLFieldSetElementImpl::attach()
{
assert(!attached());
assert(!m_render);
assert(parentNode());
RenderStyle* const _style = document()->styleSelector()->styleForElement(this);
_style->ref();
if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
_style->display() != NONE) {
m_render = new (document()->renderArena()) RenderFieldset(this);
m_render->setStyle(_style);
}
HTMLGenericFormElementImpl::attach();
_style->deref();
}
void HTMLFieldSetElementImpl::parseAttribute(AttributeImpl *attr)
{
HTMLElementImpl::parseAttribute(attr);
}
// -------------------------------------------------------------------------
HTMLInputElementImpl::HTMLInputElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
m_type = TEXT;
m_maxLen = -1;
m_size = 20;
m_clicked = false;
m_checked = false;
m_defaultChecked = false;
m_useDefaultChecked = true;
m_indeterminate = false;
m_haveType = false;
m_activeSubmit = false;
m_autocomplete = true;
m_inited = false;
m_unsubmittedFormChange = false;
xPos = 0;
yPos = 0;
if ( m_form )
m_autocomplete = f->autoComplete();
}
HTMLInputElementImpl::~HTMLInputElementImpl()
{
if (document()) document()->deregisterMaintainsState(this);
}
NodeImpl::Id HTMLInputElementImpl::id() const
{
return ID_INPUT;
}
// Called from JS. Can't merge with parseType since we
// also need to actually set ATTR_TYPE, which can't be done there.
void HTMLInputElementImpl::setType(const DOMString& t)
{
setAttribute(ATTR_TYPE, t);
}
void HTMLInputElementImpl::parseType(const DOMString& t)
{
typeEnum newType;
if ( strcasecmp( t, "password" ) == 0 )
newType = PASSWORD;
else if ( strcasecmp( t, "checkbox" ) == 0 )
newType = CHECKBOX;
else if ( strcasecmp( t, "radio" ) == 0 )
newType = RADIO;
else if ( strcasecmp( t, "submit" ) == 0 )
newType = SUBMIT;
else if ( strcasecmp( t, "reset" ) == 0 )
newType = RESET;
else if ( strcasecmp( t, "file" ) == 0 )
newType = FILE;
else if ( strcasecmp( t, "hidden" ) == 0 )
newType = HIDDEN;
else if ( strcasecmp( t, "image" ) == 0 )
newType = IMAGE;
else if ( strcasecmp( t, "button" ) == 0 )
newType = BUTTON;
else if ( strcasecmp( t, "khtml_isindex" ) == 0 )
newType = ISINDEX;
else
newType = TEXT;
// ### IMPORTANT: Don't allow the type to be changed to FILE after the first
// type change, otherwise a JavaScript programmer would be able to set a text
// field's value to something like /etc/passwd and then change it to a file field.
if (m_type != newType) {
if (newType == FILE && m_haveType) {
// Set the attribute back to the old value.
// Note that this calls parseAttribute again.
setAttribute(ATTR_TYPE, type());
} else {
m_type = newType;
// force reattach if need be.
if (attached()) {
detach();
attach();
}
}
}
m_haveType = true;
}
DOMString HTMLInputElementImpl::type() const
{
// needs to be lowercase according to DOM spec
switch (m_type) {
case TEXT: return "text";
case PASSWORD: return "password";
case CHECKBOX: return "checkbox";
case RADIO: return "radio";
case SUBMIT: return "submit";
case RESET: return "reset";
case FILE: return "file";
case HIDDEN: return "hidden";
case IMAGE: return "image";
case BUTTON: return "button";
default: return "";
}
}
QString HTMLInputElementImpl::state( )
{
switch (m_type) {
case PASSWORD:
return QLatin1String("."); // empty string, avoid restoring
case CHECKBOX:
case RADIO:
return QLatin1String(checked() ? "on" : "off");
case TEXT:
if (autoComplete() && value() != getAttribute(ATTR_VALUE) && document()->view())
document()->view()->addFormCompletionItem(name().string(), value().string());
/* nobreak */
default:
return value().string() + (m_unsubmittedFormChange ? 'M' : '.') + (value().isNull() ? 'N' : '.');
}
}
void HTMLInputElementImpl::restoreState(const QString &state)
{
switch (m_type) {
case CHECKBOX:
case RADIO:
setChecked((state == QLatin1String("on")));
break;
case FILE:
m_value = DOMString(state.left(state.length()-2));
setChanged();
break;
case HIDDEN:
case PASSWORD:
// Don't mess with those...
break;
default:
setValue(state.endsWith('N') ? DOMString() : DOMString(state.left(state.length()-2)));
m_unsubmittedFormChange = (state.right(1) == "M");
break;
}
}
void HTMLInputElementImpl::select( )
{
if(!m_render) return;
if (m_type == TEXT || m_type == PASSWORD)
static_cast<RenderLineEdit*>(m_render)->select();
else if (m_type == FILE)
static_cast<RenderFileButton*>(m_render)->select();
}
void HTMLInputElementImpl::click()
{
QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0,0), Qt::LeftButton, Qt::LeftButton, 0);
dispatchMouseEvent(&me,0, 1);
dispatchMouseEvent(&me,EventImpl::CLICK_EVENT, 1);
}
void HTMLInputElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_AUTOCOMPLETE:
m_autocomplete = strcasecmp( attr->value(), "off" );
break;
case ATTR_TYPE:
parseType(attr->value());
break;
case ATTR_VALUE:
if (m_value.isNull()) {// We only need to setChanged if the form is looking
setChanged(); // at the default value right now.
if (m_type == TEXT && m_render)
m_render->updateFromElement();
}
break;
case ATTR_CHECKED:
m_defaultChecked = attr->val();
if (m_useDefaultChecked) // We only need to setChanged if the form is looking
setChanged(); // at the default checked state right now.
break;
case ATTR_MAXLENGTH:
{
m_maxLen = -1;
if (!attr->val()) break;
bool ok;
const int ml = attr->val()->toInt(&ok);
if (ml > 0 && ml < 32767)
m_maxLen = ml;
else if (ok && ml <= 0)
m_maxLen = 0;
setChanged();
}
break;
case ATTR_SIZE:
m_size = attr->val() ? attr->val()->toInt() : 20;
break;
case ATTR_ALT:
case ATTR_SRC:
if (m_type == IMAGE)
setChanged();
break;
case ATTR_USEMAP:
// ### ignore for the moment
break;
case ATTR_ALIGN:
if ( m_inited && m_type == IMAGE )
addHTMLAlignment( attr->value() );
break;
case ATTR_ACCESSKEY:
break;
case ATTR_WIDTH:
if ( m_type == IMAGE )
addCSSLength(CSS_PROP_WIDTH, attr->value() );
break;
case ATTR_HEIGHT:
if ( m_type == IMAGE )
addCSSLength(CSS_PROP_HEIGHT, attr->value() );
break;
case ATTR_ONSELECT:
setHTMLEventListener(EventImpl::SELECT_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onselect", this));
break;
case ATTR_ONCHANGE:
setHTMLEventListener(EventImpl::CHANGE_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onchange", this));
break;
case ATTR_PLACEHOLDER:
setChanged();
break;
default:
HTMLGenericFormElementImpl::parseAttribute(attr);
}
}
void HTMLInputElementImpl::copyNonAttributeProperties(const ElementImpl* source)
{
const HTMLInputElementImpl *e =
static_cast<const HTMLInputElementImpl*>(source);
m_value = e->m_value;
m_checked = e->m_checked;
m_defaultChecked = e->m_checked;
m_useDefaultChecked = e->m_defaultChecked;
m_indeterminate = e->m_indeterminate;
// ### copy more?
HTMLGenericFormElementImpl::copyNonAttributeProperties(source);
}
void HTMLInputElementImpl::attach()
{
assert(!attached());
assert(!m_render);
assert(parentNode());
if (!m_inited) {
// FIXME: This needs to be dynamic, doesn't it, since someone could set this
// after attachment?
if ((uint) m_type <= ISINDEX && !m_value.isEmpty()) {
const QString value = m_value.string();
// remove newline stuff..
QString nvalue;
unsigned int valueLength = value.length();
for (unsigned int i = 0; i < valueLength; ++i)
if (value[i] >= ' ')
nvalue += value[i];
m_value = nvalue;
}
m_defaultChecked = (getAttribute(ATTR_CHECKED) != 0);
if ( m_type == IMAGE )
addHTMLAlignment( getAttribute( ATTR_ALIGN ) );
m_inited = true;
}
switch( m_type ) {
case PASSWORD:
if (document()->isHTMLDocument())
static_cast<HTMLDocumentImpl*>(document())->setAutoFill();
break;
case HIDDEN:
case IMAGE:
if (!getAttribute(ATTR_WIDTH).isNull())
addCSSLength(CSS_PROP_WIDTH, getAttribute(ATTR_WIDTH));
if (!getAttribute(ATTR_HEIGHT).isNull())
addCSSLength(CSS_PROP_HEIGHT, getAttribute(ATTR_HEIGHT));
default:
break;
};
RenderStyle* const _style = document()->styleSelector()->styleForElement(this);
_style->ref();
if (parentNode()->renderer() && _style->display() != NONE) {
switch(m_type)
{
case TEXT:
case PASSWORD:
case ISINDEX: m_render = new (document()->renderArena()) RenderLineEdit(this); break;
case CHECKBOX: m_render = new (document()->renderArena()) RenderCheckBox(this); break;
case RADIO: m_render = new (document()->renderArena()) RenderRadioButton(this); break;
case SUBMIT: m_render = new (document()->renderArena()) RenderSubmitButton(this); break;
case IMAGE: m_render = new (document()->renderArena()) RenderImageButton(this); break;
case RESET: m_render = new (document()->renderArena()) RenderResetButton(this); break;
case FILE: m_render = new (document()->renderArena()) RenderFileButton(this); break;
case BUTTON: m_render = new (document()->renderArena()) RenderPushButton(this);
case HIDDEN: break;
}
}
// Let check and radio boxes start indeterminate
setIndeterminate(true);
if (m_render)
m_render->setStyle(_style);
HTMLGenericFormElementImpl::attach();
_style->deref();
setChecked(defaultChecked(), true);
}
DOMString HTMLInputElementImpl::altText() const
{
// http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
// also heavily discussed by Hixie on bugzilla
// note this is intentionally different to HTMLImageElementImpl::altText()
DOMString alt = getAttribute( ATTR_ALT );
// fall back to title attribute
if ( alt.isNull() )
alt = getAttribute( ATTR_TITLE );
if ( alt.isNull() )
alt = getAttribute( ATTR_VALUE );
if ( alt.isEmpty() )
alt = i18n( "Submit" );
return alt;
}
bool HTMLInputElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoding, bool multipart)
{
const QString nme = name().string();
// image generates its own name's
if (nme.isEmpty() && m_type != IMAGE) return false;
// IMAGE needs special handling later
if(m_type != IMAGE) encoding += fixUpfromUnicode(codec, nme);
switch (m_type) {
case CHECKBOX:
if( checked() ) {
encoding += fixUpfromUnicode(codec, value().string());
return true;
}
break;
case RADIO:
if( checked() ) {
encoding += fixUpfromUnicode(codec, value().string());
return true;
}
break;
case BUTTON:
case RESET:
// these types of buttons are never successful
return false;
case IMAGE:
if(m_clicked)
{
m_clicked = false;
QString astr(nme.isEmpty() ? QLatin1String("x") : QString(nme + ".x"));
encoding += fixUpfromUnicode(codec, astr);
astr.setNum(qMax( clickX(), 0 ));
encoding += fixUpfromUnicode(codec, astr);
astr = nme.isEmpty() ? QLatin1String("y") : QString(nme + ".y");
encoding += fixUpfromUnicode(codec, astr);
astr.setNum(qMax( clickY(), 0 ) );
encoding += fixUpfromUnicode(codec, astr);
astr = value().string();
if(astr.length() > 0) {
encoding += fixUpfromUnicode(codec, nme);
encoding += fixUpfromUnicode(codec, astr);
}
return true;
}
break;
case SUBMIT:
if (m_activeSubmit)
{
QString enc_str = valueWithDefault().string();
if(!enc_str.isEmpty())
{
encoding += fixUpfromUnicode(codec, enc_str);
return true;
}
}
break;
case FILE: // hmm, we have the type FILE also. bad choice here...
{
QString local;
KUrl fileurl;
QString val = value().string();
if (!val.isEmpty() &&
QDir::isRelativePath(val) &&
QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val)) {
fileurl.setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val);
} else {
fileurl = KUrl(val);
}
KIO::UDSEntry filestat;
// can't submit file in www-url-form encoded
QWidget* const toplevel = document()->view() ? document()->view()->topLevelWidget() : 0;
if (multipart) {
QByteArray filearray;
if ( KIO::NetAccess::stat(fileurl, filestat, toplevel)) {
const KFileItem fileitem(filestat, fileurl, true, false);
if ( fileitem.isFile() &&
KIO::NetAccess::download(fileurl, local, toplevel) ) {
QFile file(local);
if ( file.open( QIODevice::ReadOnly ) ) {
filearray = file.read( file.size() );
file.close();
}
KIO::NetAccess::removeTempFile( local );
}
}
encoding += filearray;
return true;
}
// else fall through
}
case HIDDEN:
case TEXT:
case PASSWORD:
// always successful
encoding += fixUpfromUnicode(codec, value().string());
return true;
case ISINDEX:
encoding += fixUpfromUnicode(codec, value().string());
return true;
}
return false;
}
void HTMLInputElementImpl::reset()
{
if (m_type == FILE) {
// set directly to bypass security check. emptying the value
// should mean no risk.
if (!m_value.isEmpty()) {
m_value = DOMString();
setChanged();
}
} else {
setValue(getAttribute(ATTR_VALUE));
}
m_useDefaultChecked = true;
m_checked = m_defaultChecked;
setIndeterminate(true);
}
void HTMLInputElementImpl::setChecked(bool _checked, bool setDefaultChecked)
{
if (m_type == RADIO && _checked && !name().isEmpty()) {
// uncheck others in the group..
if (m_form) {
m_form->uncheckOtherRadioButtonsInGroup(this, setDefaultChecked);
} else {
// We're not in form, so we group with other formless radios with the same name
HTMLCollectionImpl candidates(document()->documentElement(), HTMLCollectionImpl::FORMLESS_INPUT);
unsigned long len = candidates.length();
for (unsigned long c = 0; c < len; ++c) {
HTMLInputElementImpl* current = static_cast<HTMLInputElementImpl*>(candidates.item(c));
if (current != this && current->name() == name() && current->inputType() == HTMLInputElementImpl::RADIO)
current->setChecked(false, setDefaultChecked);
}
}
}
if (setDefaultChecked) {
if (defaultChecked() == _checked) return;
m_defaultChecked = _checked;
if (!m_useDefaultChecked) return;
}
else {
if (checked() == _checked) return;
m_useDefaultChecked = false;
m_checked = _checked;
}
// setIndeterminate(false);
// Trigger dynamic restyles
document()->dynamicDomRestyler().restyleDependent(this, OtherStateDependency);
// We need to update rendering under all circumstances
if (!changed() && m_render) {
m_render->updateFromElement();
}
}
void HTMLInputElementImpl::setIndeterminate(bool _indeterminate)
{
// Only checkboxes and radio-boxes honor indeterminate.
if (inputType() != CHECKBOX || inputType() != RADIO || indeterminate() == _indeterminate)
return;
m_indeterminate = _indeterminate;
// Trigger dynamic restyles
// document()->dynamicDomRestyler().restyleDependent(this, OtherStateDependency);
// We need to update rendering under all circumstances
if (!changed() && m_render) {
m_render->updateFromElement();
}
}
DOMString HTMLInputElementImpl::valueWithDefault() const
{
DOMString v = value();
if (v.isNull()) {
switch (m_type) {
case RESET:
#ifdef APPLE_CHANGES
v = resetButtonDefaultLabel();
#else
v = i18n("Reset");
#endif
break;
case SUBMIT:
#ifdef APPLE_CHANGES
v = submitButtonDefaultLabel();
#else
v = i18n("Submit");
#endif
break;
case BUTTON:
case CHECKBOX:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
case PASSWORD:
case RADIO:
#ifdef APPLE_CHANGES
case RANGE:
case SEARCH:
#endif
case TEXT:
break;
}
}
return v;
}
DOMString HTMLInputElementImpl::value() const
{
if (m_type == CHECKBOX || m_type == RADIO) {
const DOMString val = getAttribute(ATTR_VALUE);
// If no attribute exists, then we'll just return "on" as
// other browsers strangely seem to do without respecting the
// checked() state of the control.
if (val.isNull())
return DOMString("on");
return val;
}
DOMString val = m_value;
// It's important *not* to fall back to the value attribute for file inputs,
// because that would allow a malicious web page to upload files by setting the
// value attribute in markup.
if (val.isNull() && m_type != FILE)
val = getAttribute(ATTR_VALUE);
return val;
}
void HTMLInputElementImpl::setValue(DOMString val)
{
if (m_type == FILE) return;
m_value = val;
// ### set attribute for other types, too. no need for m_value
// ### in those cases.
if (m_type == RADIO || m_type == CHECKBOX)
setAttribute(ATTR_VALUE, m_value);
if (m_type == TEXT && m_render)
m_render->updateFromElement();
setChanged();
}
void HTMLInputElementImpl::defaultEventHandler(EventImpl *evt)
{
if ( !m_disabled )
{
if (evt->isMouseEvent()) {
MouseEventImpl* const me = static_cast<MouseEventImpl*>(evt);
if ((m_type == RADIO || m_type == CHECKBOX)
&& me->id() == EventImpl::MOUSEUP_EVENT && me->detail() > 0) {
// Did we change? Always yes for checkboxes, and for radio buttons
// only if we're not already checked.
bool changed = m_type == CHECKBOX || !checked();
// click will follow
setChecked(m_type == RADIO ? true : !checked());
if (changed)
onChange();
}
if (evt->id() == EventImpl::CLICK_EVENT && m_type == IMAGE && m_render) {
// record the mouse position for when we get the DOMActivate event
int offsetX, offsetY;
m_render->absolutePosition(offsetX,offsetY);
xPos = me->clientX()-offsetX;
yPos = me->clientY()-offsetY;
KHTMLView* v = document()->view();
if ( v ) {
xPos += v->contentsX();
yPos += v->contentsY();
}
}
}
if (m_type == RADIO || m_type == CHECKBOX || m_type == SUBMIT || m_type == RESET || m_type == BUTTON ) {
bool check = false;
if (active() && ( evt->id() == EventImpl::KEYUP_EVENT ||
evt->id() == EventImpl::KEYPRESS_EVENT ) ) {
TextEventImpl* const te = static_cast<TextEventImpl *>(evt);
if (te->keyVal() == ' ')
check = true;
else if (te->keyVal() == '\r' && (m_type == BUTTON || m_type == RESET || m_type == SUBMIT))
check = true;
}
if (check) {
if (evt->id() == EventImpl::KEYUP_EVENT)
click();
// Tell the parent that we handle this key (keyup and keydown), even though only keyup activates (#70478)
evt->setDefaultHandled();
}
}
// Submit form when a return is pressed in a radio/checkbox
// TODO: move this for text here (from RenderLineEdit)
if (m_type == CHECKBOX || m_type == RADIO) {
if (evt->id() == EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) {
QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
if (ke && m_form && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter))
m_form->submitFromKeyboard();
}
}
// DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
// actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
// on the element, or presses enter while it is the active element. Javascript code wishing to activate the element
// must dispatch a DOMActivate event - a click event will not do the job.
if (m_type == IMAGE || m_type == SUBMIT || m_type == RESET) {
bool act = (evt->id() == EventImpl::DOMACTIVATE_EVENT);
if (!act && evt->id() == EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) {
QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space))
act = true;
}
if (act)
activate();
}
}
HTMLGenericFormElementImpl::defaultEventHandler(evt);
}
void HTMLInputElementImpl::activate()
{
if (!m_form)
return;
m_clicked = true;
if (m_type == RESET) {
m_form->reset();
}
else {
m_activeSubmit = true;
if (!m_form->prepareSubmit()) {
xPos = 0;
yPos = 0;
}
m_activeSubmit = false;
}
}
bool HTMLInputElementImpl::isEditable()
{
return ((m_type == TEXT) || (m_type == PASSWORD) || (m_type == ISINDEX) || (m_type == FILE));
}
long HTMLInputElementImpl::selectionStart()
{
if (m_type != TEXT || !m_render) return -1;
return static_cast<RenderLineEdit*>(m_render)->selectionStart();
}
long HTMLInputElementImpl::selectionEnd()
{
if (m_type != TEXT || !m_render) return -1;
return static_cast<RenderLineEdit*>(m_render)->selectionEnd();
}
void HTMLInputElementImpl::setSelectionStart(long pos)
{
if (m_type != TEXT || !m_render) return;
static_cast<RenderLineEdit*>(m_render)->setSelectionStart(pos);
}
void HTMLInputElementImpl::setSelectionEnd (long pos)
{
if (m_type != TEXT || !m_render) return;
static_cast<RenderLineEdit*>(m_render)->setSelectionEnd(pos);
}
void HTMLInputElementImpl::setSelectionRange(long start, long end)
{
if (m_type != TEXT || !m_render) return;
static_cast<RenderLineEdit*>(m_render)->setSelectionRange(start, end);
}
void HTMLInputElementImpl::setPlaceholder(const DOMString& p)
{
setAttribute(ATTR_PLACEHOLDER, p);
}
DOMString HTMLInputElementImpl::placeholder() const
{
return getAttribute(ATTR_PLACEHOLDER);
}
// -------------------------------------------------------------------------
HTMLLabelElementImpl::HTMLLabelElementImpl(DocumentImpl *doc)
: HTMLGenericFormElementImpl(doc)
{
}
HTMLLabelElementImpl::~HTMLLabelElementImpl()
{
}
NodeImpl::Id HTMLLabelElementImpl::id() const
{
return ID_LABEL;
}
void HTMLLabelElementImpl::attach()
{
// skip the generic handler
HTMLElementImpl::attach();
}
bool HTMLLabelElementImpl::isFocusableImpl(FocusType ft) const
{
if (hasTabIndex())
return HTMLGenericFormElementImpl::isFocusableImpl(ft);
// We want labels to accept focus on click, but not on tabbing.
return (ft != FT_Tab);
}
NodeImpl* HTMLLabelElementImpl::getFormElement()
{
const DOMString formElementId = getAttribute(ATTR_FOR);
NodeImpl *newNode=0L;
if (!formElementId.isEmpty())
newNode=document()->getElementById(formElementId);
if (!newNode){
const uint children=childNodeCount();
if (children>1)
for (unsigned int i=0;i<children;++i){
const uint nodeId=childNode(i)->id();
if (nodeId==ID_INPUT || nodeId==ID_SELECT || nodeId==ID_TEXTAREA){
newNode=childNode(i);
break;
}
}
}
return newNode;
}
void HTMLLabelElementImpl::defaultEventHandler(EventImpl *evt)
{
if ( !m_disabled ) {
bool act = false;
if ( evt->id() == EventImpl::CLICK_EVENT ) {
act = true;
}
else if ( evt->isKeyRelatedEvent() && ( evt->id() == EventImpl::KEYUP_EVENT ||
evt->id() == EventImpl::KEYPRESS_EVENT ) ) {
QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space))
act = true;
}
if (act) {
NodeImpl* const formNode=getFormElement();
if (formNode && evt->target() != formNode) {
document()->setFocusNode(formNode);
if (formNode->id()==ID_INPUT && !static_cast<DOM::HTMLInputElementImpl*>(formNode)->disabled())
static_cast<DOM::HTMLInputElementImpl*>(formNode)->click();
evt->setDefaultHandled();
}
}
}
HTMLGenericFormElementImpl::defaultEventHandler(evt);
}
// -------------------------------------------------------------------------
HTMLLegendElementImpl::HTMLLegendElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
}
HTMLLegendElementImpl::~HTMLLegendElementImpl()
{
}
NodeImpl::Id HTMLLegendElementImpl::id() const
{
return ID_LEGEND;
}
void HTMLLegendElementImpl::attach()
{
assert(!attached());
assert(!m_render);
assert(parentNode());
RenderStyle* const _style = document()->styleSelector()->styleForElement(this);
_style->ref();
if (parentNode()->renderer() && _style->display() != NONE) {
m_render = new (document()->renderArena()) RenderLegend(this);
m_render->setStyle(_style);
}
HTMLGenericFormElementImpl::attach();
_style->deref();
}
void HTMLLegendElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_ACCESSKEY:
break;
default:
HTMLElementImpl::parseAttribute(attr);
}
}
// -------------------------------------------------------------------------
HTMLSelectElementImpl::HTMLSelectElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
m_multiple = false;
m_recalcListItems = false;
// 0 means invalid (i.e. not set)
m_size = 0;
m_minwidth = 0;
m_length = 0;
}
HTMLSelectElementImpl::~HTMLSelectElementImpl()
{
if (document()) document()->deregisterMaintainsState(this);
}
NodeImpl::Id HTMLSelectElementImpl::id() const
{
return ID_SELECT;
}
DOMString HTMLSelectElementImpl::type() const
{
return (m_multiple ? "select-multiple" : "select-one");
}
HTMLCollectionImpl* HTMLSelectElementImpl::options()
{
return new HTMLCollectionImpl(this, HTMLCollectionImpl::SELECT_OPTIONS);
}
long HTMLSelectElementImpl::selectedIndex() const
{
// return the number of the first option selected
uint o = 0;
const QVector<HTMLGenericFormElementImpl*> items = listItems();
const unsigned int itemsSize = items.size();
for (unsigned int i = 0; i < itemsSize; ++i) {
if (items[i]->id() == ID_OPTION) {
if (static_cast<HTMLOptionElementImpl*>(items[i])->selectedBit())
return o;
o++;
}
}
// Q_ASSERT(m_multiple || items.isEmpty());
return -1;
}
void HTMLSelectElementImpl::setSelectedIndex( long index )
{
// deselect all other options and select only the new one
const QVector<HTMLGenericFormElementImpl*> items = listItems();
int listIndex;
const int itemsSize = int(items.size());
for (listIndex = 0; listIndex < itemsSize; ++listIndex) {
if (items[listIndex]->id() == ID_OPTION)
static_cast<HTMLOptionElementImpl*>(items[listIndex])->setSelected(false);
}
listIndex = optionToListIndex(index);
if (listIndex >= 0)
static_cast<HTMLOptionElementImpl*>(items[listIndex])->setSelected(true);
setChanged(true);
}
long HTMLSelectElementImpl::length() const
{
if (m_recalcListItems)
recalcListItems();
return m_length;
}
void HTMLSelectElementImpl::add( HTMLElementImpl* element, HTMLElementImpl* before, int& exceptioncode )
{
if(!element || element->id() != ID_OPTION)
return;
HTMLOptionElementImpl* option = static_cast<HTMLOptionElementImpl*>(element);
//Fast path for appending an item. Can't be done if it is selected and
//we're single-select, since we may need to drop an implicitly-selected item
bool fastAppendLast = false;
if (before == 0 && (m_multiple || !option->selectedBit()) && !m_recalcListItems)
fastAppendLast = true;
insertBefore(option, before, exceptioncode);
if (fastAppendLast && !exceptioncode) {
m_listItems.resize(m_listItems.size() + 1);
m_listItems[m_listItems.size() - 1] = option;
++m_length;
if (m_length == 1 && !m_multiple) //we added the first item in single-select --- select it.
option->setSelected(true);
m_recalcListItems = false; // was set by insertBefore
} else if (!exceptioncode)
setRecalcListItems();
}
void HTMLSelectElementImpl::remove( long index )
{
int exceptioncode = 0;
const int listIndex = optionToListIndex(index);
const QVector<HTMLGenericFormElementImpl*> items = listItems();
if(listIndex < 0 || index >= int(items.size()))
return; // ### what should we do ? remove the last item?
//Fast path for last element, for e.g. clearing the box
//Note that if this is a single-select, we may have to recompute
//anyway if the item was selected, since we may want to set
//a different one
bool fastRemoveLast = false;
if ((listIndex == items.size() - 1) && !m_recalcListItems &&
(m_multiple || !static_cast<HTMLOptionElementImpl*>(items[listIndex])->selectedBit()))
fastRemoveLast = true;
removeChild(items[listIndex], exceptioncode);
if (fastRemoveLast) {
m_listItems.resize(m_listItems.size() - 1);
--m_length;
m_recalcListItems = false;
} else if( !exceptioncode)
setRecalcListItems();
}
HTMLOptionElementImpl* HTMLSelectElementImpl::firstSelectedItem() const
{
uint i;
const QVector<HTMLGenericFormElementImpl*> items = listItems();
const uint itemsSize = items.size();
for (i = 0; i < itemsSize; ++i) {
if ( items[i]->id() == ID_OPTION
&& static_cast<HTMLOptionElementImpl*>(items[i])->selectedBit())
return static_cast<HTMLOptionElementImpl*>(items[i]);
}
return 0;
}
DOMString HTMLSelectElementImpl::value( ) const
{
HTMLOptionElementImpl* o = firstSelectedItem();
if (o)
return o->value();
return DOMString("");
}
void HTMLSelectElementImpl::setValue(DOMStringImpl* value)
{
// find the option with value() matching the given parameter
// and make it the current selection.
const QVector<HTMLGenericFormElementImpl*> items = listItems();
for (int i = 0; i < items.size(); i++)
if (items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->value() == value) {
static_cast<HTMLOptionElementImpl*>(items[i])->setSelected(true);
return;
}
}
QString HTMLSelectElementImpl::state( )
{
QString state;
const QVector<HTMLGenericFormElementImpl*> items = listItems();
const int l = items.count();
// We have two encoding schemes: for single-select ones, if the selected
// element has an ID, we encoded it as iFoo.
if (!multiple()) {
HTMLOptionElementImpl* item = firstSelectedItem();
if (item && item->hasID())
return QString::fromLatin1("i") + item->getAttribute(ATTR_ID).string();
}
// Otherwise we merely go positional..
state.fill('.', l);
for(int i = 0; i < l; ++i)
if(items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->selectedBit())
state[i] = 'X';
return state;
}
void HTMLSelectElementImpl::restoreState(const QString &_state)
{
recalcListItems();
QString state = _state;
const QVector<HTMLGenericFormElementImpl*> items = listItems();
const int l = items.count();
// First see if we have an id-tagged one, and if it's correct.
if (state.startsWith(QLatin1Char('i'))) {
DOM::DOMString id = state.mid(1);
DOM::ElementImpl* cand = document()->getElementById(id);
// No such ID or not an option -> wrong
if (!cand || cand->id() != ID_OPTION)
return;
// See if it's one of ours, and select if so.
for (int i = 0; i < l; ++i) {
if (items[i] == cand)
static_cast<HTMLOptionElementImpl*>(cand)->setSelected(true);
}
// If we succeeded, our state is updated --- if not, we better
// leave this be.
return;
}
if(!state.isEmpty() && !state.contains('X') && !m_multiple && m_size <= 1) {
qWarning("should not happen in restoreState!");
state[0] = 'X';
}
// Now we have positional encoding. Make sure the length matches.
if (m_length != state.length()) // m_length == number of <option>, while
// l == items.length() includes optgroups, too.
return;
for(int i = 0; i < l; ++i) {
if(items[i]->id() == ID_OPTION) {
HTMLOptionElementImpl* const oe = static_cast<HTMLOptionElementImpl*>(items[i]);
oe->setSelected(state[i] == 'X');
}
}
setChanged(true);
}
NodeImpl *HTMLSelectElementImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode )
{
NodeImpl* const result = HTMLGenericFormElementImpl::insertBefore(newChild,refChild, exceptioncode );
if (!exceptioncode)
setRecalcListItems();
return result;
}
void HTMLSelectElementImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode )
{
HTMLGenericFormElementImpl::replaceChild(newChild,oldChild, exceptioncode);
if( !exceptioncode )
setRecalcListItems();
}
void HTMLSelectElementImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode )
{
HTMLGenericFormElementImpl::removeChild(oldChild, exceptioncode);
if( !exceptioncode )
setRecalcListItems();
}
void HTMLSelectElementImpl::removeChildren()
{
HTMLGenericFormElementImpl::removeChildren();
setRecalcListItems();
}
NodeImpl *HTMLSelectElementImpl::appendChild ( NodeImpl *newChild, int &exceptioncode )
{
NodeImpl* const result = HTMLGenericFormElementImpl::appendChild(newChild, exceptioncode);
if( !exceptioncode )
setRecalcListItems();
setChanged(true);
return result;
}
NodeImpl* HTMLSelectElementImpl::addChild(NodeImpl* newChild)
{
setRecalcListItems();
return HTMLGenericFormElementImpl::addChild(newChild);
}
void HTMLSelectElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_SIZE:
m_size = qMax( attr->val()->toInt(), 1 );
setChanged();
break;
case ATTR_WIDTH:
m_minwidth = qMax( attr->val()->toInt(), 0 );
break;
case ATTR_MULTIPLE:
m_multiple = (attr->val() != 0);
break;
case ATTR_ACCESSKEY:
break;
case ATTR_ALIGN:
addHTMLAlignment( attr->value() );
break;
case ATTR_ONCHANGE:
setHTMLEventListener(EventImpl::CHANGE_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onchange", this));
break;
default:
HTMLGenericFormElementImpl::parseAttribute(attr);
}
}
void HTMLSelectElementImpl::attach()
{
assert(!attached());
assert(parentNode());
assert(!renderer());
RenderStyle* const _style = document()->styleSelector()->styleForElement(this);
_style->ref();
if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
_style->display() != NONE) {
m_render = new (document()->renderArena()) RenderSelect(this);
m_render->setStyle(_style);
}
HTMLGenericFormElementImpl::attach();
_style->deref();
}
bool HTMLSelectElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoded_values, bool)
{
// submitting with no name would lead to empty lhs ?=foo&=bar
if (name().isEmpty())
return false;
bool successful = false;
const QByteArray enc_name = fixUpfromUnicode(codec, name().string());
const QVector<HTMLGenericFormElementImpl*> items = listItems();
uint i;
const uint itemsSize = items.size();
for (i = 0; i < itemsSize; ++i) {
if (items[i]->id() == ID_OPTION) {
HTMLOptionElementImpl* const option = static_cast<HTMLOptionElementImpl*>(items[i]);
if (option->selectedBit() && !option->disabled()) {
encoded_values += enc_name;
encoded_values += fixUpfromUnicode(codec, option->value().string());
successful = true;
}
}
}
// ### this case should not happen. make sure that we select the first option
// in any case. otherwise we have no consistency with the DOM interface. FIXME!
// we return the first one if it was a combobox select
if (!successful && !m_multiple && m_size <= 1 && itemsSize &&
(items[0]->id() == ID_OPTION && !items[0]->disabled()) ) {
HTMLOptionElementImpl* const option = static_cast<HTMLOptionElementImpl*>(items[0]);
encoded_values += enc_name;
if (option->value().isNull())
encoded_values += fixUpfromUnicode(codec, option->text().string());
else
encoded_values += fixUpfromUnicode(codec, option->value().string());
successful = true;
}
return successful;
}
int HTMLSelectElementImpl::optionToListIndex(int optionIndex) const
{
const QVector<HTMLGenericFormElementImpl*> items = listItems();
const int itemsSize = int(items.size());
if (optionIndex < 0 || optionIndex >= itemsSize)
return -1;
//See if we're asked for the very last item, and check whether it's an <option>
//to fastpath clear
if (optionIndex == (m_length - 1) && items[itemsSize - 1]->id() == ID_OPTION)
return itemsSize - 1;
int listIndex = 0;
int optionIndex2 = 0;
for (;
optionIndex2 < itemsSize && optionIndex2 <= optionIndex;
++listIndex) { // not a typo!
if (items[listIndex]->id() == ID_OPTION)
++optionIndex2;
}
--listIndex;
return listIndex;
}
int HTMLSelectElementImpl::listToOptionIndex(int listIndex) const
{
const QVector<HTMLGenericFormElementImpl*> items = listItems();
if (listIndex < 0 || listIndex >= int(items.size()) ||
items[listIndex]->id() != ID_OPTION)
return -1;
int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
int i;
for (i = 0; i < listIndex; i++)
if (items[i]->id() == ID_OPTION)
optionIndex++;
return optionIndex;
}
void HTMLSelectElementImpl::recalcListItems() const
{
NodeImpl* current = firstChild();
m_listItems.resize(0);
HTMLOptionElementImpl* foundSelected = 0;
m_length = 0;
while(current) {
if (current->id() == ID_OPTGROUP && current->firstChild()) {
// ### what if optgroup contains just comments? don't want one of no options in it...
m_listItems.resize(m_listItems.size()+1);
m_listItems[m_listItems.size()-1] = static_cast<HTMLGenericFormElementImpl*>(current);
current = current->firstChild();
}
if (current->id() == ID_OPTION) {
++m_length;
m_listItems.resize(m_listItems.size()+1);
m_listItems[m_listItems.size()-1] = static_cast<HTMLGenericFormElementImpl*>(current);
if (!foundSelected && !m_multiple && m_size <= 1) {
foundSelected = static_cast<HTMLOptionElementImpl*>(current);
foundSelected->m_selected = true;
}
else if (foundSelected && !m_multiple && static_cast<HTMLOptionElementImpl*>(current)->selectedBit()) {
foundSelected->m_selected = false;
foundSelected = static_cast<HTMLOptionElementImpl*>(current);
}
}
NodeImpl* const parent = current->parentNode();
current = current->nextSibling();
if (!current) {
if (static_cast<const NodeImpl *>(parent) != this)
current = parent->nextSibling();
}
}
m_recalcListItems = false;
}
void HTMLSelectElementImpl::childrenChanged()
{
setRecalcListItems();
HTMLGenericFormElementImpl::childrenChanged();
}
void HTMLSelectElementImpl::setRecalcListItems()
{
m_recalcListItems = true;
if (m_render)
static_cast<khtml::RenderSelect*>(m_render)->setOptionsChanged(true);
setChanged();
}
void HTMLSelectElementImpl::reset()
{
const QVector<HTMLGenericFormElementImpl*> items = listItems();
uint i;
const uint itemsSize = items.size();
bool anySelected = false;
for (i = 0; i < itemsSize; ++i) {
if (items[i]->id() == ID_OPTION) {
HTMLOptionElementImpl* const option = static_cast<HTMLOptionElementImpl*>(items[i]);
const bool selected = (!option->getAttribute(ATTR_SELECTED).isNull());
option->setSelected(selected);
if (selected)
anySelected = true;
}
}
// If this is a single-row SELECT and there is no default selection, jump to first option.
if ( !anySelected && m_size <= 1 ) {
for (i = 0; i < itemsSize; ++i) {
if (items[i]->id() == ID_OPTION) {
static_cast<HTMLOptionElementImpl*>(items[i])->setSelected(true);
break;
}
}
}
if ( m_render )
static_cast<RenderSelect*>(m_render)->setSelectionChanged(true);
setChanged( true );
}
void HTMLSelectElementImpl::notifyOptionSelected(HTMLOptionElementImpl *selectedOption, bool selected)
{
if (selected && !m_multiple) {
// deselect all other options
const QVector<HTMLGenericFormElementImpl*> items = listItems();
uint i;
const uint itemsSize = items.size();
for (i = 0; i < itemsSize; ++i) {
if (items[i]->id() == ID_OPTION)
static_cast<HTMLOptionElementImpl*>(items[i])->m_selected = (items[i] == selectedOption);
}
}
if (m_render)
static_cast<RenderSelect*>(m_render)->setSelectionChanged(true);
setChanged(true);
}
// -------------------------------------------------------------------------
HTMLKeygenElementImpl::HTMLKeygenElementImpl(DocumentImpl* doc, HTMLFormElementImpl* f)
: HTMLSelectElementImpl(doc, f)
{
const QStringList keys = KSSLKeyGen::supportedKeySizes();
QStringList::ConstIterator i = keys.begin();
const QStringList::ConstIterator iEnd = keys.end();
for ( ; i != iEnd; ++i) {
HTMLOptionElementImpl* const o = new HTMLOptionElementImpl(doc, form());
addChild(o);
o->addChild(doc->createTextNode(DOMString(*i).implementation()));
}
}
NodeImpl::Id HTMLKeygenElementImpl::id() const
{
return ID_KEYGEN;
}
void HTMLKeygenElementImpl::parseAttribute(AttributeImpl* attr)
{
switch(attr->id())
{
case ATTR_CHALLENGE:
break;
default:
// skip HTMLSelectElementImpl parsing!
HTMLGenericFormElementImpl::parseAttribute(attr);
}
}
bool HTMLKeygenElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoded_values, bool)
{
bool successful = false;
const QByteArray enc_name = fixUpfromUnicode(codec, name().string());
encoded_values += enc_name;
// pop up the fancy certificate creation dialog here
KSSLKeyGen* const kg = new KSSLKeyGen(static_cast<RenderWidget *>(m_render)->widget());
kg->setWindowTitle(i18n("Key Generator"));
kg->setModal(true);
kg->setKeySize(0);
successful = (QDialog::Accepted == kg->exec());
delete kg;
encoded_values += "deadbeef";
return successful;
}
// -------------------------------------------------------------------------
NodeImpl::Id HTMLOptGroupElementImpl::id() const
{
return ID_OPTGROUP;
}
// -------------------------------------------------------------------------
HTMLOptionElementImpl::HTMLOptionElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
m_selected = false;
m_defaultSelected = false;
}
NodeImpl::Id HTMLOptionElementImpl::id() const
{
return ID_OPTION;
}
DOMString HTMLOptionElementImpl::text() const
{
if (firstChild() && firstChild()->nodeType() == Node::TEXT_NODE && !firstChild()->nextSibling())
return firstChild()->nodeValue();
DOMString ret = "";
NodeImpl *n = firstChild();
for (; n; n = n->nextSibling()) {
if (n->nodeType() == Node::TEXT_NODE ||
n->nodeType() == Node::CDATA_SECTION_NODE)
ret += n->nodeValue();
}
return ret;
}
long HTMLOptionElementImpl::index() const
{
// Let's do this dynamically. Might be a bit slow, but we're sure
// we won't forget to update a member variable in some cases...
const QVector<HTMLGenericFormElementImpl*> items = getSelect()->listItems();
const int l = items.count();
int optionIndex = 0;
for(int i = 0; i < l; ++i) {
if(items[i]->id() == ID_OPTION)
{
if (static_cast<HTMLOptionElementImpl*>(items[i]) == this)
return optionIndex;
++optionIndex;
}
}
kWarning() << "HTMLOptionElementImpl::index(): option not found!";
return 0;
}
void HTMLOptionElementImpl::setIndex( long )
{
kWarning() << "Unimplemented HTMLOptionElementImpl::setIndex(long) called";
// ###
}
void HTMLOptionElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_SELECTED:
m_selected = (attr->val() != 0);
m_defaultSelected = m_selected;
break;
case ATTR_VALUE:
m_value = attr->value();
break;
default:
HTMLGenericFormElementImpl::parseAttribute(attr);
}
}
DOMString HTMLOptionElementImpl::value() const
{
if ( !m_value.isNull() )
return m_value;
// Use the text if the value wasn't set.
return text().string();
}
void HTMLOptionElementImpl::setValue(DOMStringImpl* value)
{
setAttribute(ATTR_VALUE, value);
}
void HTMLOptionElementImpl::setSelected(bool _selected)
{
if(m_selected == _selected)
return;
m_selected = _selected;
HTMLSelectElementImpl* const select = getSelect();
if (select)
select->notifyOptionSelected(this,_selected);
}
bool HTMLOptionElementImpl::selected() const
{
// make sure our parent select is up-to-date, since that may update our selected bit
if (HTMLSelectElementImpl* select = getSelect())
(void)select->listItems();
return m_selected;
}
void HTMLOptionElementImpl::setDefaultSelected( bool _defaultSelected )
{
setAttribute(ATTR_SELECTED, _defaultSelected ? "" : 0);
}
HTMLSelectElementImpl *HTMLOptionElementImpl::getSelect() const
{
NodeImpl *select = parentNode();
while (select && select->id() != ID_SELECT)
select = select->parentNode();
return static_cast<HTMLSelectElementImpl*>(select);
}
// -------------------------------------------------------------------------
/*
The rules for storing the value are simple:
If there is no renderer, either m_value or defaultValue() is definitive,
depending on whether m_initialized is true or not.
If there is a renderer, m_render->text() is definitive. During its construction,
m_value is initialized if needed, so there is no longer any need to worry
about default values or not.
*/
HTMLTextAreaElementImpl::HTMLTextAreaElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(doc, f)
{
// DTD requires rows & cols be specified, but we will provide reasonable defaults
m_rows = 2;
m_cols = 20;
m_wrap = ta_Virtual;
m_changed = false;
m_initialized = false;
m_unsubmittedFormChange = false;
}
HTMLTextAreaElementImpl::~HTMLTextAreaElementImpl()
{
if (document()) document()->deregisterMaintainsState(this);
}
NodeImpl::Id HTMLTextAreaElementImpl::id() const
{
return ID_TEXTAREA;
}
DOMString HTMLTextAreaElementImpl::type() const
{
return "textarea";
}
QString HTMLTextAreaElementImpl::state( )
{
return value().string() + (m_unsubmittedFormChange ? 'M' : '.');
}
void HTMLTextAreaElementImpl::restoreState(const QString &state)
{
setDefaultValue(state.left(state.length()-1));
m_unsubmittedFormChange = state.endsWith('M');
}
void HTMLTextAreaElementImpl::select( )
{
if (m_render)
static_cast<RenderTextArea*>(m_render)->select();
onSelect();
}
void HTMLTextAreaElementImpl::childrenChanged()
{
setValue(defaultValue());
}
void HTMLTextAreaElementImpl::parseAttribute(AttributeImpl *attr)
{
switch(attr->id())
{
case ATTR_ROWS:
m_rows = 0;
if (attr->val())
m_rows = DOMString(attr->val()).string().toInt();
if (!m_rows) m_rows = 2;
if (renderer())
renderer()->setNeedsLayoutAndMinMaxRecalc();
break;
case ATTR_COLS:
m_cols = 0;
if (attr->val())
m_cols = DOMString(attr->val()).string().toInt();
if (!m_cols) m_cols = 20;
if (renderer())
renderer()->setNeedsLayoutAndMinMaxRecalc();
break;
case ATTR_WRAP:
// virtual / physical is Netscape extension of HTML 3.0, now deprecated
// soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4
if ( strcasecmp( attr->value(), "virtual" ) == 0 || strcasecmp( attr->value(), "soft") == 0)
m_wrap = ta_Virtual;
else if ( strcasecmp ( attr->value(), "physical" ) == 0 || strcasecmp( attr->value(), "hard") == 0)
m_wrap = ta_Physical;
else if(strcasecmp( attr->value(), "on" ) == 0)
m_wrap = ta_Physical;
else if(strcasecmp( attr->value(), "off") == 0)
m_wrap = ta_NoWrap;
break;
case ATTR_ACCESSKEY:
break;
case ATTR_ALIGN:
break;
case ATTR_ONSELECT:
setHTMLEventListener(EventImpl::SELECT_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onselect", this));
break;
case ATTR_ONCHANGE:
setHTMLEventListener(EventImpl::CHANGE_EVENT,
document()->createHTMLEventListener(attr->value().string(), "onchange", this));
break;
case ATTR_PLACEHOLDER:
setChanged();
break;
default:
HTMLGenericFormElementImpl::parseAttribute(attr);
}
}
void HTMLTextAreaElementImpl::attach()
{
assert(!attached());
assert(!m_render);
assert(parentNode());
RenderStyle* const _style = document()->styleSelector()->styleForElement(this);
_style->ref();
if (parentNode()->renderer() && _style->display() != NONE) {
m_render = new (document()->renderArena()) RenderTextArea(this);
m_render->setStyle(_style);
}
HTMLGenericFormElementImpl::attach();
_style->deref();
}
static QString expandLF(const QString& s)
{
// LF -> CRLF
unsigned crs = s.count( '\n' );
if (crs == 0)
return s;
unsigned len = s.length();
QString r;
r.reserve(len + crs + 1);
unsigned pos2 = 0;
for(unsigned pos = 0; pos < len; pos++)
{
QChar c = s.at(pos);
switch(c.unicode())
{
case '\n':
r[pos2++] = '\r';
r[pos2++] = '\n';
break;
case '\r':
break;
default:
r[pos2++]= c;
break;
}
}
r.squeeze();
return r;
}
bool HTMLTextAreaElementImpl::encoding(const QTextCodec* codec, encodingList& encoding, bool)
{
if (name().isEmpty()) return false;
encoding += fixUpfromUnicode(codec, name().string());
encoding += fixUpfromUnicode(codec, expandLF(value().string()));
return true;
}
void HTMLTextAreaElementImpl::reset()
{
setValue(defaultValue());
}
DOMString HTMLTextAreaElementImpl::value()
{
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
m_value = renderArea->text();
} else {
if (!m_initialized) {
m_value = defaultValue().string();
m_initialized = true;
}
}
if ( m_value.isNull() ) return "";
return m_value;
}
void HTMLTextAreaElementImpl::setValue(DOMString _value)
{
// \r\n -> \n, \r -> \n
QString str = _value.string().replace( "\r\n", "\n" );
m_value = str.replace( '\r', '\n' );
m_initialized = true;
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
renderArea->setText( m_value );
}
setChanged(true);
}
DOMString HTMLTextAreaElementImpl::defaultValue()
{
DOMString val = "";
// there may be comments - just grab the text nodes
NodeImpl *n;
for (n = firstChild(); n; n = n->nextSibling())
if (n->isTextNode())
val += static_cast<TextImpl*>(n)->data();
if (val[0] == '\r' && val[1] == '\n') {
val = val.copy();
val.remove(0,2);
}
else if (val[0] == '\r' || val[0] == '\n') {
val = val.copy();
val.remove(0,1);
}
return val;
}
void HTMLTextAreaElementImpl::setDefaultValue(DOMString _defaultValue)
{
// there may be comments - remove all the text nodes and replace them with one
QList<NodeImpl*> toRemove;
NodeImpl *n;
for (n = firstChild(); n; n = n->nextSibling())
if (n->isTextNode())
toRemove.append(n);
QListIterator<NodeImpl*> it(toRemove);
int exceptioncode = 0;
while (it.hasNext()) {
removeChild(it.next(), exceptioncode);
}
insertBefore(document()->createTextNode(_defaultValue.implementation()),firstChild(), exceptioncode);
setValue(_defaultValue);
}
bool HTMLTextAreaElementImpl::isEditable()
{
return true;
}
//Mozilla extensions.
long HTMLTextAreaElementImpl::selectionStart()
{
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
return renderArea->selectionStart();
}
return 0;
}
long HTMLTextAreaElementImpl::selectionEnd()
{
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
return renderArea->selectionEnd();
}
return 0;
}
void HTMLTextAreaElementImpl::setSelectionStart(long pos)
{
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
renderArea->setSelectionStart( pos );
}
}
void HTMLTextAreaElementImpl::setSelectionEnd(long pos)
{
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
renderArea->setSelectionEnd( pos );
}
}
void HTMLTextAreaElementImpl::setSelectionRange(long start, long end)
{
if (m_render) {
RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render );
renderArea->setSelectionRange( start, end );
}
}
long HTMLTextAreaElementImpl::textLength()
{
return value().length();
}
void HTMLTextAreaElementImpl::setPlaceholder(const DOMString& p)
{
setAttribute(ATTR_PLACEHOLDER, p);
}
DOMString HTMLTextAreaElementImpl::placeholder() const
{
return getAttribute(ATTR_PLACEHOLDER);
}
// -------------------------------------------------------------------------
HTMLIsIndexElementImpl::HTMLIsIndexElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLInputElementImpl(doc, f)
{
m_type = TEXT;
setName("isindex");
}
HTMLIsIndexElementImpl::~HTMLIsIndexElementImpl()
{
}
NodeImpl::Id HTMLIsIndexElementImpl::id() const
{
return ID_ISINDEX;
}
void HTMLIsIndexElementImpl::parseAttribute(AttributeImpl* attr)
{
// don't call HTMLInputElement::parseAttribute here, as it would
// accept attributes this element does not support
HTMLGenericFormElementImpl::parseAttribute(attr);
}
DOMString HTMLIsIndexElementImpl::prompt() const
{
// When IsIndex is parsed, <HR/>Prompt: <ISINDEX/><HR/> is created.
// So we have to look at the previous sibling to find the prompt text
DOM::NodeImpl* const prev = previousSibling();
if ( prev && prev->nodeType() == DOM::Node::TEXT_NODE)
return prev->nodeValue();
return "";
}
void HTMLIsIndexElementImpl::setPrompt(const DOMString& str)
{
// When IsIndex is parsed, <HR/>Prompt: <ISINDEX/><HR/> is created.
// So we have to look at the previous sibling to find the prompt text
int exceptioncode = 0;
DOM::NodeImpl* const prev = previousSibling();
if ( prev && prev->nodeType() == DOM::Node::TEXT_NODE)
static_cast<DOM::TextImpl *>(prev)->setData(str, exceptioncode);
}
// -------------------------------------------------------------------------
// kate: indent-width 4; replace-tabs on; tab-width 8; space-indent on;
diff --git a/khtml/html/htmltokenizer.cpp b/khtml/html/htmltokenizer.cpp
index 4328ab427e..302b2ea628 100644
--- a/khtml/html/htmltokenizer.cpp
+++ b/khtml/html/htmltokenizer.cpp
@@ -1,2178 +1,2178 @@
/*
This file is part of the KDE libraries
Copyright (C) 1997 Martin Jones (mjones@kde.org)
(C) 1997 Torben Weis (weis@kde.org)
(C) 1998 Waldo Bastian (bastian@kde.org)
(C) 1999 Lars Knoll (knoll@kde.org)
(C) 1999 Antti Koivisto (koivisto@kde.org)
(C) 2001-2003 Dirk Mueller (mueller@kde.org)
(C) 2004-2008 Apple Computer, Inc.
(C) 2006-2008 Germain Garand (germain@ebooksfrance.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.
*/
//----------------------------------------------------------------------------
//
// KDE HTML Widget - Tokenizers
// #define TOKEN_DEBUG 1
//#define TOKEN_DEBUG 2
#include "htmltokenizer.h"
#include "html_documentimpl.h"
#include "htmlparser.h"
#include "dtd.h"
#include <misc/loader.h>
#include <khtmlview.h>
#include <khtml_part.h>
#include <xml/dom_docimpl.h>
#include <css/csshelper.h>
#include <ecma/kjs_proxy.h>
#include <kcharsets.h>
#include <ctype.h>
#include <assert.h>
#include <QtCore/QVariant>
#include <kdebug.h>
#include <stdlib.h>
#include "kentities.c"
#include "htmlprospectivetokenizer.h"
#define PROSPECTIVE_TOKENIZER_ENABLED 1
using namespace khtml;
static const QChar commentStart [] = { '<','!','-','-', QChar::Null };
static const char doctypeStart [] = "<!doctype";
static const char publicStart [] = "public";
static const char systemStart [] = "system";
static const char scriptEnd [] = "</script";
static const char xmpEnd [] = "</xmp";
static const char styleEnd [] = "</style";
static const char textareaEnd [] = "</textarea";
static const char titleEnd [] = "</title";
#ifndef NDEBUG
static const int sTokenizerChunkSize = 2048;
static const int sTokenizerFastYieldDelay = 220;
static const int sTokenizerYieldDelay = 650;
#else
static const int sTokenizerChunkSize = 4096;
static const int sTokenizerFastYieldDelay = 180;
static const int sTokenizerYieldDelay = 450;
#endif
#define KHTML_ALLOC_QCHAR_VEC( N ) (QChar*) malloc( sizeof(QChar)*( N ) )
#define KHTML_REALLOC_QCHAR_VEC(P, N ) (QChar*) realloc(P, sizeof(QChar)*( N ))
#define KHTML_DELETE_QCHAR_VEC( P ) free((char*)( P ))
// Full support for MS Windows extensions to Latin-1.
// Technically these extensions should only be activated for pages
// marked "windows-1252" or "cp1252", but
// in the standard Microsoft way, these extensions infect hundreds of thousands
// of web pages. Note that people with non-latin-1 Microsoft extensions
// are SOL.
//
// See: http://www.microsoft.com/globaldev/reference/WinCP.asp
// http://www.bbsinc.com/iso8859.html
// http://www.obviously.com/
//
// There may be better equivalents
#if 0
#define fixUpChar(x)
#else
#define fixUpChar(x) \
switch ((x).unicode()) \
{ \
case 0x80: (x) = 0x20ac; break; \
case 0x82: (x) = 0x201a; break; \
case 0x83: (x) = 0x0192; break; \
case 0x84: (x) = 0x201e; break; \
case 0x85: (x) = 0x2026; break; \
case 0x86: (x) = 0x2020; break; \
case 0x87: (x) = 0x2021; break; \
case 0x88: (x) = 0x02C6; break; \
case 0x89: (x) = 0x2030; break; \
case 0x8A: (x) = 0x0160; break; \
case 0x8b: (x) = 0x2039; break; \
case 0x8C: (x) = 0x0152; break; \
case 0x8E: (x) = 0x017D; break; \
case 0x91: (x) = 0x2018; break; \
case 0x92: (x) = 0x2019; break; \
case 0x93: (x) = 0x201C; break; \
case 0x94: (x) = 0X201D; break; \
case 0x95: (x) = 0x2022; break; \
case 0x96: (x) = 0x2013; break; \
case 0x97: (x) = 0x2014; break; \
case 0x98: (x) = 0x02DC; break; \
case 0x99: (x) = 0x2122; break; \
case 0x9A: (x) = 0x0161; break; \
case 0x9b: (x) = 0x203A; break; \
case 0x9C: (x) = 0x0153; break; \
case 0x9E: (x) = 0x017E; break; \
case 0x9F: (x) = 0x0178; break; \
default: break; \
}
#endif
// ----------------------------------------------------------------------------
HTMLTokenizer::HTMLTokenizer(DOM::DocumentImpl *_doc, KHTMLView *_view)
{
view = _view;
buffer = 0;
rawContent = 0;
rawContentSize = rawContentMaxSize = rawContentResync = rawContentSinceLastEntity = 0;
charsets = KCharsets::charsets();
parser = new KHTMLParser(_view, _doc);
m_executingScript = 0;
m_externalScriptsTimerId = 0;
m_tokenizerYieldDelay = sTokenizerFastYieldDelay;
m_yieldTimer = 0;
m_prospectiveTokenizer = 0;
onHold = false;
m_documentTokenizer = true;
m_hasScriptsWaitingForStylesheets = false;
reset();
}
HTMLTokenizer::HTMLTokenizer(DOM::DocumentImpl *_doc, DOM::DocumentFragmentImpl *i)
{
view = 0;
buffer = 0;
rawContent = 0;
rawContentSize = rawContentMaxSize = rawContentResync = rawContentSinceLastEntity = 0;
charsets = KCharsets::charsets();
parser = new KHTMLParser( i, _doc );
m_executingScript = 0;
m_externalScriptsTimerId = 0;
m_tokenizerYieldDelay = sTokenizerFastYieldDelay;
m_yieldTimer = 0;
m_prospectiveTokenizer = 0;
onHold = false;
m_documentTokenizer = false;
m_hasScriptsWaitingForStylesheets = false;
reset();
}
void HTMLTokenizer::setNormalYieldDelay()
{
m_tokenizerYieldDelay = sTokenizerYieldDelay;
}
void HTMLTokenizer::reset()
{
assert(m_executingScript == 0);
Q_ASSERT(onHold == false);
m_abort = false;
while (!cachedScript.isEmpty())
cachedScript.dequeue()->deref(this);
if ( buffer )
KHTML_DELETE_QCHAR_VEC(buffer);
buffer = dest = 0;
size = 0;
if ( rawContent )
KHTML_DELETE_QCHAR_VEC(rawContent);
rawContent = 0;
rawContentSize = rawContentMaxSize = rawContentResync = 0;
if (m_yieldTimer > 0) {
killTimer(m_yieldTimer);
m_yieldTimer = 0;
}
if (m_externalScriptsTimerId > 0) {
killTimer(m_externalScriptsTimerId);
m_externalScriptsTimerId = 0;
}
currToken.reset();
doctypeToken.reset();
javascript = false;
}
void HTMLTokenizer::begin()
{
m_executingScript = 0;
onHold = false;
reset();
size = 254;
buffer = KHTML_ALLOC_QCHAR_VEC( 255 );
dest = buffer;
tag = NoTag;
pending = NonePending;
discard = NoneDiscard;
pre = false;
prePos = 0;
plaintext = false;
xmp = false;
processingInstruction = false;
script = false;
escaped = false;
style = false;
skipLF = false;
select = false;
comment = false;
doctype = false;
doctypeComment = NoDoctypeComment;
doctypeAllowComment = false;
server = false;
textarea = false;
title = false;
startTag = false;
tquote = NoQuote;
searchCount = 0;
doctypeSearchCount = 0;
doctypeSecondarySearchCount = 0;
Entity = NoEntity;
noMoreData = false;
brokenComments = false;
brokenServer = false;
lineno = 0;
scriptStartLineno = 0;
tagStartLineno = 0;
}
void HTMLTokenizer::processListing(TokenizerString list)
{
bool old_pre = pre;
// This function adds the listing 'list' as
// preformatted text-tokens to the token-collection
// thereby converting TABs.
if(!style) pre = true;
prePos = 0;
while ( !list.isEmpty() )
{
checkBuffer(3*TAB_SIZE);
if (skipLF && ( list->unicode() != '\n' ))
{
skipLF = false;
}
if (skipLF)
{
skipLF = false;
++list;
}
else if (( list->unicode() == '\n' ) || ( list->unicode() == '\r' ))
{
if (discard == LFDiscard)
{
// Ignore this LF
discard = NoneDiscard; // We have discarded 1 LF
}
else
{
// Process this LF
if (pending)
addPending();
// we used to do it not at all and we want to have
// it fixed for textarea. So here we are
if ( textarea ) {
prePos++;
*dest++ = *list;
} else
pending = LFPending;
}
/* Check for MS-DOS CRLF sequence */
if (list->unicode() == '\r')
{
skipLF = true;
}
++list;
}
else if (( list->unicode() == ' ' ) || ( list->unicode() == '\t'))
{
if (pending)
addPending();
if (*list == ' ')
pending = SpacePending;
else
pending = TabPending;
++list;
}
else
{
discard = NoneDiscard;
if (pending)
addPending();
prePos++;
*dest++ = *list;
++list;
}
}
if ((pending == SpacePending) || (pending == TabPending))
addPending();
else
pending = NonePending;
prePos = 0;
pre = old_pre;
}
void HTMLTokenizer::parseRawContent(TokenizerString &src)
{
// The 'raw content' mode is a very lax tokenizing mode
// that will absorb anything but the exact closing tag
// that made us enter this mode, *except* if it inside a comment.
//
// Any other tag or comment will be passed verbatim to the parser as part
// of the content. It is used for script, style, and a few others.
//
assert( textarea || title || !Entity );
assert( !tag );
assert( xmp+textarea+title+style+script == 1 );
if (script)
scriptStartLineno = lineno+src.lineCount();
if ( comment ) parseComment( src );
while ( !src.isEmpty() ) {
checkRawContentBuffer();
unsigned char ch = src->toLatin1();
if ( !rawContentResync && !brokenComments && !xmp && ch == '-' &&
rawContentSize >= 3 && ((!textarea && !title) || rawContentSinceLastEntity >= 3) && !src.escaped() &&
QString::fromRawData( rawContent+rawContentSize-3, 3 ) == "<!-" ) {
comment = true;
rawContent[ rawContentSize++ ] = ch;
++src;
parseComment( src );
continue;
}
if ( rawContentResync && !tquote && ( ch == '>' ) ) {
++src;
rawContentSize = rawContentResync-1;
rawContentResync = 0;
rawContent[ rawContentSize ] = rawContent[ rawContentSize + 1 ] = 0;
if ( script )
scriptHandler();
else {
processListing(TokenizerString(rawContent, rawContentSize));
processToken();
if ( style ) { currToken.tid = ID_STYLE + ID_CLOSE_TAG; }
else if ( textarea ) { currToken.tid = ID_TEXTAREA + ID_CLOSE_TAG; }
else if ( title ) { currToken.tid = ID_TITLE + ID_CLOSE_TAG; }
else if ( xmp ) { currToken.tid = ID_XMP + ID_CLOSE_TAG; }
processToken();
script = style = textarea = title = xmp = false;
tquote = NoQuote;
rawContentSize = rawContentResync = 0;
}
return;
}
// possible end of tagname, lets check.
if ( !rawContentResync && !escaped && !src.escaped() && ( ch == '>' || ch == '/' || ch <= ' ' ) && ch &&
rawContentSize >= searchStopperLen && ((!textarea && !title) || rawContentSinceLastEntity >= searchStopperLen) &&
QString::compare(QString::fromRawData(rawContent + rawContentSize - searchStopperLen, searchStopperLen),
QLatin1String(searchStopper), Qt::CaseInsensitive) == 0) {
// the purpose of rawContentResync is to look for an end tag that could possibly be of the form:
// </endtag junk="more junk>\"><>" >
// IOW, once the '</endtag' sequence has been found, the rest of the tag must still be validated,
// so this micro-tokenizer switches to rawContentResync state until '>' is finally found.
rawContentResync = rawContentSize-searchStopperLen+1;
tquote = NoQuote;
continue;
}
if ( rawContentResync && !escaped ) {
if(ch == '\"')
tquote = (tquote == NoQuote) ? DoubleQuote : ((tquote == SingleQuote) ? SingleQuote : NoQuote);
else if(ch == '\'')
tquote = (tquote == NoQuote) ? SingleQuote : (tquote == DoubleQuote) ? DoubleQuote : NoQuote;
else if (tquote != NoQuote && (ch == '\r' || ch == '\n'))
tquote = NoQuote;
}
escaped = ( !escaped && ch == '\\' );
if (!rawContentResync && (textarea||title) && !src.escaped() && ch == '&') {
QChar *rawContentDest = rawContent+rawContentSize;
++src;
parseEntity(src,rawContentDest,true);
rawContentSize = rawContentDest-rawContent;
}
else {
rawContent[ rawContentSize++ ] = *src;
++src;
++rawContentSinceLastEntity;
}
}
}
void HTMLTokenizer::scriptHandler()
{
QString currentScriptSrc = scriptSrc;
scriptSrc.clear();
processListing(TokenizerString(rawContent, rawContentSize));
QString exScript( buffer, dest-buffer );
processToken();
currToken.tid = ID_SCRIPT + ID_CLOSE_TAG;
processToken();
// Scripts following a frameset element should not be executed or even loaded in the case of extern scripts.
bool followingFrameset = (parser->doc()->body() && parser->doc()->body()->id() == ID_FRAMESET);
bool effectiveScript = !parser->skipMode() && !followingFrameset;
bool deferredScript = false;
if ( effectiveScript ) {
CachedScript* cs = 0;
// forget what we just got, load from src url instead
if ( !currentScriptSrc.isEmpty() && javascript &&
(cs = parser->doc()->docLoader()->requestScript(currentScriptSrc, scriptSrcCharset) )) {
cachedScript.enqueue(cs);
}
if (cs) {
pendingQueue.push(src);
int scriptCount = cachedScript.count();
setSrc(TokenizerString());
rawContentSize = rawContentResync = 0;
cs->ref(this);
if (cachedScript.count() == scriptCount)
deferredScript = true;
}
else if (currentScriptSrc.isEmpty() && view && javascript ) {
pendingQueue.push(src);
setSrc(TokenizerString());
rawContentSize = rawContentResync = 0;
scriptExecution( exScript, QString(), tagStartLineno /*scriptStartLineno*/ );
} else {
// script was filtered or disallowed
effectiveScript = false;
}
}
script = false;
rawContentSize = rawContentResync = 0;
if ( !effectiveScript )
return;
if ( !m_executingScript && cachedScript.isEmpty() ) {
src.append(pendingQueue.pop());
} else if ( cachedScript.isEmpty() ) {
write( pendingQueue.pop(), false );
} else if ( !deferredScript && pendingQueue.count() > 1) {
TokenizerString t = pendingQueue.pop();
pendingQueue.top().prepend( t );
}
#if PROSPECTIVE_TOKENIZER_ENABLED
if (!cachedScript.isEmpty() && !m_executingScript) {
if (!m_prospectiveTokenizer)
m_prospectiveTokenizer = new ProspectiveTokenizer(parser->docPtr());
if (!m_prospectiveTokenizer->inProgress() && !pendingQueue.isEmpty()) {
m_prospectiveTokenizer->begin();
m_prospectiveTokenizer->write(pendingQueue.top());
}
}
#endif
}
void HTMLTokenizer::scriptExecution( const QString& str, const QString& scriptURL,
int baseLine)
{
bool oldscript = script;
m_executingScript++;
script = false;
QString url;
if (scriptURL.isNull() && view)
url = static_cast<DocumentImpl*>(view->part()->document().handle())->URL().url();
else
url = scriptURL;
if (view)
view->part()->executeScript(url,baseLine,Node(),str);
m_executingScript--;
script = oldscript;
}
void HTMLTokenizer::parseComment(TokenizerString &src)
{
checkRawContentBuffer(src.length());
while ( src.length() ) {
rawContent[ rawContentSize++ ] = *src;
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("comment is now: *%s*", src.toString().left(16).toLatin1().constData());
#endif
if (src->unicode() == '>')
{
bool handleBrokenComments = brokenComments && !( script || style );
bool scriptEnd=false;
if ( rawContentSize > 2 && rawContent[rawContentSize-3] == '-' &&
rawContent[rawContentSize-2] == '-' )
{
scriptEnd=true;
}
if (handleBrokenComments || scriptEnd ){
++src;
if ( !( title || script || xmp || textarea || style) ) {
checkRawContentBuffer();
rawContent[ rawContentSize ] = 0;
rawContent[ rawContentSize + 1 ] = 0;
currToken.tid = ID_COMMENT;
int size = scriptEnd ? rawContentSize - 3 : rawContentSize - 1;
processListing(TokenizerString(rawContent, size));
processToken();
currToken.tid = ID_COMMENT + ID_CLOSE_TAG;
processToken();
rawContentSize = 0;
}
comment = false;
return; // Finished parsing comment
}
}
++src;
}
}
void HTMLTokenizer::parseDoctypeComment(TokenizerString &src)
{
while (!src.isEmpty()) {
QChar c = *src;
switch (doctypeComment) {
case DoctypeCommentHalfBegin: {
if (c != '-') {
// Ooops, it's not comment
doctypeComment = DoctypeCommentBogus;
return;
} else {
// Doctype comment begins
doctypeComment = DoctypeComment;
++src;
}
break;
}
case DoctypeComment: {
if (c == '-') {
// Perhaps this is end of comment
doctypeComment = DoctypeCommentHalfEnd;
++src;
} else {
// Keep scanning for '--'
++src;
}
break;
}
case DoctypeCommentHalfEnd: {
if (c == '-') {
// Doctype comment ends
doctypeComment = DoctypeCommentEnd;
return;
} else {
// It's not '--'
++src;
doctypeComment = DoctypeComment;
}
break;
}
default: {
assert(!"Undefined doctype comment state");
break;
}
}
}
}
void HTMLTokenizer::parseDoctype(TokenizerString &src)
{
while (!src.isEmpty() && doctype) {
QChar c;
bool isWhitespace = false;
int dontAdvance = 0;
if (doctypeComment == DoctypeCommentEnd) {
doctypeComment = NoDoctypeComment;
isWhitespace = true;
} else if (doctypeComment == DoctypeCommentBogus) {
doctypeComment = NoDoctypeComment;
c = '-';
dontAdvance++;
} else {
c = *src;
if (doctypeAllowComment) {
if (!doctypeComment && c == '-') {
doctypeComment = DoctypeCommentHalfBegin;
++src;
}
if (doctypeComment) {
parseDoctypeComment(src);
continue;
}
isWhitespace = c == '\r' || c == '\n' || c == '\t' || c == ' ';
}
}
switch (doctypeToken.state) {
case DoctypeBegin: {
doctypeToken.state = DoctypeBeforeName;
if (isWhitespace) {
// nothing
}
break;
}
case DoctypeBeforeName: {
if (c == '>') {
// Malformed. Just exit.
doctype = false;
} else if (isWhitespace) {
// nothing
} else {
dontAdvance++;
doctypeToken.state = DoctypeName;
}
break;
}
case DoctypeName: {
if (c == '>') {
// Valid doctype. Emit it.
doctype = false;
processDoctypeToken();
} else if (isWhitespace) {
doctypeSearchCount = 0; // Used now to scan for PUBLIC
doctypeSecondarySearchCount = 0; // Used now to scan for SYSTEM
doctypeToken.state = DoctypeAfterName;
} else {
doctypeToken.name.append(c);
}
break;
}
case DoctypeAfterName: {
if (c == '>') {
// Valid doctype. Emit it.
doctype = false;
processDoctypeToken();
} else if (c == '[') {
if(doctypeSearchCount > 0 || doctypeSecondarySearchCount > 0) { // is there any public/system indicator before?
doctypeSearchCount = doctypeSecondarySearchCount = 0;
doctypeToken.state = DoctypeBogus;
}
// Found internal subset
doctypeToken.state = DoctypeInternalSubset;
doctypeAllowComment = false;
} else if (!isWhitespace) {
if (c.toLower() == publicStart[doctypeSearchCount]) {
doctypeSearchCount++;
if(doctypeSearchCount == 6)
// Found 'PUBLIC' sequence
doctypeToken.state = DoctypeBeforePublicID;
} else if (doctypeSearchCount > 0) {
doctypeSearchCount = 0;
doctypeToken.state = DoctypeBogus;
} else if (c.toLower() == systemStart[doctypeSecondarySearchCount]) {
doctypeSecondarySearchCount++;
if(doctypeSecondarySearchCount == 6)
// Found 'SYSTEM' sequence
doctypeToken.state = DoctypeBeforeSystemID;
} else {
doctypeSecondarySearchCount = 0;
doctypeToken.state = DoctypeBogus;
}
} else {
// Whitespace keeps us in the after name state
}
break;
}
case DoctypeBeforePublicID: {
if (c == '\"' || c == '\'') {
tquote = c == '\"' ? DoubleQuote : SingleQuote;
doctypeToken.state = DoctypePublicID;
doctypeAllowComment = false;
} else if (c == '>') {
// Considered bogus. Don't process the doctype.
doctype = false;
} else if (isWhitespace) {
// nothing
} else
doctypeToken.state = DoctypeBogus;
break;
}
case DoctypePublicID: {
if ((c == '\"' && tquote == DoubleQuote) || (c == '\'' && tquote == SingleQuote)) {
doctypeToken.state = DoctypeAfterPublicID;
doctypeAllowComment = true;
} else if (c == '>') {
// Considered bogus. Don't process the doctype.
doctype = false;
} else {
doctypeToken.publicID.append(c);
}
break;
}
case DoctypeAfterPublicID: {
if (c == '\"' || c == '\'') {
tquote = c == '\"' ? DoubleQuote : SingleQuote;
doctypeToken.state = DoctypeSystemID;
} else if (c == '>') {
// Valid doctype. Emit it now.
doctype = false;
processDoctypeToken();
} else if (isWhitespace) {
// nothing
} else if (c == '[') {
// Found internal subset
doctypeToken.state = DoctypeInternalSubset;
doctypeAllowComment = false;
} else
doctypeToken.state = DoctypeBogus;
break;
}
case DoctypeBeforeSystemID: {
if (c == '\"' || c == '\'') {
tquote = c == '\"' ? DoubleQuote : SingleQuote;
doctypeToken.state = DoctypeSystemID;
doctypeAllowComment = false;
} else if (c == '>') {
// Considered bogus. Don't process the doctype.
doctype = false;
} else if (isWhitespace) {
// nothing
} else
doctypeToken.state = DoctypeBogus;
break;
}
case DoctypeSystemID: {
if ((c == '\"' && tquote == DoubleQuote) || (c == '\'' && tquote == SingleQuote)) {
doctypeToken.state = DoctypeAfterSystemID;
doctypeAllowComment = true;
} else if (c == '>') {
// Considered bogus. Don't process the doctype.
doctype = false;
} else {
doctypeToken.systemID.append(c);
}
break;
}
case DoctypeAfterSystemID: {
if (c == '>') {
// Valid doctype. Emit it now.
doctype = false;
processDoctypeToken();
} else if (isWhitespace) {
// nothing
} else if (c == '[') {
// Found internal subset
doctypeToken.state = DoctypeInternalSubset;
doctypeAllowComment = false;
} else {
doctypeToken.state = DoctypeBogus;
}
break;
}
case DoctypeInternalSubset: {
if(c == ']') {
// Done
doctypeToken.state = DoctypeAfterInternalSubset;
doctypeAllowComment = true;
} else {
doctypeToken.internalSubset.append(c);
}
break;
}
case DoctypeAfterInternalSubset: {
if (c == '>') {
// Valid doctype. Emit it now.
doctype = false;
processDoctypeToken();
} else if (isWhitespace) {
// nothing
} else
doctypeToken.state = DoctypeBogus;
break;
}
case DoctypeBogus: {
if (c == '>') {
// Done with the bogus doctype.
doctype = false;
} else {
// Just keep scanning for '>'
}
break;
}
default:
break;
}
if (!dontAdvance)
++src;
else if (dontAdvance == 1)
continue;
else // double dontAdvance++, do workaround
doctypeComment = DoctypeCommentBogus;
}
}
void HTMLTokenizer::parseServer(TokenizerString &src)
{
checkRawContentBuffer(src.length());
while ( !src.isEmpty() ) {
rawContent[ rawContentSize++ ] = *src;
if (src->unicode() == '>' &&
rawContentSize > 1 && rawContent[rawContentSize-2] == '%') {
++src;
server = false;
rawContentSize = 0;
return; // Finished parsing server include
}
++src;
}
}
void HTMLTokenizer::parseProcessingInstruction(TokenizerString &src)
{
char oldchar = 0;
while ( !src.isEmpty() )
{
unsigned char chbegin = src->toLatin1();
if(chbegin == '\'') {
tquote = tquote == SingleQuote ? NoQuote : SingleQuote;
}
else if(chbegin == '\"') {
tquote = tquote == DoubleQuote ? NoQuote : DoubleQuote;
}
// Look for '?>'
// some crappy sites omit the "?" before it, so
// we look for an unquoted '>' instead. (IE compatible)
else if ( chbegin == '>' && ( !tquote || oldchar == '?' ) )
{
// We got a '?>' sequence
processingInstruction = false;
++src;
discard=LFDiscard;
return; // Finished parsing comment!
}
++src;
oldchar = chbegin;
}
}
void HTMLTokenizer::parseText(TokenizerString &src)
{
while ( !src.isEmpty() )
{
// do we need to enlarge the buffer?
checkBuffer();
// ascii is okay because we only do ascii comparisons
unsigned char chbegin = src->toLatin1();
if (skipLF && ( chbegin != '\n' ))
{
skipLF = false;
}
if (skipLF)
{
skipLF = false;
++src;
}
else if (( chbegin == '\n' ) || ( chbegin == '\r' ))
{
if (chbegin == '\r')
skipLF = true;
*dest++ = '\n';
++src;
}
else {
*dest++ = *src;
++src;
}
}
}
void HTMLTokenizer::parseEntity(TokenizerString &src, QChar *&dest, bool start)
{
if( start )
{
cBufferPos = 0;
entityLen = 0;
Entity = SearchEntity;
}
while( !src.isEmpty() )
{
ushort cc = src->unicode();
switch(Entity) {
case NoEntity:
return;
break;
case SearchEntity:
if(cc == '#') {
cBuffer[cBufferPos++] = cc;
++src;
Entity = NumericSearch;
}
else
Entity = EntityName;
break;
case NumericSearch:
if(cc == 'x' || cc == 'X') {
cBuffer[cBufferPos++] = cc;
++src;
Entity = Hexadecimal;
}
else if(cc >= '0' && cc <= '9')
Entity = Decimal;
else
Entity = SearchSemicolon;
break;
case Hexadecimal:
{
int uc = EntityChar.unicode();
int ll = qMin<uint>(src.length(), 8);
while(ll--) {
QChar csrc(src->toLower());
cc = csrc.cell();
if(csrc.row() || !((cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'f'))) {
break;
}
uc = uc*16 + (cc - ( cc < 'a' ? '0' : 'a' - 10));
cBuffer[cBufferPos++] = cc;
++src;
}
EntityChar = QChar(uc);
Entity = SearchSemicolon;
break;
}
case Decimal:
{
int uc = EntityChar.unicode();
int ll = qMin(src.length(), 9-cBufferPos);
while(ll--) {
cc = src->cell();
if(src->row() || !(cc >= '0' && cc <= '9')) {
Entity = SearchSemicolon;
break;
}
uc = uc * 10 + (cc - '0');
cBuffer[cBufferPos++] = cc;
++src;
}
EntityChar = QChar(uc);
if(cBufferPos == 9) Entity = SearchSemicolon;
break;
}
case EntityName:
{
int ll = qMin(src.length(), 9-cBufferPos);
while(ll--) {
QChar csrc = *src;
cc = csrc.cell();
if(csrc.row() || !((cc >= 'a' && cc <= 'z') ||
(cc >= '0' && cc <= '9') || (cc >= 'A' && cc <= 'Z'))) {
Entity = SearchSemicolon;
break;
}
cBuffer[cBufferPos++] = cc;
++src;
// be IE compatible and interpret even unterminated entities
// outside tags. like "foo &nbspstuff bla".
if ( tag == NoTag ) {
const entity* e = kde_findEntity(cBuffer, cBufferPos);
if ( e && e->code < 256 ) {
EntityChar = e->code;
entityLen = cBufferPos;
}
}
}
if(cBufferPos == 9) Entity = SearchSemicolon;
if(Entity == SearchSemicolon) {
if(cBufferPos > 1) {
const entity *e = kde_findEntity(cBuffer, cBufferPos);
// IE only accepts unterminated entities < 256,
// Gecko accepts them all, but only outside tags
if(e && ( tag == NoTag || e->code < 256 || *src == ';' )) {
EntityChar = e->code;
entityLen = cBufferPos;
}
}
}
break;
}
case SearchSemicolon:
#ifdef TOKEN_DEBUG
kDebug( 6036 ) << "ENTITY " << EntityChar.unicode();
#endif
fixUpChar(EntityChar);
if (*src == ';')
++src;
if ( !EntityChar.isNull() ) {
checkBuffer();
if (entityLen > 0 && entityLen < cBufferPos) {
int rem = cBufferPos - entityLen;
- src.prepend( TokenizerString(QString::fromAscii(cBuffer+entityLen, rem)) );
+ src.prepend( TokenizerString(QString::fromLatin1(cBuffer+entityLen, rem)) );
}
src.push( EntityChar );
rawContentSinceLastEntity = -1;
} else {
#ifdef TOKEN_DEBUG
kDebug( 6036 ) << "unknown entity!";
#endif
checkBuffer(11);
// ignore the sequence, add it to the buffer as plaintext
*dest++ = '&';
for(unsigned int i = 0; i < cBufferPos; i++)
dest[i] = cBuffer[i];
dest += cBufferPos;
rawContentSinceLastEntity += cBufferPos+1;
if (pre)
prePos += cBufferPos+1;
}
Entity = NoEntity;
EntityChar = QChar::Null;
return;
};
}
}
void HTMLTokenizer::parseTag(TokenizerString &src)
{
assert(!Entity );
checkRawContentBuffer( src.length() );
while ( !src.isEmpty() )
{
checkBuffer();
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
uint l = 0;
while(l < src.length() && (src.toString()[l]).toLatin1().constData() != '>')
l++;
qDebug("src is now: *%s*, tquote: %d",
src.toString().left(l).toLatin1().constData(), tquote);
#endif
switch(tag) {
case NoTag:
return;
case TagName:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("TagName");
#endif
if (searchCount > 0)
{
if (*src == commentStart[searchCount])
{
searchCount++;
if (searchCount == 2)
doctypeSearchCount++; // A '!' is also part of doctype, so we are moving through that still as well
else
doctypeSearchCount = 0;
if (searchCount == 4)
{
#ifdef TOKEN_DEBUG
kDebug( 6036 ) << "Found comment";
#endif
// Found '<!--' sequence
++src;
dest = buffer; // ignore the previous part of this tag
tag = NoTag;
comment = true;
parseComment(src);
return; // Finished parsing tag!
}
// cuts of high part, is okay
cBuffer[cBufferPos++] = src->cell();
++src;
break;
}
else
searchCount = 0; // Stop looking for '<!--' sequence
}
if (doctypeSearchCount > 0) {
if((*src).toLower() == doctypeStart[doctypeSearchCount]) {
doctypeSearchCount++;
cBuffer[cBufferPos++] = src->cell();
++src;
if(doctypeSearchCount == 9) {
// Found '<!DOCTYPE' sequence
tag = NoTag;
doctypeAllowComment = true;
doctypeComment = NoDoctypeComment;
doctypeToken.reset();
doctype = true;
parseDoctype(src);
return;
}
break;
} else
doctypeSearchCount = 0; // Stop looking for '<!DOCTYPE' sequence
}
bool finish = false;
unsigned int ll = qMin(src.length(), CBUFLEN-cBufferPos);
while(ll--) {
ushort curchar = src->unicode();
if(curchar <= ' ' || curchar == '>' ) {
finish = true;
break;
}
// this is a nasty performance trick. will work for the A-Z
// characters, but not for others. if it contains one,
// we fail anyway
char cc = curchar;
cBuffer[cBufferPos++] = cc | 0x20;
++src;
}
// Disadvantage: we add the possible rest of the tag
// as attribute names. ### judge if this causes problems
if(finish || CBUFLEN == cBufferPos) {
bool beginTag;
char* ptr = cBuffer;
unsigned int len = cBufferPos;
cBuffer[cBufferPos] = '\0';
if ((cBufferPos > 0) && (*ptr == '/'))
{
// End Tag
beginTag = false;
ptr++;
len--;
}
else
// Start Tag
beginTag = true;
// Accept empty xml tags like <br/>
if(len > 1 && ptr[len-1] == '/' ) {
ptr[--len] = '\0';
// if it is like <br/> and not like <input/ value=foo>, take it as flat
if (*src == '>')
currToken.flat = true;
}
uint tagID = 0;
if (!tagID) {
DOMString tagName(ptr);
if (Element::khtmlValidQualifiedName(tagName)) {
safeLocalName = LocalName::fromString(tagName, IDS_NormalizeLower);
tagID = safeLocalName.id();
}
#ifdef TOKEN_DEBUG
QByteArray tmp(ptr, len+1);
kDebug( 6036 ) << "Unknown tag: \"" << tmp.data() << "\"";
#endif
}
if (tagID) {
#ifdef TOKEN_DEBUG
QByteArray tmp(ptr, len+1);
kDebug( 6036 ) << "found tag id=" << tagID << ": " << tmp.data();
#endif
currToken.tid = beginTag ? tagID : tagID + ID_CLOSE_TAG;
}
dest = buffer;
tag = SearchAttribute;
cBufferPos = 0;
}
break;
}
case SearchAttribute:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("SearchAttribute");
#endif
bool atespace = false;
ushort curchar;
while(!src.isEmpty()) {
curchar = src->unicode();
if(curchar > ' ') {
if(curchar == '<' || curchar == '>')
tag = SearchEnd;
else if(atespace && (curchar == '\'' || curchar == '"'))
{
tag = SearchValue;
*dest++ = 0;
attrName = DOMString("");
}
else
tag = AttributeName;
cBufferPos = 0;
break;
}
atespace = true;
++src;
}
break;
}
case AttributeName:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("AttributeName");
#endif
ushort curchar;
int ll = qMin(src.length(), CBUFLEN-cBufferPos);
while(ll--) {
curchar = src->unicode();
if(curchar <= '>') {
if(curchar <= ' ' || curchar == '=' || curchar == '>') {
unsigned int a;
cBuffer[cBufferPos] = '\0';
a = LocalName::fromString(DOMString(cBuffer), IDS_NormalizeLower).id(); // ### still deep copy?
if (a > ATTR_LAST_ATTR)
a = 0;
if ( !a ) {
// did we just get /> or e.g checked/>
if (curchar == '>' && cBufferPos >=1 && cBuffer[cBufferPos-1] == '/') {
currToken.flat = true;
cBuffer[cBufferPos - 1] = '\0';
if (cBufferPos>1)
a = LocalName::fromString(DOMString(cBuffer), IDS_NormalizeLower).id();
if (a > ATTR_LAST_ATTR)
a = 0;
cBuffer[cBufferPos - 1] = '/';
}
if (!a)
attrName = DOMString(cBuffer, cBufferPos);
}
dest = buffer;
*dest++ = a;
#ifdef TOKEN_DEBUG
if (!a || (cBufferPos && *cBuffer == '!'))
kDebug( 6036 ) << "Unknown attribute: *" << QByteArray(cBuffer, cBufferPos+1).data() << "*";
else
kDebug( 6036 ) << "Known attribute: " << QByteArray(cBuffer, cBufferPos+1).data();
#endif
tag = SearchEqual;
break;
}
}
cBuffer[cBufferPos++] =
( curchar >= 'A' && curchar <= 'Z' ) ? curchar | 0x20 : curchar;
++src;
}
if ( cBufferPos == CBUFLEN ) {
cBuffer[cBufferPos] = '\0';
attrName = DOMString(cBuffer, cBufferPos);
dest = buffer;
*dest++ = 0;
tag = SearchEqual;
}
break;
}
case SearchEqual:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("SearchEqual");
#endif
ushort curchar;
bool atespace = false;
while(!src.isEmpty()) {
curchar = src->unicode();
if(curchar > ' ') {
if(curchar == '=') {
#ifdef TOKEN_DEBUG
kDebug(6036) << "found equal";
#endif
tag = SearchValue;
++src;
}
else if(atespace && (curchar == '\'' || curchar == '"'))
{
tag = SearchValue;
*dest++ = 0;
attrName = DOMString("");
}
else {
DOMString v("");
currToken.addAttribute(parser->docPtr(), buffer, attrName, v);
dest = buffer;
tag = SearchAttribute;
}
break;
}
atespace = true;
++src;
}
break;
}
case SearchValue:
{
ushort curchar;
while(!src.isEmpty()) {
curchar = src->unicode();
if(curchar > ' ') {
if(( curchar == '\'' || curchar == '\"' )) {
tquote = curchar == '\"' ? DoubleQuote : SingleQuote;
tag = QuotedValue;
++src;
} else
tag = Value;
break;
}
++src;
}
break;
}
case QuotedValue:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("QuotedValue");
#endif
ushort curchar;
while(!src.isEmpty()) {
checkBuffer();
curchar = src->unicode();
if(curchar <= '\'' && !src.escaped()) {
// ### attributes like '&{blaa....};' are supposed to be treated as jscript.
if ( curchar == '&' )
{
++src;
parseEntity(src, dest, true);
break;
}
else if ( (tquote == SingleQuote && curchar == '\'') ||
(tquote == DoubleQuote && curchar == '\"') )
{
// some <input type=hidden> rely on trailing spaces. argh
while(dest > buffer+1 && (*(dest-1) == '\n' || *(dest-1) == '\r'))
dest--; // remove trailing newlines
DOMString v(buffer+1, dest-buffer-1);
currToken.addAttribute(parser->docPtr(), buffer, attrName, v);
dest = buffer;
tag = SearchAttribute;
tquote = NoQuote;
++src;
break;
}
}
*dest++ = *src;
++src;
}
break;
}
case Value:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("Value");
#endif
ushort curchar;
while(!src.isEmpty()) {
checkBuffer();
curchar = src->unicode();
if(curchar <= '>' && !src.escaped()) {
// parse Entities
if ( curchar == '&' )
{
++src;
parseEntity(src, dest, true);
break;
}
// no quotes. Every space means end of value
// '/' does not delimit in IE!
// HTML5: must not contain any literal space characters, any U+0022 QUOTATION MARK (") characters,
// U+0027 APOSTROPHE (') characters, U+003D EQUALS SIGN (=) characters, U+003C LESS-THAN SIGN (<) characters,
// U+003E GREATER-THAN SIGN (>) characters, or U+0060 GRAVE ACCENT (`) characters, and must not be the empty string.
// Real life: images.google.com uses URLs including form arguments (foo=bar)
// in unquoted parameters --- with an html5 <!doctype html> DTD.
// Real life takes priority, so we accept at least =
if ( curchar <= ' ' || curchar == '>' || curchar == '\'' || curchar == '"' || curchar == '<' || /*curchar == '=' ||*/ curchar == '`' )
{
DOMString v(buffer+1, dest-buffer-1);
currToken.addAttribute(parser->docPtr(), buffer, attrName, v);
dest = buffer;
tag = SearchAttribute;
break;
}
}
*dest++ = *src;
++src;
}
break;
}
case SearchEnd:
{
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
qDebug("SearchEnd");
#endif
while(!src.isEmpty()) {
if(*src == '<' || *src == '>')
break;
if (*src == '/')
currToken.flat = true;
++src;
}
if(src.isEmpty() && *src != '<' && *src != '>') break;
searchCount = 0; // Stop looking for '<!--' sequence
tag = NoTag;
tquote = NoQuote;
if ( *src == '>' )
++src;
if ( !currToken.tid ) //stop if tag is unknown
return;
uint tagID = currToken.tid;
#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 0
kDebug( 6036 ) << "appending Tag: " << tagID;
#endif
// When parsing HTML flat tags like <div /> should
// be ignored, the only exception is SCRIPT, and
// tags with forbidden end-tags
if (tagID < ID_CLOSE_TAG && tagID != ID_SCRIPT &&
DOM::endTagRequirement(tagID) != DOM::FORBIDDEN &&
parser->doc()->htmlMode() != DocumentImpl::XHtml)
currToken.flat = false;
bool beginTag = !currToken.flat && (tagID < ID_CLOSE_TAG);
HTMLScriptElementImpl* prevScriptElem = 0;
if(tagID >= ID_CLOSE_TAG)
tagID -= ID_CLOSE_TAG;
else if ( tagID == ID_SCRIPT ) {
prevScriptElem = parser->currentScriptElement();
DOMStringImpl* a = 0;
scriptSrc.clear(); scriptSrcCharset.clear();
if ( currToken.attrs && /* potentially have a ATTR_SRC ? */
view && /* are we a regular tokenizer or just for innerHTML ? */
parser->doc()->view()->part()->jScriptEnabled() /* jscript allowed at all? */
) {
if ( ( a = currToken.attrs->getValue( ATTR_SRC ) ) )
scriptSrc = parser->doc()->completeURL(khtml::parseURL( DOMString(a) ).string() );
if ( ( a = currToken.attrs->getValue( ATTR_CHARSET ) ) )
scriptSrcCharset = DOMString(a).string().trimmed();
if ( scriptSrcCharset.isEmpty() && view)
scriptSrcCharset = parser->doc()->view()->part()->encoding();
}
javascript = true;
}
processToken();
if (javascript) {
HTMLScriptElementImpl* sc = parser->currentScriptElement();
javascript = (sc && sc != prevScriptElem) ? sc->isValidScript() : false;
}
if ( parser->selectMode() && beginTag)
discard = AllDiscard;
switch( tagID ) {
case ID_LISTING:
case ID_PRE:
pre = beginTag;
if (beginTag)
discard = LFDiscard;
prePos = 0;
break;
case ID_BR:
prePos = 0;
break;
case ID_SCRIPT:
if (beginTag) {
searchStopper = scriptEnd;
searchStopperLen = 8;
script = true;
parseRawContent(src);
}
else if (tagID < ID_CLOSE_TAG) { // Handle <script src="foo"/>
script = true;
scriptHandler();
}
break;
case ID_STYLE:
if (beginTag) {
searchStopper = styleEnd;
searchStopperLen = 7;
style = true;
parseRawContent(src);
}
break;
case ID_TEXTAREA:
if(beginTag) {
searchStopper = textareaEnd;
searchStopperLen = 10;
textarea = true;
discard = NoneDiscard;
rawContentSinceLastEntity = 0;
parseRawContent(src);
}
break;
case ID_TITLE:
if (beginTag) {
searchStopper = titleEnd;
searchStopperLen = 7;
title = true;
rawContentSinceLastEntity = 0;
parseRawContent(src);
}
break;
case ID_XMP:
if (beginTag) {
searchStopper = xmpEnd;
searchStopperLen = 5;
xmp = true;
parseRawContent(src);
}
break;
case ID_SELECT:
select = beginTag;
break;
case ID_PLAINTEXT:
plaintext = beginTag;
break;
}
return; // Finished parsing tag!
}
} // end switch
}
return;
}
void HTMLTokenizer::addPending()
{
if ( select && !(comment || script))
{
*dest++ = ' ';
}
else
{
switch(pending) {
case LFPending: *dest++ = QLatin1Char('\n'); prePos = 0; break;
case SpacePending: *dest++ = QLatin1Char(' '); ++prePos; break;
case TabPending: {
// Don't expand tabs inside <textarea> or script
int p = TAB_SIZE - ( prePos % TAB_SIZE );
if (textarea || script) {
*dest++ = QLatin1Char('\t');
} else {
for ( int x = 0; x < p; x++ )
*dest++ = QLatin1Char(' ');
}
prePos += p;
break;
}
case NonePending:
assert(0);
}
}
pending = NonePending;
}
inline bool HTMLTokenizer::continueProcessing(int& processedCount)
{
// We don't want to be checking elapsed time with every character, so we only check after we've
// processed a certain number of characters. We also do not do suspension if we're
// parsing something like innerHTML.
if (!m_executingScript && processedCount > sTokenizerChunkSize && cachedScript.isEmpty()) {
processedCount = 0;
if ( m_time.elapsed() > m_tokenizerYieldDelay && m_documentTokenizer) {
m_yieldTimer = startTimer(0);
m_tokenizerYieldDelay = sTokenizerFastYieldDelay;
return false;
}
}
processedCount++;
return true;
}
#include "khtmlpart_p.h"
void HTMLTokenizer::write( const TokenizerString &str, bool appendData )
{
#ifdef TOKEN_DEBUG
kDebug( 6036 ) << this << " Tokenizer::write(\"" << str.toString() << "\"," << appendData << ")";
#endif
if ( !buffer )
return;
if ( ( m_executingScript && appendData ) || cachedScript.count() ) {
// don't parse; we will do this later
if (pendingQueue.isEmpty())
pendingQueue.push(str);
else if (appendData)
pendingQueue.bottom().append(str);
else
pendingQueue.top().append(str);
#if PROSPECTIVE_TOKENIZER_ENABLED
if (m_prospectiveTokenizer && m_prospectiveTokenizer->inProgress() && appendData)
m_prospectiveTokenizer->write(str);
#endif
return;
}
#if PROSPECTIVE_TOKENIZER_ENABLED
if (m_prospectiveTokenizer && m_prospectiveTokenizer->inProgress() && appendData)
m_prospectiveTokenizer->end();
#endif
if ( onHold ) {
src.append(str);
return;
}
if (!src.isEmpty())
src.append(str);
else
setSrc(str);
// Once a timer is set, it has control of when the tokenizer continues.
if (m_yieldTimer > 0)
return;
int processedCount = 0;
m_time.start();
while ( !src.isEmpty() )
{
if ( m_abort || !continueProcessing(processedCount) )
break;
// do we need to enlarge the buffer?
checkBuffer();
ushort cc = src->unicode();
if (skipLF && (cc != '\n'))
skipLF = false;
if (skipLF) {
skipLF = false;
++src;
}
else if ( Entity )
parseEntity( src, dest );
else if ( plaintext )
parseText( src );
else if (script)
parseRawContent(src);
else if (style)
parseRawContent(src);
else if (xmp)
parseRawContent(src);
else if (textarea)
parseRawContent(src);
else if (title)
parseRawContent(src);
else if (comment)
parseComment(src);
else if (doctypeComment && doctypeComment != DoctypeCommentEnd && doctypeComment != DoctypeCommentBogus)
parseDoctypeComment(src);
else if (doctype)
parseDoctype(src);
else if (server)
parseServer(src);
else if (processingInstruction)
parseProcessingInstruction(src);
else if (tag)
parseTag(src);
else if ( startTag )
{
startTag = false;
switch(cc) {
case '/':
break;
case '!':
{
// <!-- comment --> or <!DOCTYPE ...>
searchCount = 1; // Look for '<!--' sequence to start comment...
doctypeSearchCount = 1; // ... or for '<!DOCTYPE' sequence to start doctype
break;
}
case '?':
{
// xml processing instruction
processingInstruction = true;
tquote = NoQuote;
parseProcessingInstruction(src);
continue;
}
case '%':
if (!brokenServer) {
// <% server stuff, handle as comment %>
server = true;
tquote = NoQuote;
parseServer(src);
continue;
}
// else fall through
default:
{
if( ((cc >= 'a') && (cc <= 'z')) || ((cc >= 'A') && (cc <= 'Z')))
{
// Start of a Start-Tag
}
else
{
// Invalid tag
// Add as is
if (pending)
addPending();
*dest = '<';
dest++;
continue;
}
}
}; // end case
// According to SGML any LF immediately after a starttag, or
// immediately before an endtag should be ignored.
// ### Gecko and MSIE though only ignores LF immediately after
// starttags and only for PRE elements -- asj (28/06-2005)
if ( pending )
{
if (!select)
{
addPending();
}
else
{
pending = NonePending;
}
}
// Cancel unused discards
discard = NoneDiscard;
// if (!endTag) discard = LFDiscard;
processToken();
cBufferPos = 0;
tag = TagName;
parseTag(src);
}
else if ( cc == '&' && !src.escaped())
{
++src;
if ( pending )
addPending();
discard = NoneDiscard;
parseEntity(src, dest, true);
}
else if ( cc == '<' && !src.escaped())
{
tagStartLineno = lineno+src.lineCount();
++src;
discard = NoneDiscard;
startTag = true;
}
else if (( cc == '\n' ) || ( cc == '\r' ))
{
if (discard == SpaceDiscard)
discard = NoneDiscard;
if (discard == LFDiscard) {
// Ignore one LF
discard = NoneDiscard;
}
else if (discard == AllDiscard)
{
// Ignore
}
else
{
if (select && !script) {
pending = LFPending;
} else {
if (pending)
addPending();
pending = LFPending;
}
}
/* Check for MS-DOS CRLF sequence */
if (cc == '\r')
{
skipLF = true;
}
++src;
}
else if (( cc == ' ' ) || ( cc == '\t' ))
{
if(discard == LFDiscard)
discard = NoneDiscard;
if(discard == SpaceDiscard) {
// Ignore one space
discard = NoneDiscard;
}
else if(discard == AllDiscard)
{
// Ignore
}
else {
if (select && !script) {
if (!pending)
pending = SpacePending;
} else {
if (pending)
addPending();
if (cc == ' ')
pending = SpacePending;
else
pending = TabPending;
}
}
++src;
}
else
{
if (pending)
addPending();
discard = NoneDiscard;
if ( pre )
{
prePos++;
}
*dest = *src;
fixUpChar( *dest );
++dest;
++src;
}
}
if (noMoreData && cachedScript.isEmpty() && !m_executingScript && m_yieldTimer<=0)
end(); // this actually causes us to be deleted
}
void HTMLTokenizer::timerEvent( QTimerEvent *e )
{
if ( e->timerId() == m_yieldTimer ) {
killTimer(m_yieldTimer);
m_yieldTimer = 0;
write( TokenizerString(), true );
} else if ( e->timerId() == m_externalScriptsTimerId ) {
if (view && view->hasLayoutPending()) {
// all stylesheets are loaded but the style modifications
// they triggered have yet to be applied, BBIAB
return;
}
killTimer(m_externalScriptsTimerId);
m_externalScriptsTimerId = 0;
notifyFinished(0);
}
}
void HTMLTokenizer::end()
{
if ( buffer ) {
// parseTag is using the buffer for different matters
if ( !tag )
processToken();
if(buffer)
KHTML_DELETE_QCHAR_VEC(buffer);
if(rawContent)
KHTML_DELETE_QCHAR_VEC(rawContent);
rawContent = 0;
rawContentSize = rawContentMaxSize = rawContentResync = 0;
buffer = 0;
}
emit finishedParsing();
}
void HTMLTokenizer::finish()
{
// The purpose of this iteration is to recover from 'raw content' tokenizing mode.
// In this mode, any error such as the lack of a closing tag (for the considered element) or of a closing comment,
// would result in the entire document being absorbed in one node.
// When it happens, we simply put back in the input buffer what this mode's output has accumulated so far,
// and retokenize after either disabling the 'raw content' mode (by setting the corresponding members to false)
// or after setting a few flags disabling some lax parsing 'features' (brokenComments/brokenServer).
while((title || comment || server) && rawContent && rawContentSize)
{
// we've found an unmatched comment start
if (comment)
brokenComments = true;
else if (server)
brokenServer = true;
checkRawContentBuffer();
rawContent[ rawContentSize ] = 0;
rawContent[ rawContentSize + 1 ] = 0;
int pos;
QString food;
if (title || style || script || textarea) {
rawContentSinceLastEntity = 0;
food.setUnicode(rawContent, rawContentSize);
} else if (server) {
food = "<";
food += QString(rawContent, rawContentSize);
}
else {
pos = QString::fromRawData(rawContent, rawContentSize).indexOf('>');
food.setUnicode(rawContent+pos+1, rawContentSize-pos-1); // deep copy
}
KHTML_DELETE_QCHAR_VEC(rawContent);
rawContent = 0;
rawContentSize = rawContentMaxSize = rawContentResync = 0;
comment = server = title = false;
if ( !food.isEmpty() )
write(food, true);
}
// this indicates we will not receive any more data... but if we are waiting on
// an external script to load, we can't finish parsing until that is done
noMoreData = true;
if (cachedScript.isEmpty() && !m_executingScript && !onHold && m_yieldTimer <= 0)
end(); // this actually causes us to be deleted
}
void HTMLTokenizer::processToken()
{
KJSProxy *jsProxy = view ? view->part()->jScript() : 0L;
if (jsProxy)
jsProxy->setEventHandlerLineno(tagStartLineno);
if ( dest > buffer )
{
#if 0
if(currToken.tid) {
qDebug( "unexpected token id: %d, str: *%s*", currToken.tid,QString::fromRawData( buffer, dest-buffer ).toLatin1().constData() );
assert(0);
}
#endif
currToken.text = new DOMStringImpl( buffer, dest - buffer );
currToken.text->ref();
if (currToken.tid != ID_COMMENT)
currToken.tid = ID_TEXT;
}
else if(!currToken.tid) {
currToken.reset();
if (jsProxy)
jsProxy->setEventHandlerLineno(lineno+src.lineCount());
return;
}
dest = buffer;
#ifdef TOKEN_DEBUG
QString text;
bool closing = (currToken.tid > ID_CLOSE_TAG);
int rid = currToken.tid-(closing ? ID_CLOSE_TAG : 0);
if(currToken.text)
text = QString::fromRawData(currToken.text->s, currToken.text->l);
kDebug( 6036 ) << "Token -->" << LocalName::fromId(localNamePart(rid)).toString()
<< "id =" << currToken.tid << "closing ="<< closing;
if (currToken.flat)
kDebug( 6036 ) << "Token is FLAT!";
if(!text.isNull())
kDebug( 6036 ) << "text: \"" << text << "\"";
unsigned long l = currToken.attrs ? currToken.attrs->length() : 0;
if(l) {
kDebug( 6036 ) << "Attributes: " << l;
for (unsigned long i = 0; i < l; ++i) {
NodeImpl::Id tid = currToken.attrs->idAt(i);
DOMString value = currToken.attrs->valueAt(i);
kDebug( 6036 ) << " " << tid << " " << LocalName::fromId(localNamePart(tid)).toString()
<< "=\"" << value.string() << "\"" << endl;
}
}
#endif
// In some cases, parseToken() can cause javascript code to be executed
// (for example, when setting an attribute that causes an event handler
// to be created). So we need to protect against re-entrancy into the parser
m_executingScript++;
// pass the token over to the parser, the parser DOES NOT delete the token
parser->parseToken(&currToken);
m_executingScript--;
if ( currToken.flat && currToken.tid != ID_TEXT && !parser->noSpaces() )
discard = NoneDiscard;
currToken.reset();
if (jsProxy)
jsProxy->setEventHandlerLineno(0);
}
void HTMLTokenizer::processDoctypeToken()
{
// kDebug( 6036 ) << "Process DoctypeToken (name: " << doctypeToken.name << ", publicID: " << doctypeToken.publicID << ", systemID: " << doctypeToken.systemID;
doctypeToken.publicID = doctypeToken.publicID.simplified();
doctypeToken.systemID = doctypeToken.systemID.simplified();
parser->parseDoctypeToken(&doctypeToken);
}
HTMLTokenizer::~HTMLTokenizer()
{
reset();
delete m_prospectiveTokenizer;
delete parser;
}
void HTMLTokenizer::enlargeBuffer(int len)
{
int newsize = qMax(size*2, size+len);
int oldoffs = (dest - buffer);
buffer = KHTML_REALLOC_QCHAR_VEC(buffer, newsize);
dest = buffer + oldoffs;
size = newsize;
}
void HTMLTokenizer::enlargeRawContentBuffer(int len)
{
int newsize = qMax(rawContentMaxSize*2, rawContentMaxSize+len);
rawContent = KHTML_REALLOC_QCHAR_VEC(rawContent, newsize);
rawContentMaxSize = newsize;
}
void HTMLTokenizer::notifyFinished(CachedObject* finishedObj)
{
assert(!cachedScript.isEmpty());
// Make external scripts wait for external stylesheets.
// FIXME: This needs to be done for inline scripts too.
m_hasScriptsWaitingForStylesheets = !parser->doc()->haveStylesheetsLoaded();
if (m_hasScriptsWaitingForStylesheets) {
kDebug( 6036 ) << "Delaying script execution until stylesheets have loaded.";
return;
}
kDebug( 6036 ) << (finishedObj ? "Processing an external script" :
"Continuing processing of delayed external scripts");
bool done = false;
m_scriptTime.start();
while (!done && cachedScript.head()->isLoaded()) {
if (!continueProcessingScripts())
break;
CachedScript* cs = cachedScript.dequeue();
DOMString scriptSource = cs->script();
#ifdef TOKEN_DEBUG
kDebug( 6036 ) << "External script is:" << endl << scriptSource.string();
#endif
setSrc(TokenizerString());
// make sure we forget about the script before we execute the new one
// infinite recursion might happen otherwise
QString cachedScriptUrl( cs->url().string() );
cs->deref(this);
scriptExecution( scriptSource.string(), cachedScriptUrl );
done = cachedScript.isEmpty();
if (done) {
assert(!m_hasScriptsWaitingForStylesheets);
} else if (m_hasScriptsWaitingForStylesheets) {
// flag has changed during the script execution,
// so we need to wait for stylesheets again.
done = true;
}
// 'script' is true when we are called synchronously from
// scriptHandler(). In that case scriptHandler() will take care
// of the pending queue.
if ( !script ) {
while (pendingQueue.count() > 1) {
TokenizerString t = pendingQueue.pop();
pendingQueue.top().prepend( t );
}
if (done) {
write(pendingQueue.pop(), false);
}
// we might be deleted at this point, do not
// access any members.
}
}
}
bool HTMLTokenizer::continueProcessingScripts()
{
if (m_externalScriptsTimerId)
return false;
if (m_scriptTime.elapsed() > m_tokenizerYieldDelay && m_documentTokenizer) {
if ( (m_externalScriptsTimerId = startTimer(0)) )
return false;
}
return true;
}
void HTMLTokenizer::executeScriptsWaitingForStylesheets()
{
assert(parser->doc()->haveStylesheetsLoaded());
if (m_hasScriptsWaitingForStylesheets)
notifyFinished(0);
}
bool HTMLTokenizer::isWaitingForScripts() const
{
return cachedScript.count();
}
bool HTMLTokenizer::isExecutingScript() const
{
return (m_executingScript > 0);
}
void HTMLTokenizer::setSrc(const TokenizerString& source)
{
lineno += src.lineCount();
src = source;
src.resetLineCount();
}
void HTMLTokenizer::setOnHold(bool _onHold)
{
if (onHold == _onHold) return;
onHold = _onHold;
}
diff --git a/khtml/java/kjavaappletserver.cpp b/khtml/java/kjavaappletserver.cpp
index f2e90a4118..c0c7995dee 100644
--- a/khtml/java/kjavaappletserver.cpp
+++ b/khtml/java/kjavaappletserver.cpp
@@ -1,844 +1,844 @@
/* This file is part of the KDE project
*
* Copyright (C) 2000 Richard Moore <rich@kde.org>
* 2000 Wynn Wilkes <wynnw@caldera.com>
*
* 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 "kjavaappletserver.h"
#include "kjavaappletcontext.h"
#include "kjavaprocess.h"
#include "kjavadownloader.h"
#include <kdebug.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <klocalizedstring.h>
#include <kparts/browserextension.h>
#include <kio/job.h>
#include <kio/kprotocolmanager.h>
#include <ksslcertificate.h>
#include <ksslcertchain.h>
#include <kssl.h>
#include <QtCore/QTimer>
#include <QtCore/QPointer>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>
#include <QApplication>
#include <QLabel>
#include <QDialog>
#include <QPushButton>
#include <QLayout>
#include <QtCore/QRegExp>
#include <stdlib.h>
#include <assert.h>
#include <QtCore/QAbstractEventDispatcher>
#include <qstandardpaths.h>
#define KJAS_CREATE_CONTEXT (char)1
#define KJAS_DESTROY_CONTEXT (char)2
#define KJAS_CREATE_APPLET (char)3
#define KJAS_DESTROY_APPLET (char)4
#define KJAS_START_APPLET (char)5
#define KJAS_STOP_APPLET (char)6
#define KJAS_INIT_APPLET (char)7
#define KJAS_SHOW_DOCUMENT (char)8
#define KJAS_SHOW_URLINFRAME (char)9
#define KJAS_SHOW_STATUS (char)10
#define KJAS_RESIZE_APPLET (char)11
#define KJAS_GET_URLDATA (char)12
#define KJAS_URLDATA (char)13
#define KJAS_SHUTDOWN_SERVER (char)14
#define KJAS_JAVASCRIPT_EVENT (char)15
#define KJAS_GET_MEMBER (char)16
#define KJAS_CALL_MEMBER (char)17
#define KJAS_PUT_MEMBER (char)18
#define KJAS_DEREF_OBJECT (char)19
#define KJAS_AUDIOCLIP_PLAY (char)20
#define KJAS_AUDIOCLIP_LOOP (char)21
#define KJAS_AUDIOCLIP_STOP (char)22
#define KJAS_APPLET_STATE (char)23
#define KJAS_APPLET_FAILED (char)24
#define KJAS_DATA_COMMAND (char)25
#define KJAS_PUT_URLDATA (char)26
#define KJAS_PUT_DATA (char)27
#define KJAS_SECURITY_CONFIRM (char)28
#define KJAS_SHOW_CONSOLE (char)29
class JSStackFrame;
typedef QMap< int, KJavaKIOJob* > KIOJobMap;
typedef QMap< int, JSStackFrame* > JSStack;
class JSStackFrame {
public:
JSStackFrame(JSStack & stack, QStringList & a)
: jsstack(stack), args(a), ticket(counter++), ready(false), exit (false) {
jsstack.insert( ticket, this );
}
~JSStackFrame() {
jsstack.remove( ticket );
}
JSStack & jsstack;
QStringList & args;
int ticket;
bool ready : 1;
bool exit : 1;
static int counter;
};
int JSStackFrame::counter = 0;
class KJavaAppletServerPrivate
{
friend class KJavaAppletServer;
private:
KJavaAppletServerPrivate() : kssl( 0L ) {}
~KJavaAppletServerPrivate() {
delete kssl;
}
int counter;
QMap< int, QPointer<KJavaAppletContext> > contexts;
QString appletLabel;
JSStack jsstack;
KIOJobMap kiojobs;
bool javaProcessFailed;
bool useKIO;
KSSL * kssl;
//int locked_context;
//QValueList<QByteArray> java_requests;
};
static KJavaAppletServer* self = 0;
KJavaAppletServer::KJavaAppletServer()
: d(new KJavaAppletServerPrivate)
{
process = new KJavaProcess();
connect( process, SIGNAL(received(QByteArray)),
this, SLOT(slotJavaRequest(QByteArray)) );
setupJava( process );
if( process->startJava() ) {
d->appletLabel = i18n( "Loading Applet" );
d->javaProcessFailed = false;
}
else {
d->appletLabel = i18n( "Error: java executable not found" );
d->javaProcessFailed = true;
}
}
KJavaAppletServer::~KJavaAppletServer()
{
disconnect(process, 0, 0, 0); // first disconnect from process.
quit();
delete process;
process = 0;
delete d;
}
QString KJavaAppletServer::getAppletLabel()
{
if( self )
return self->appletLabel();
else
return QString();
}
QString KJavaAppletServer::appletLabel()
{
return d->appletLabel;
}
KJavaAppletServer* KJavaAppletServer::allocateJavaServer()
{
if( self == 0 )
{
self = new KJavaAppletServer();
self->d->counter = 0;
}
++(self->d->counter);
return self;
}
void KJavaAppletServer::freeJavaServer()
{
--(self->d->counter);
if( self->d->counter == 0 )
{
//instead of immediately quitting here, set a timer to kill us
//if there are still no servers- give us one minute
//this is to prevent repeated loading and unloading of the jvm
KConfig config( "konquerorrc" );
KConfigGroup group = config.group( "Java/JavaScript Settings" );
if( group.readEntry( "ShutdownAppletServer", true ) )
{
const int value = group.readEntry( "AppletServerTimeout", 60 );
QTimer::singleShot( value*1000, self, SLOT(checkShutdown()) );
}
}
}
void KJavaAppletServer::checkShutdown()
{
if( self->d->counter == 0 )
{
delete self;
self = 0;
}
}
void KJavaAppletServer::setupJava( KJavaProcess *p )
{
KConfig configFile ( "konquerorrc" );
KConfigGroup config(&configFile, "Java/JavaScript Settings" );
QString jvm_path = "java";
QString jPath = config.readPathEntry( "JavaPath", QString() );
if ( !jPath.isEmpty() && jPath != "java" )
{
// Cut off trailing slash if any
if( jPath[jPath.length()-1] == '/' )
jPath.remove(jPath.length()-1, 1);
QDir dir( jPath );
if( dir.exists( "bin/java" ) )
{
jvm_path = jPath + "/bin/java";
}
else if (dir.exists( "/jre/bin/java" ) )
{
jvm_path = jPath + "/jre/bin/java";
}
else if( QFile::exists(jPath) )
{
//check here to see if they entered the whole path the java exe
jvm_path = jPath;
}
}
//check to see if jvm_path is valid and set d->appletLabel accordingly
p->setJVMPath( jvm_path );
// Prepare classpath variable
QString kjava_class = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kjava/kjava.jar");
kDebug(6100) << "kjava_class = " << kjava_class;
if( kjava_class.isNull() ) // Should not happen
return;
QDir dir( kjava_class );
dir.cdUp();
kDebug(6100) << "dir = " << dir.absolutePath();
const QStringList entries = dir.entryList(QDir::nameFiltersFromString( "*.jar" ));
kDebug(6100) << "entries = " << entries.join( ":" );
QString classes;
{
QStringList::ConstIterator it = entries.begin();
const QStringList::ConstIterator itEnd = entries.end();
for( ; it != itEnd; ++it )
{
if( !classes.isEmpty() )
classes += ':';
classes += dir.absoluteFilePath( *it );
}
}
p->setClasspath( classes );
// Fix all the extra arguments
const QString extraArgs = config.readEntry( "JavaArgs" );
p->setExtraArgs( extraArgs );
if( config.readEntry( "UseSecurityManager", true ) )
{
QString class_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kjava/kjava.policy" );
p->setSystemProperty( "java.security.policy", class_file );
p->setSystemProperty( "java.security.manager",
"org.kde.kjas.server.KJASSecurityManager" );
}
d->useKIO = config.readEntry("UseKio", false);
if( d->useKIO )
{
p->setSystemProperty( "kjas.useKio", QString() );
}
//check for http proxies...
if( KProtocolManager::useProxy() )
{
// only proxyForUrl honors automatic proxy scripts
// we do not know the applet url here so we just use a dummy url
// this is a workaround for now
// FIXME
const QUrl dummyURL( "http://www.kde.org/" );
const QString httpProxy = KProtocolManager::proxyForUrl(dummyURL);
kDebug(6100) << "httpProxy is " << httpProxy;
const QUrl url( httpProxy );
p->setSystemProperty( "http.proxyHost", url.host() );
p->setSystemProperty( "http.proxyPort", QString::number( url.port() ) );
}
//set the main class to run
p->setMainClass( "org.kde.kjas.server.Main" );
}
void KJavaAppletServer::createContext( int contextId, KJavaAppletContext* context )
{
// kDebug(6100) << "createContext: " << contextId;
if ( d->javaProcessFailed ) return;
d->contexts.insert( contextId, context );
QStringList args;
args.append( QString::number( contextId ) );
process->send( KJAS_CREATE_CONTEXT, args );
}
void KJavaAppletServer::destroyContext( int contextId )
{
// kDebug(6100) << "destroyContext: " << contextId;
if ( d->javaProcessFailed ) return;
d->contexts.remove( contextId );
QStringList args;
args.append( QString::number( contextId ) );
process->send( KJAS_DESTROY_CONTEXT, args );
}
bool KJavaAppletServer::createApplet( int contextId, int appletId,
const QString & name, const QString & clazzName,
const QString & baseURL, const QString & user,
const QString & password, const QString & authname,
const QString & codeBase, const QString & jarFile,
QSize size, const QMap<QString,QString>& params,
const QString & windowTitle )
{
// kDebug(6100) << "createApplet: contextId = " << contextId << endl
// << " appletId = " << appletId << endl
// << " name = " << name << endl
// << " clazzName = " << clazzName << endl
// << " baseURL = " << baseURL << endl
// << " codeBase = " << codeBase << endl
// << " jarFile = " << jarFile << endl
// << " width = " << size.width() << endl
// << " height = " << size.height() << endl;
if ( d->javaProcessFailed ) return false;
QStringList args;
args.append( QString::number( contextId ) );
args.append( QString::number( appletId ) );
//it's ok if these are empty strings, I take care of it later...
args.append( name );
args.append( clazzName );
args.append( baseURL );
args.append( user );
args.append( password );
args.append( authname );
args.append( codeBase );
args.append( jarFile );
args.append( QString::number( size.width() ) );
args.append( QString::number( size.height() ) );
args.append( windowTitle );
//add on the number of parameter pairs...
const int num = params.count();
const QString num_params = QString("%1").arg( num, 8 );
args.append( num_params );
QMap< QString, QString >::ConstIterator it = params.begin();
const QMap< QString, QString >::ConstIterator itEnd = params.end();
for( ; it != itEnd; ++it )
{
args.append( it.key() );
args.append( it.value() );
}
process->send( KJAS_CREATE_APPLET, args );
return true;
}
void KJavaAppletServer::initApplet( int contextId, int appletId )
{
if ( d->javaProcessFailed ) return;
QStringList args;
args.append( QString::number( contextId ) );
args.append( QString::number( appletId ) );
process->send( KJAS_INIT_APPLET, args );
}
void KJavaAppletServer::destroyApplet( int contextId, int appletId )
{
if ( d->javaProcessFailed ) return;
QStringList args;
args.append( QString::number(contextId) );
args.append( QString::number(appletId) );
process->send( KJAS_DESTROY_APPLET, args );
}
void KJavaAppletServer::startApplet( int contextId, int appletId )
{
if ( d->javaProcessFailed ) return;
QStringList args;
args.append( QString::number(contextId) );
args.append( QString::number(appletId) );
process->send( KJAS_START_APPLET, args );
}
void KJavaAppletServer::stopApplet( int contextId, int appletId )
{
if ( d->javaProcessFailed ) return;
QStringList args;
args.append( QString::number(contextId) );
args.append( QString::number(appletId) );
process->send( KJAS_STOP_APPLET, args );
}
void KJavaAppletServer::showConsole() {
if ( d->javaProcessFailed ) return;
QStringList args;
process->send( KJAS_SHOW_CONSOLE, args );
}
void KJavaAppletServer::sendURLData( int loaderID, int code, const QByteArray& data )
{
QStringList args;
args.append( QString::number(loaderID) );
args.append( QString::number(code) );
process->send( KJAS_URLDATA, args, data );
}
void KJavaAppletServer::removeDataJob( int loaderID )
{
const KIOJobMap::iterator it = d->kiojobs.find( loaderID );
if (it != d->kiojobs.end()) {
it.value()->deleteLater();
d->kiojobs.erase( it );
}
}
void KJavaAppletServer::quit()
{
const QStringList args;
process->send( KJAS_SHUTDOWN_SERVER, args );
process->waitForFinished( 10000 );
}
void KJavaAppletServer::slotJavaRequest( const QByteArray& qb )
{
// qb should be one command only without the length string,
// we parse out the command and it's meaning here...
QString cmd;
QStringList args;
int index = 0;
const int qb_size = qb.size();
//get the command code
const char cmd_code = qb[ index++ ];
++index; //skip the next sep
//get contextID
QString contextID;
while( index < qb_size && qb[index] != 0 )
{
contextID += qb[ index++ ];
}
bool ok;
const int ID_num = contextID.toInt( &ok ); // context id or kio job id
/*if (d->locked_context > -1 &&
ID_num != d->locked_context &&
(cmd_code == KJAS_JAVASCRIPT_EVENT ||
cmd_code == KJAS_APPLET_STATE ||
cmd_code == KJAS_APPLET_FAILED))
{
/ * Don't allow requests from other contexts if we're waiting
* on a return value that can trigger JavaScript events
* /
d->java_requests.push_back(qb);
return;
}*/
++index; //skip the sep
if (cmd_code == KJAS_PUT_DATA) {
// rest of the data is for kio put
if (ok) {
KIOJobMap::iterator it = d->kiojobs.find( ID_num );
if (ok && it != d->kiojobs.end()) {
QByteArray qba;
qba = QByteArray::fromRawData(qb.data() + index, qb.size() - index - 1);
it.value()->data(qba);
qba = QByteArray::fromRawData(qb.data() + index, qb.size() - index - 1);
}
kDebug(6100) << "PutData(" << ID_num << ") size=" << qb.size() - index;
} else
kError(6100) << "PutData error " << ok << endl;
return;
}
//now parse out the arguments
while( index < qb_size )
{
int sep_pos = qb.indexOf( (char) 0, index );
if (sep_pos < 0) {
kError(6100) << "Missing separation byte" << endl;
sep_pos = qb_size;
}
//kDebug(6100) << "KJavaAppletServer::slotJavaRequest: "<< QString::fromLocal8Bit( qb.data() + index, sep_pos - index );
args.append( QString::fromLocal8Bit( qb.data() + index, sep_pos - index ) );
index = sep_pos + 1; //skip the sep
}
//here I should find the context and call the method directly
//instead of emitting signals
switch( cmd_code )
{
case KJAS_SHOW_DOCUMENT:
cmd = QLatin1String( "showdocument" );
break;
case KJAS_SHOW_URLINFRAME:
cmd = QLatin1String( "showurlinframe" );
break;
case KJAS_SHOW_STATUS:
cmd = QLatin1String( "showstatus" );
break;
case KJAS_RESIZE_APPLET:
cmd = QLatin1String( "resizeapplet" );
break;
case KJAS_GET_URLDATA:
if (ok && !args.empty() ) {
d->kiojobs.insert(ID_num, new KJavaDownloader(ID_num, args.first()));
kDebug(6100) << "GetURLData(" << ID_num << ") url=" << args.first();
} else
kError(6100) << "GetURLData error " << ok << " args:" << args.size() << endl;
return;
case KJAS_PUT_URLDATA:
if (ok && !args.empty()) {
KJavaUploader* const job = new KJavaUploader(ID_num, args.first());
d->kiojobs.insert(ID_num, job);
job->start();
kDebug(6100) << "PutURLData(" << ID_num << ") url=" << args.first();
} else
kError(6100) << "PutURLData error " << ok << " args:" << args.size() << endl;
return;
case KJAS_DATA_COMMAND:
if (ok && !args.empty()) {
const int cmd = args.first().toInt( &ok );
KIOJobMap::iterator it = d->kiojobs.find( ID_num );
if (ok && it != d->kiojobs.end())
it.value()->jobCommand( cmd );
kDebug(6100) << "KIO Data command: " << ID_num << " " << args.first();
} else
kError(6100) << "KIO Data command error " << ok << " args:" << args.size() << endl;
return;
case KJAS_JAVASCRIPT_EVENT:
cmd = QLatin1String( "JS_Event" );
if(!args.empty()) {
kDebug(6100) << "Javascript request: "<< contextID
<< " code: " << args[0] << endl;
} else {
kError(6100) << "Expected args not to be empty!" << endl;
}
break;
case KJAS_GET_MEMBER:
case KJAS_PUT_MEMBER:
case KJAS_CALL_MEMBER: {
if(!args.empty()) {
const int ticket = args[0].toInt();
JSStack::iterator it = d->jsstack.find(ticket);
if (it != d->jsstack.end()) {
kDebug(6100) << "slotJavaRequest: " << ticket;
args.pop_front();
it.value()->args.operator=(args); // just in case ..
it.value()->ready = true;
it.value()->exit = true;
} else
kDebug(6100) << "Error: Missed return member data";
} else {
kError(6100) << "Expected args not to be empty!" << endl;
}
return;
}
case KJAS_AUDIOCLIP_PLAY:
cmd = QLatin1String( "audioclip_play" );
if(!args.empty())
kDebug(6100) << "Audio Play: url=" << args[0];
else
kError(6100) << "Expected args not to be empty!" << endl;
break;
case KJAS_AUDIOCLIP_LOOP:
cmd = QLatin1String( "audioclip_loop" );
if(!args.empty())
kDebug(6100) << "Audio Loop: url=" << args[0];
else
kError(6100) << "Expected args not to be empty!" << endl;
break;
case KJAS_AUDIOCLIP_STOP:
cmd = QLatin1String( "audioclip_stop" );
if(!args.empty())
kDebug(6100) << "Audio Stop: url=" << args[0];
else
kError(6100) << "Expected args not to be empty!" << endl;
break;
case KJAS_APPLET_STATE:
if(args.size() > 1)
kDebug(6100) << "Applet State Notification for Applet " << args[0] << ". New state=" << args[1];
else
kError(6100) << "Expected args not to be empty!" << endl;
cmd = QLatin1String( "AppletStateNotification" );
break;
case KJAS_APPLET_FAILED:
if(args.size() > 1)
kDebug(6100) << "Applet " << args[0] << " Failed: " << args[1];
else
kError(6100) << "Expected args not to be empty!" << endl;
cmd = QLatin1String( "AppletFailed" );
break;
case KJAS_SECURITY_CONFIRM: {
if (KSSL::doesSSLWork() && !d->kssl)
d->kssl = new KSSL;
QStringList sl;
QString answer( "invalid" );
if (!d->kssl) {
answer = "nossl";
} else if (args.size() > 2) {
const int certsnr = args[1].toInt();
Q_ASSERT(args.size() > certsnr + 1);
QString text;
QList<KSSLCertificate *> certs;
for (int i = certsnr - 1; i >= 0; --i) {
- const QByteArray &arg = args[i + 2].toAscii();
+ const QByteArray &arg = args[i + 2].toLatin1();
KSSLCertificate * cert = KSSLCertificate::fromString(arg.constData());
if (cert) {
certs.prepend(cert);
if (cert->isSigner())
text += i18n("Signed by (validation: %1)", KSSLCertificate::verifyText(cert->validate()));
else
text += i18n("Certificate (validation: %1)", KSSLCertificate::verifyText(cert->validate()));
text += "\n";
QString subject = cert->getSubject() + QChar('\n');
QRegExp reg(QString("/[A-Z]+="));
int pos = 0;
while ((pos = subject.indexOf(reg, pos)) > -1)
subject.replace(pos, 1, QString("\n "));
text += subject.mid(1);
}
}
kDebug(6100) << "Security confirm " << args.first() << certs.count();
if ( !certs.isEmpty() ) {
KSSLCertChain chain;
chain.setChain( certs );
if ( chain.isValid() )
answer = PermissionDialog( qApp->activeWindow() ).exec( text, args[0] );
}
qDeleteAll(certs);
}
sl.push_front( answer );
sl.push_front( QString::number(ID_num) );
process->send( KJAS_SECURITY_CONFIRM, sl );
return;
}
default:
return;
break;
}
if( !ok )
{
kError(6100) << "could not parse out contextID to call command on" << endl;
return;
}
KJavaAppletContext* const context = d->contexts[ ID_num ];
if( context )
context->processCmd( cmd, args );
else if (cmd != "AppletStateNotification")
kError(6100) << "no context object for this id" << endl;
}
void KJavaAppletServer::killTimers()
{
QAbstractEventDispatcher::instance()->unregisterTimers(this);
}
void KJavaAppletServer::endWaitForReturnData() {
kDebug(6100) << "KJavaAppletServer::endWaitForReturnData";
killTimers();
JSStack::iterator it = d->jsstack.begin();
JSStack::iterator itEnd = d->jsstack.end();
for (; it != itEnd; ++it)
it.value()->exit = true;
}
void KJavaAppletServer::timerEvent(QTimerEvent *) {
endWaitForReturnData();
kDebug(6100) << "KJavaAppletServer::timerEvent timeout";
}
void KJavaAppletServer::waitForReturnData(JSStackFrame * frame) {
kDebug(6100) << ">KJavaAppletServer::waitForReturnData";
killTimers();
startTimer(15000);
while (!frame->exit) {
QAbstractEventDispatcher::instance()->processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents);
}
if (d->jsstack.size() <= 1)
killTimers();
kDebug(6100) << "<KJavaAppletServer::waitForReturnData stacksize:" << d->jsstack.size();
}
bool KJavaAppletServer::getMember(QStringList & args, QStringList & ret_args) {
JSStackFrame frame( d->jsstack, ret_args );
args.push_front( QString::number(frame.ticket) );
process->send( KJAS_GET_MEMBER, args );
waitForReturnData( &frame );
return frame.ready;
}
bool KJavaAppletServer::putMember( QStringList & args ) {
QStringList ret_args;
JSStackFrame frame( d->jsstack, ret_args );
args.push_front( QString::number(frame.ticket) );
process->send( KJAS_PUT_MEMBER, args );
waitForReturnData( &frame );
return frame.ready && ret_args.count() > 0 && ret_args[0].toInt();
}
bool KJavaAppletServer::callMember(QStringList & args, QStringList & ret_args) {
JSStackFrame frame( d->jsstack, ret_args );
args.push_front( QString::number(frame.ticket) );
process->send( KJAS_CALL_MEMBER, args );
waitForReturnData( &frame );
return frame.ready;
}
void KJavaAppletServer::derefObject( QStringList & args ) {
process->send( KJAS_DEREF_OBJECT, args );
}
bool KJavaAppletServer::usingKIO() {
return d->useKIO;
}
PermissionDialog::PermissionDialog( QWidget* parent )
: QObject(parent), m_button("no")
{}
QString PermissionDialog::exec( const QString & cert, const QString & perm ) {
QPointer<QDialog> dialog = new QDialog( static_cast<QWidget*>(parent()) );
dialog->setObjectName("PermissionDialog");
QSizePolicy sizeplcy( QSizePolicy::Minimum, QSizePolicy::Minimum);
sizeplcy.setHeightForWidth(dialog->sizePolicy().hasHeightForWidth());
dialog->setSizePolicy(sizeplcy);
dialog->setModal( true );
dialog->setWindowTitle( i18n("Security Alert") );
QVBoxLayout* const dialogLayout = new QVBoxLayout( dialog );
dialogLayout->setObjectName("dialogLayout");
dialogLayout->addWidget( new QLabel( i18n("Do you grant Java applet with certificate(s):"), dialog ) );
dialogLayout->addWidget( new QLabel( cert, dialog ) );
dialogLayout->addWidget( new QLabel( i18n("the following permission"), dialog ) );
dialogLayout->addWidget( new QLabel( perm, dialog ) );
QSpacerItem* const spacer2 = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
dialogLayout->addItem( spacer2 );
QHBoxLayout* const buttonLayout = new QHBoxLayout();
buttonLayout->setMargin(0);
buttonLayout->setObjectName("buttonLayout");
QPushButton* const no = new QPushButton( i18n("&No"), dialog );
no->setDefault( true );
buttonLayout->addWidget( no );
QPushButton* const reject = new QPushButton( i18n("&Reject All"), dialog );
buttonLayout->addWidget( reject );
QPushButton* const yes = new QPushButton( i18n("&Yes"), dialog );
buttonLayout->addWidget( yes );
QPushButton* const grant = new QPushButton( i18n("&Grant All"), dialog );
buttonLayout->addWidget( grant );
dialogLayout->addLayout( buttonLayout );
dialog->resize( dialog->minimumSizeHint() );
//clearWState( WState_Polished );
connect( no, SIGNAL(clicked()), this, SLOT(clicked()) );
connect( reject, SIGNAL(clicked()), this, SLOT(clicked()) );
connect( yes, SIGNAL(clicked()), this, SLOT(clicked()) );
connect( grant, SIGNAL(clicked()), this, SLOT(clicked()) );
dialog->exec();
delete dialog;
return m_button;
}
PermissionDialog::~PermissionDialog()
{}
void PermissionDialog::clicked()
{
m_button = sender()->objectName();
static_cast<const QWidget*>(sender())->parentWidget()->close();
}
diff --git a/khtml/java/kjavadownloader.cpp b/khtml/java/kjavadownloader.cpp
index ebbfff9c2a..f5295b74e8 100644
--- a/khtml/java/kjavadownloader.cpp
+++ b/khtml/java/kjavadownloader.cpp
@@ -1,297 +1,297 @@
/* This file is part of the KDE project
*
* Copyright (C) 2000 Richard Moore <rich@kde.org>
* 2000 Wynn Wilkes <wynnw@caldera.com>
*
* 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 "kjavadownloader.h"
#include "kjavaappletserver.h"
#include <kurl.h>
#include <kio/job.h>
#include <kio/jobclasses.h>
#include <kdebug.h>
#include <QtCore/QFile>
static const int DATA = 0;
static const int FINISHED = 1;
static const int ERRORCODE = 2;
static const int HEADERS = 3;
static const int REDIRECT = 4;
static const int MIMETYPE = 5;
static const int CONNECTED = 6;
static const int REQUESTDATA = 7;
static const int KJAS_STOP = 0;
static const int KJAS_HOLD = 1;
static const int KJAS_RESUME = 2;
KJavaKIOJob::~KJavaKIOJob() {}
void KJavaKIOJob::data( const QByteArray& )
{
kError(6100) << "Job id mixup" << endl;
}
//-----------------------------------------------------------------------------
class KJavaDownloaderPrivate
{
friend class KJavaDownloader;
public:
KJavaDownloaderPrivate() : responseCode(0), isfirstdata(true) {}
~KJavaDownloaderPrivate()
{
delete url;
if (job) job->kill(); // KIO::Job::kill deletes itself
}
private:
int loaderID;
KUrl* url;
QByteArray file;
KIO::TransferJob* job;
int responseCode;
bool isfirstdata;
};
KJavaDownloader::KJavaDownloader( int ID, const QString& url )
:d(new KJavaDownloaderPrivate)
{
kDebug(6100) << "KJavaDownloader(" << ID << ") = " << url;
d->loaderID = ID;
d->url = new KUrl( url );
d->job = KIO::get( *d->url, KIO::NoReload, KIO::HideProgressInfo );
d->job->addMetaData("PropagateHttpHeader", "true");
connect( d->job, SIGNAL(data(KIO::Job*,QByteArray)),
this, SLOT(slotData(KIO::Job*,QByteArray)) );
connect( d->job, SIGNAL(connected(KIO::Job*)),
this, SLOT(slotConnected(KIO::Job*)));
connect( d->job, SIGNAL(mimetype(KIO::Job*,QString)),
this, SLOT(slotMimetype(KIO::Job*,QString)));
connect( d->job, SIGNAL(result(KJob*)),
this, SLOT(slotResult(KJob*)) );
}
KJavaDownloader::~KJavaDownloader()
{
delete d;
}
void KJavaDownloader::slotData( KIO::Job*, const QByteArray& qb )
{
//kDebug(6100) << "slotData(" << d->loaderID << ")";
KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
if (d->isfirstdata) {
QString headers = d->job->queryMetaData("HTTP-Headers");
if (!headers.isEmpty()) {
d->file.resize( headers.length() );
- memcpy( d->file.data(), headers.toAscii().constData(), headers.length() );
+ memcpy( d->file.data(), headers.toLatin1().constData(), headers.length() );
server->sendURLData( d->loaderID, HEADERS, d->file );
d->file.resize( 0 );
}
d->isfirstdata = false;
}
if ( qb.size() )
server->sendURLData( d->loaderID, DATA, qb );
KJavaAppletServer::freeJavaServer();
}
void KJavaDownloader::slotConnected(KIO::Job*)
{
kDebug(6100) << "slave connected";
d->responseCode = d->job->error();
}
void KJavaDownloader::slotMimetype(KIO::Job*, const QString & type) {
kDebug(6100) << "slave mimetype " << type;
}
void KJavaDownloader::slotResult( KJob* )
{
kDebug(6100) << "slotResult(" << d->loaderID << ")";
KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
if( d->job->error())
{
kDebug(6100) << "slave had an error = " << d->job->errorString();
int code = d->job->error();
if (!code)
code = 404;
QString codestr = QString::number(code);
d->file.resize(codestr.length());
- memcpy( d->file.data(), codestr.toAscii().constData(), codestr.length() );
+ memcpy( d->file.data(), codestr.toLatin1().constData(), codestr.length() );
kDebug(6100) << "slave had an error = " << code;
server->sendURLData( d->loaderID, ERRORCODE, d->file );
d->file.resize( 0 );
}
else
{
server->sendURLData( d->loaderID, FINISHED, d->file );
}
d->job = 0L; // signal KIO::Job::result deletes itself
server->removeDataJob( d->loaderID ); // will delete this
KJavaAppletServer::freeJavaServer();
}
void KJavaDownloader::jobCommand( int cmd )
{
if (!d->job) return;
switch (cmd) {
case KJAS_STOP: {
kDebug(6100) << "jobCommand(" << d->loaderID << ") stop";
d->job->kill();
d->job = 0L; // KIO::Job::kill deletes itself
KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
server->removeDataJob( d->loaderID ); // will delete this
KJavaAppletServer::freeJavaServer();
break;
}
case KJAS_HOLD:
kDebug(6100) << "jobCommand(" << d->loaderID << ") hold";
d->job->suspend();
break;
case KJAS_RESUME:
kDebug(6100) << "jobCommand(" << d->loaderID << ") resume";
d->job->resume();
break;
}
}
//-----------------------------------------------------------------------------
class KJavaUploaderPrivate
{
public:
KJavaUploaderPrivate() {}
~KJavaUploaderPrivate()
{
delete url;
if (job) job->kill(); // KIO::Job::kill deletes itself
}
int loaderID;
KUrl* url;
QByteArray file;
KIO::TransferJob* job;
bool finished;
};
KJavaUploader::KJavaUploader( int ID, const QString& url )
:d(new KJavaUploaderPrivate)
{
kDebug(6100) << "KJavaUploader(" << ID << ") = " << url;
d->loaderID = ID;
d->url = new KUrl( url );
d->job = 0L;
d->finished = false;
}
void KJavaUploader::start()
{
kDebug(6100) << "KJavaUploader::start(" << d->loaderID << ")";
KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
// create a suspended job
d->job = KIO::put( *d->url, -1, KIO::HideProgressInfo );
d->job->suspend();
connect( d->job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
this, SLOT(slotDataRequest(KIO::Job*,QByteArray&)) );
connect( d->job, SIGNAL(result(KJob*)),
this, SLOT(slotResult(KJob*)) );
server->sendURLData( d->loaderID, CONNECTED, d->file );
KJavaAppletServer::freeJavaServer();
}
KJavaUploader::~KJavaUploader()
{
delete d;
}
void KJavaUploader::slotDataRequest( KIO::Job*, QByteArray& qb )
{
// send our data and suspend
kDebug(6100) << "slotDataRequest(" << d->loaderID << ") finished:" << d->finished;
qb.resize( d->file.size() );
KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
if (d->file.size() == 0) {
d->job = 0L; // eof, job deletes itself
server->removeDataJob( d->loaderID ); // will delete this
} else {
memcpy( qb.data(), d->file.data(), d->file.size() );
d->file.resize( 0 );
if (!d->finished) {
server->sendURLData( d->loaderID, REQUESTDATA, d->file );
d->job->suspend();
}
}
KJavaAppletServer::freeJavaServer();
}
void KJavaUploader::data( const QByteArray& qb )
{
kDebug(6100) << "KJavaUploader::data(" << d->loaderID << ")";
d->file.resize( qb.size() );
memcpy( d->file.data(), qb.data(), qb.size() );
d->job->resume();
}
void KJavaUploader::slotResult( KJob* )
{
kDebug(6100) << "slotResult(" << d->loaderID << ") job:" << d->job;
if (!d->job)
return;
KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
if (d->job->error())
{
int code = d->job->error();
QString codestr = QString::number(code);
d->file.resize(codestr.length());
- memcpy( d->file.data(), codestr.toAscii().constData(), codestr.length() );
+ memcpy( d->file.data(), codestr.toLatin1().constData(), codestr.length() );
kDebug(6100) << "slave had an error " << code << ": " << d->job->errorString();
server->sendURLData( d->loaderID, ERRORCODE, d->file );
d->file.resize( 0 );
}
else // shouldn't come here
kError(6100) << "slotResult(" << d->loaderID << ") job:" << d->job << endl;
d->job = 0L; // signal KIO::Job::result deletes itself
server->removeDataJob( d->loaderID ); // will delete this
KJavaAppletServer::freeJavaServer();
}
void KJavaUploader::jobCommand( int cmd )
{
if (!d->job) return;
switch (cmd) {
case KJAS_STOP: {
kDebug(6100) << "jobCommand(" << d->loaderID << ") stop";
d->finished = true;
if (d->job->isSuspended())
d->job->resume();
break;
}
}
}
diff --git a/khtml/khtml_part.cpp b/khtml/khtml_part.cpp
index 7c89140fed..6f6fb67cf6 100644
--- a/khtml/khtml_part.cpp
+++ b/khtml/khtml_part.cpp
@@ -1,7494 +1,7494 @@
/* This file is part of the KDE project
*
* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
* 1999 Lars Knoll <knoll@kde.org>
* 1999 Antti Koivisto <koivisto@kde.org>
* 2000 Simon Hausmann <hausmann@kde.org>
* 2000 Stefan Schimanski <1Stein@gmx.de>
* 2001-2005 George Staikos <staikos@kde.org>
* 2001-2003 Dirk Mueller <mueller@kde.org>
* 2000-2005 David Faure <faure@kde.org>
* 2002 Apple Computer, Inc.
* 2010 Maksim Orlovich (maksim@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.
*/
//#define SPEED_DEBUG
#include "khtml_part.h"
#include "ui_htmlpageinfo.h"
#include "khtmlviewbar.h"
#include "khtml_pagecache.h"
#include "dom/dom_string.h"
#include "dom/dom_element.h"
#include "dom/dom_exception.h"
#include "dom/html_document.h"
#include "dom/dom2_range.h"
#include "editing/editor.h"
#include "html/html_documentimpl.h"
#include "html/html_baseimpl.h"
#include "html/html_objectimpl.h"
#include "html/html_miscimpl.h"
#include "html/html_imageimpl.h"
#include "imload/imagemanager.h"
#include "rendering/render_text.h"
#include "rendering/render_frames.h"
#include "rendering/render_layer.h"
#include "rendering/render_position.h"
#include "misc/loader.h"
#include "misc/khtml_partaccessor.h"
#include "xml/dom2_eventsimpl.h"
#include "xml/dom2_rangeimpl.h"
#include "xml/xml_tokenizer.h"
#include "css/cssstyleselector.h"
#include "css/csshelper.h"
using namespace DOM;
#include "khtmlview.h"
#include <kparts/partmanager.h>
#include <kparts/browseropenorsavequestion.h>
#include <kacceleratormanager.h>
#include "ecma/kjs_proxy.h"
#include "ecma/kjs_window.h"
#include "ecma/kjs_events.h"
#include "khtml_settings.h"
#include "kjserrordlg.h"
#include <kjs/function.h>
#include <kjs/interpreter.h>
#include <sys/types.h>
#include <assert.h>
#include <unistd.h>
#include <kstringhandler.h>
#include <kio/job.h>
#include <kio/jobuidelegate.h>
#include <kio/global.h>
#include <kio/pixmaploader.h>
#include <kio/netaccess.h>
#include <kio/hostinfo_p.h>
#include <kprotocolmanager.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kstandardaction.h>
#include <kstandardguiitem.h>
#include <kactioncollection.h>
#include <kfiledialog.h>
#include <kmimetypetrader.h>
#include <qtemporaryfile.h>
#include <ktoolinvocation.h>
#include <kauthorized.h>
#include <kparts/browserinterface.h>
#include <kparts/scriptableextension.h>
#include <kde_file.h>
#include <kactionmenu.h>
#include <ktoggleaction.h>
#include <kcodecaction.h>
#include <kselectaction.h>
#include <ksslinfodialog.h>
#include <ksslsettings.h>
#include <kfileitem.h>
#include <kurifilter.h>
#include <kurllabel.h>
#include <kurlmimedata.h>
#include <QClipboard>
#include <QToolTip>
#include <QDrag>
#include <QtCore/QFile>
#include <QtCore/QMetaEnum>
#include <QTextDocument>
#include <QtCore/QDate>
#include <QtNetwork/QSslCertificate>
#include <QStatusBar>
#include <QStyle>
#include <qmimedatabase.h>
#include "khtmlpart_p.h"
#include "khtml_iface.h"
#include "kpassivepopup.h"
#include "kmenu.h"
#include "rendering/render_form.h"
#include <kwindowsystem.h>
#include <kconfiggroup.h>
#ifdef KJS_DEBUGGER
#include "ecma/debugger/debugwindow.h"
#endif
// SVG
#include <svg/SVGDocument.h>
#include <qstandardpaths.h>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#define toDisplayString() toString()
#endif
bool KHTMLPartPrivate::s_dnsInitialised = false;
// DNS prefetch settings
static const int sMaxDNSPrefetchPerPage = 42;
static const int sDNSPrefetchTimerDelay = 200;
static const int sDNSTTLSeconds = 400;
static const int sDNSCacheSize = 500;
namespace khtml {
class PartStyleSheetLoader : public CachedObjectClient
{
public:
PartStyleSheetLoader(KHTMLPart *part, DOM::DOMString url, DocLoader* dl)
{
m_part = part;
m_cachedSheet = dl->requestStyleSheet(url, QString(), "text/css",
true /* "user sheet" */);
if (m_cachedSheet)
m_cachedSheet->ref( this );
}
virtual ~PartStyleSheetLoader()
{
if ( m_cachedSheet ) m_cachedSheet->deref(this);
}
virtual void setStyleSheet(const DOM::DOMString&, const DOM::DOMString &sheet, const DOM::DOMString &, const DOM::DOMString &/*mimetype*/)
{
if ( m_part )
m_part->setUserStyleSheet( sheet.string() );
delete this;
}
virtual void error( int, const QString& ) {
delete this;
}
QPointer<KHTMLPart> m_part;
khtml::CachedCSSStyleSheet *m_cachedSheet;
};
}
KHTMLPart::KHTMLPart( QWidget *parentWidget, QObject *parent, GUIProfile prof )
: KParts::ReadOnlyPart( parent )
{
d = 0;
KHTMLGlobal::registerPart( this );
setComponentData( KHTMLGlobal::componentData(), false );
init( new KHTMLView( this, parentWidget ), prof );
}
KHTMLPart::KHTMLPart( KHTMLView *view, QObject *parent, GUIProfile prof )
: KParts::ReadOnlyPart( parent )
{
d = 0;
KHTMLGlobal::registerPart( this );
setComponentData( KHTMLGlobal::componentData(), false );
assert( view );
if (!view->part())
view->setPart( this );
init( view, prof );
}
void KHTMLPart::init( KHTMLView *view, GUIProfile prof )
{
if ( prof == DefaultGUI )
setXMLFile( "khtml.rc" );
else if ( prof == BrowserViewGUI )
setXMLFile( "khtml_browser.rc" );
d = new KHTMLPartPrivate(this, parent());
d->m_view = view;
if (!parentPart()) {
QWidget *widget = new QWidget( view->parentWidget() );
widget->setObjectName("khtml_part_widget");
QVBoxLayout *layout = new QVBoxLayout( widget );
layout->setContentsMargins( 0, 0, 0, 0 );
layout->setSpacing( 0 );
widget->setLayout( layout );
d->m_topViewBar = new KHTMLViewBar( KHTMLViewBar::Top, d->m_view, widget );
d->m_bottomViewBar = new KHTMLViewBar( KHTMLViewBar::Bottom, d->m_view, widget );
layout->addWidget( d->m_topViewBar );
layout->addWidget( d->m_view );
layout->addWidget( d->m_bottomViewBar );
setWidget( widget );
widget->setFocusProxy( d->m_view );
} else {
setWidget( view );
}
d->m_guiProfile = prof;
d->m_extension = new KHTMLPartBrowserExtension( this );
d->m_extension->setObjectName( "KHTMLBrowserExtension" );
d->m_hostExtension = new KHTMLPartBrowserHostExtension( this );
d->m_statusBarExtension = new KParts::StatusBarExtension( this );
d->m_scriptableExtension = new KJS::KHTMLPartScriptable( this );
new KHTMLTextExtension( this );
new KHTMLHtmlExtension( this );
d->m_statusBarPopupLabel = 0L;
d->m_openableSuppressedPopups = 0;
d->m_paLoadImages = 0;
d->m_paDebugScript = 0;
d->m_bMousePressed = false;
d->m_bRightMousePressed = false;
d->m_bCleared = false;
if ( prof == BrowserViewGUI ) {
d->m_paViewDocument = new KAction( i18n( "View Do&cument Source" ), this );
actionCollection()->addAction( "viewDocumentSource", d->m_paViewDocument );
connect( d->m_paViewDocument, SIGNAL(triggered(bool)), this, SLOT(slotViewDocumentSource()) );
if (!parentPart()) {
d->m_paViewDocument->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_U) );
}
d->m_paViewFrame = new KAction( i18n( "View Frame Source" ), this );
actionCollection()->addAction( "viewFrameSource", d->m_paViewFrame );
connect( d->m_paViewFrame, SIGNAL(triggered(bool)), this, SLOT(slotViewFrameSource()) );
if (!parentPart()) {
d->m_paViewFrame->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U) );
}
d->m_paViewInfo = new KAction( i18n( "View Document Information" ), this );
actionCollection()->addAction( "viewPageInfo", d->m_paViewInfo );
if (!parentPart()) {
d->m_paViewInfo->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_I) );
}
connect( d->m_paViewInfo, SIGNAL(triggered(bool)), this, SLOT(slotViewPageInfo()) );
d->m_paSaveBackground = new KAction( i18n( "Save &Background Image As..." ), this );
actionCollection()->addAction( "saveBackground", d->m_paSaveBackground );
connect( d->m_paSaveBackground, SIGNAL(triggered(bool)), this, SLOT(slotSaveBackground()) );
d->m_paSaveDocument = actionCollection()->addAction( KStandardAction::SaveAs, "saveDocument",
this, SLOT(slotSaveDocument()) );
if ( parentPart() )
d->m_paSaveDocument->setShortcuts( KShortcut() ); // avoid clashes
d->m_paSaveFrame = new KAction( i18n( "Save &Frame As..." ), this );
actionCollection()->addAction( "saveFrame", d->m_paSaveFrame );
connect( d->m_paSaveFrame, SIGNAL(triggered(bool)), this, SLOT(slotSaveFrame()) );
} else {
d->m_paViewDocument = 0;
d->m_paViewFrame = 0;
d->m_paViewInfo = 0;
d->m_paSaveBackground = 0;
d->m_paSaveDocument = 0;
d->m_paSaveFrame = 0;
}
d->m_paSecurity = new KAction( i18n( "SSL" ), this );
actionCollection()->addAction( "security", d->m_paSecurity );
connect( d->m_paSecurity, SIGNAL(triggered(bool)), this, SLOT(slotSecurity()) );
d->m_paDebugRenderTree = new KAction( i18n( "Print Rendering Tree to STDOUT" ), this );
actionCollection()->addAction( "debugRenderTree", d->m_paDebugRenderTree );
connect( d->m_paDebugRenderTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugRenderTree()) );
d->m_paDebugDOMTree = new KAction( i18n( "Print DOM Tree to STDOUT" ), this );
actionCollection()->addAction( "debugDOMTree", d->m_paDebugDOMTree );
connect( d->m_paDebugDOMTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugDOMTree()) );
KAction* paDebugFrameTree = new KAction( i18n( "Print frame tree to STDOUT" ), this );
actionCollection()->addAction( "debugFrameTree", paDebugFrameTree );
connect( paDebugFrameTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugFrameTree()) );
d->m_paStopAnimations = new KAction( i18n( "Stop Animated Images" ), this );
actionCollection()->addAction( "stopAnimations", d->m_paStopAnimations );
connect( d->m_paStopAnimations, SIGNAL(triggered(bool)), this, SLOT(slotStopAnimations()) );
d->m_paSetEncoding = new KCodecAction( KDE::icon("character-set"), i18n( "Set &Encoding" ), this, true );
actionCollection()->addAction( "setEncoding", d->m_paSetEncoding );
// d->m_paSetEncoding->setDelayed( false );
connect( d->m_paSetEncoding, SIGNAL(triggered(QString)), this, SLOT(slotSetEncoding(QString)));
connect( d->m_paSetEncoding, SIGNAL(triggered(KEncodingProber::ProberType)), this, SLOT(slotAutomaticDetectionLanguage(KEncodingProber::ProberType)));
if ( KSharedConfig::openConfig()->hasGroup( "HTML Settings" ) ) {
KConfigGroup config( KSharedConfig::openConfig(), "HTML Settings" );
d->m_autoDetectLanguage = static_cast<KEncodingProber::ProberType>(config.readEntry( "AutomaticDetectionLanguage", /*static_cast<int>(language) */0));
if (d->m_autoDetectLanguage==KEncodingProber::None) {
const QByteArray name = KLocale::global()->encoding().toLower();
// kWarning() << "00000000 ";
if (name.endsWith("1251")||name.startsWith("koi")||name=="iso-8859-5")
d->m_autoDetectLanguage=KEncodingProber::Cyrillic;
else if (name.endsWith("1256")||name=="iso-8859-6")
d->m_autoDetectLanguage=KEncodingProber::Arabic;
else if (name.endsWith("1257")||name=="iso-8859-13"||name=="iso-8859-4")
d->m_autoDetectLanguage=KEncodingProber::Baltic;
else if (name.endsWith("1250")|| name=="ibm852" || name=="iso-8859-2" || name=="iso-8859-3" )
d->m_autoDetectLanguage=KEncodingProber::CentralEuropean;
else if (name.endsWith("1253")|| name=="iso-8859-7" )
d->m_autoDetectLanguage=KEncodingProber::Greek;
else if (name.endsWith("1255")|| name=="iso-8859-8" || name=="iso-8859-8-i" )
d->m_autoDetectLanguage=KEncodingProber::Hebrew;
else if (name=="jis7" || name=="eucjp" || name=="sjis" )
d->m_autoDetectLanguage=KEncodingProber::Japanese;
else if (name=="gb2312" || name=="gbk" || name=="gb18030" )
d->m_autoDetectLanguage=KEncodingProber::ChineseSimplified;
else if (name=="big5" )
d->m_autoDetectLanguage=KEncodingProber::ChineseTraditional;
else if (name=="euc-kr" )
d->m_autoDetectLanguage=KEncodingProber::Korean;
else if (name.endsWith("1254")|| name=="iso-8859-9" )
d->m_autoDetectLanguage=KEncodingProber::Turkish;
else if (name.endsWith("1252")|| name=="iso-8859-1" || name=="iso-8859-15" )
d->m_autoDetectLanguage=KEncodingProber::WesternEuropean;
else
d->m_autoDetectLanguage=KEncodingProber::Universal;
// kWarning() << "0000000end " << d->m_autoDetectLanguage << " " << KLocale::global()->encodingMib();
}
d->m_paSetEncoding->setCurrentProberType(d->m_autoDetectLanguage);
}
d->m_paUseStylesheet = new KSelectAction( i18n( "Use S&tylesheet"), this );
actionCollection()->addAction( "useStylesheet", d->m_paUseStylesheet );
connect( d->m_paUseStylesheet, SIGNAL(triggered(int)), this, SLOT(slotUseStylesheet()) );
if ( prof == BrowserViewGUI ) {
d->m_paIncZoomFactor = new KHTMLZoomFactorAction( this, true, "format-font-size-more", i18n( "Enlarge Font" ), this );
actionCollection()->addAction( "incFontSizes", d->m_paIncZoomFactor );
connect(d->m_paIncZoomFactor, SIGNAL(triggered(bool)), SLOT(slotIncFontSizeFast()));
d->m_paIncZoomFactor->setWhatsThis( i18n( "<qt>Enlarge Font<br /><br />"
"Make the font in this window bigger. "
"Click and hold down the mouse button for a menu with all available font sizes.</qt>" ) );
d->m_paDecZoomFactor = new KHTMLZoomFactorAction( this, false, "format-font-size-less", i18n( "Shrink Font" ), this );
actionCollection()->addAction( "decFontSizes", d->m_paDecZoomFactor );
connect(d->m_paDecZoomFactor, SIGNAL(triggered(bool)), SLOT(slotDecFontSizeFast()));
d->m_paDecZoomFactor->setWhatsThis( i18n( "<qt>Shrink Font<br /><br />"
"Make the font in this window smaller. "
"Click and hold down the mouse button for a menu with all available font sizes.</qt>" ) );
if (!parentPart()) {
// For framesets, this action also affects frames, so only
// the frameset needs to define a shortcut for the action.
// TODO: Why also CTRL+=? Because of http://trolltech.com/developer/knowledgebase/524/?
// Nobody else does it...
d->m_paIncZoomFactor->setShortcut( KShortcut("CTRL++; CTRL+=") );
d->m_paDecZoomFactor->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_Minus) );
}
}
d->m_paFind = actionCollection()->addAction( KStandardAction::Find, "find", this, SLOT(slotFind()) );
d->m_paFind->setWhatsThis( i18n( "<qt>Find text<br /><br />"
"Shows a dialog that allows you to find text on the displayed page.</qt>" ) );
d->m_paFindNext = actionCollection()->addAction( KStandardAction::FindNext, "findNext", this, SLOT(slotFindNext()) );
d->m_paFindNext->setWhatsThis( i18n( "<qt>Find next<br /><br />"
"Find the next occurrence of the text that you "
"have found using the <b>Find Text</b> function.</qt>" ) );
d->m_paFindPrev = actionCollection()->addAction( KStandardAction::FindPrev, "findPrevious",
this, SLOT(slotFindPrev()) );
d->m_paFindPrev->setWhatsThis( i18n( "<qt>Find previous<br /><br />"
"Find the previous occurrence of the text that you "
"have found using the <b>Find Text</b> function.</qt>" ) );
// These two actions aren't visible in the menus, but exist for the (configurable) shortcut
d->m_paFindAheadText = new KAction( i18n("Find Text as You Type"), this );
actionCollection()->addAction( "findAheadText", d->m_paFindAheadText );
d->m_paFindAheadText->setShortcuts( KShortcut( '/' ) );
d->m_paFindAheadText->setHelpText(i18n("This shortcut shows the find bar, for finding text in the displayed page. It cancels the effect of \"Find Links as You Type\", which sets the \"Find links only\" option."));
connect( d->m_paFindAheadText, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadText()) );
d->m_paFindAheadLinks = new KAction( i18n("Find Links as You Type"), this );
actionCollection()->addAction( "findAheadLink", d->m_paFindAheadLinks );
// The issue is that it sets the (sticky) option FindLinksOnly, so
// if you trigger this shortcut once by mistake, Esc and Ctrl+F will still have the option set.
// Better let advanced users configure a shortcut for this advanced option
//d->m_paFindAheadLinks->setShortcuts( KShortcut( '\'' ) );
d->m_paFindAheadLinks->setHelpText(i18n("This shortcut shows the find bar, and sets the option \"Find links only\"."));
connect( d->m_paFindAheadLinks, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadLink()) );
if ( parentPart() )
{
d->m_paFind->setShortcuts( KShortcut() ); // avoid clashes
d->m_paFindNext->setShortcuts( KShortcut() ); // avoid clashes
d->m_paFindPrev->setShortcuts( KShortcut() ); // avoid clashes
d->m_paFindAheadText->setShortcuts( KShortcut());
d->m_paFindAheadLinks->setShortcuts( KShortcut());
}
d->m_paPrintFrame = new KAction( i18n( "Print Frame..." ), this );
actionCollection()->addAction( "printFrame", d->m_paPrintFrame );
d->m_paPrintFrame->setIcon( KDE::icon( "document-print-frame" ) );
connect( d->m_paPrintFrame, SIGNAL(triggered(bool)), this, SLOT(slotPrintFrame()) );
d->m_paPrintFrame->setWhatsThis( i18n( "<qt>Print Frame<br /><br />"
"Some pages have several frames. To print only a single frame, click "
"on it and then use this function.</qt>" ) );
// Warning: The name selectAll is used hardcoded by some 3rd parties to remove the
// shortcut for selectAll so they do not get ambigous shortcuts. Renaming it
// will either crash or render useless that workaround. It would be better
// to use the name KStandardAction::name(KStandardAction::SelectAll) but we
// can't for the same reason.
d->m_paSelectAll = actionCollection()->addAction( KStandardAction::SelectAll, "selectAll",
this, SLOT(slotSelectAll()) );
if ( parentPart() ) // Only the frameset has the shortcut, but the slot uses the current frame.
d->m_paSelectAll->setShortcuts( KShortcut() ); // avoid clashes
d->m_paToggleCaretMode = new KToggleAction(i18n("Toggle Caret Mode"), this );
actionCollection()->addAction( "caretMode", d->m_paToggleCaretMode );
d->m_paToggleCaretMode->setShortcut( QKeySequence(Qt::Key_F7) );
connect( d->m_paToggleCaretMode, SIGNAL(triggered(bool)), this, SLOT(slotToggleCaretMode()) );
d->m_paToggleCaretMode->setChecked(isCaretMode());
if (parentPart())
d->m_paToggleCaretMode->setShortcut(KShortcut()); // avoid clashes
// set the default java(script) flags according to the current host.
d->m_bOpenMiddleClick = d->m_settings->isOpenMiddleClickEnabled();
d->m_bJScriptEnabled = d->m_settings->isJavaScriptEnabled();
setDebugScript( d->m_settings->isJavaScriptDebugEnabled() );
d->m_bJavaEnabled = d->m_settings->isJavaEnabled();
d->m_bPluginsEnabled = d->m_settings->isPluginsEnabled();
// Set the meta-refresh flag...
d->m_metaRefreshEnabled = d->m_settings->isAutoDelayedActionsEnabled ();
KHTMLSettings::KSmoothScrollingMode ssm = d->m_settings->smoothScrolling();
if (ssm == KHTMLSettings::KSmoothScrollingDisabled)
d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMDisabled);
else if (ssm == KHTMLSettings::KSmoothScrollingWhenEfficient)
d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMWhenEfficient);
else
d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMEnabled);
if (d->m_bDNSPrefetchIsDefault && !onlyLocalReferences()) {
KHTMLSettings::KDNSPrefetch dpm = d->m_settings->dnsPrefetch();
if (dpm == KHTMLSettings::KDNSPrefetchDisabled)
d->m_bDNSPrefetch = DNSPrefetchDisabled;
else if (dpm == KHTMLSettings::KDNSPrefetchOnlyWWWAndSLD)
d->m_bDNSPrefetch = DNSPrefetchOnlyWWWAndSLD;
else
d->m_bDNSPrefetch = DNSPrefetchEnabled;
}
if (!KHTMLPartPrivate::s_dnsInitialised && d->m_bDNSPrefetch != DNSPrefetchDisabled) {
KIO::HostInfo::setCacheSize( sDNSCacheSize );
KIO::HostInfo::setTTL( sDNSTTLSeconds );
KHTMLPartPrivate::s_dnsInitialised = true;
}
// all shortcuts should only be active, when this part has focus
foreach ( QAction *action, actionCollection ()->actions () ) {
action->setShortcutContext ( Qt::WidgetWithChildrenShortcut );
}
actionCollection()->associateWidget(view);
connect( view, SIGNAL(zoomView(int)), SLOT(slotZoomView(int)) );
connect( this, SIGNAL(completed()),
this, SLOT(updateActions()) );
connect( this, SIGNAL(completed(bool)),
this, SLOT(updateActions()) );
connect( this, SIGNAL(started(KIO::Job*)),
this, SLOT(updateActions()) );
// #### FIXME: the process wide loader is going to signal every part about every loaded object.
// That's quite inefficient. Should be per-document-tree somehow. Even signaling to
// child parts that a request from an ancestor has loaded is inefficent..
connect( khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)) );
connect( khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)) );
connect( khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)) );
connect ( &d->m_progressUpdateTimer, SIGNAL(timeout()), this, SLOT(slotProgressUpdate()) );
findTextBegin(); //reset find variables
connect( &d->m_redirectionTimer, SIGNAL(timeout()),
this, SLOT(slotRedirect()) );
if (QDBusConnection::sessionBus().isConnected()) {
new KHTMLPartIface(this); // our "adaptor"
for (int i = 1; ; ++i)
if (QDBusConnection::sessionBus().registerObject(QString("/KHTML/%1/widget").arg(i), this))
break;
else if (i == 0xffff)
kFatal() << "Something is very wrong in KHTMLPart!";
}
if (prof == BrowserViewGUI && !parentPart())
loadPlugins();
// "khtml" catalog does not exist, our translations are in kdelibs.
// removing this catalog from KLocalizedString prevents problems
// with changing the language in applications at runtime -Thomas Reitelbach
// DF: a better fix would be to set the right catalog name in the KComponentData!
KLocalizedString::removeCatalog("khtml");
}
KHTMLPart::~KHTMLPart()
{
kDebug(6050) << this;
KConfigGroup config( KSharedConfig::openConfig(), "HTML Settings" );
config.writeEntry( "AutomaticDetectionLanguage", int(d->m_autoDetectLanguage) );
if (d->m_manager) { // the PartManager for this part's children
d->m_manager->removePart(this);
}
slotWalletClosed();
if (!parentPart()) { // only delete it if the top khtml_part closes
removeJSErrorExtension();
}
stopAutoScroll();
d->m_redirectionTimer.stop();
if (!d->m_bComplete)
closeUrl();
disconnect( khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)) );
disconnect( khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)) );
disconnect( khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)) );
clear();
hide();
if ( d->m_view )
{
d->m_view->m_part = 0;
}
// Have to delete this here since we forward declare it in khtmlpart_p and
// at least some compilers won't call the destructor in this case.
delete d->m_jsedlg;
d->m_jsedlg = 0;
if (!parentPart()) // only delete d->m_frame if the top khtml_part closes
delete d->m_frame;
else if (d->m_frame && d->m_frame->m_run) // for kids, they may get detached while
d->m_frame->m_run.data()->abort(); // resolving mimetype; cancel that if needed
delete d; d = 0;
KHTMLGlobal::deregisterPart( this );
}
bool KHTMLPart::restoreURL( const KUrl &url )
{
kDebug( 6050 ) << url;
d->m_redirectionTimer.stop();
/*
* That's not a good idea as it will call closeUrl() on all
* child frames, preventing them from further loading. This
* method gets called from restoreState() in case of a full frameset
* restoral, and restoreState() calls closeUrl() before restoring
* anyway.
kDebug( 6050 ) << "closing old URL";
closeUrl();
*/
d->m_bComplete = false;
d->m_bLoadEventEmitted = false;
d->m_workingURL = url;
// set the java(script) flags according to the current host.
d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
setDebugScript( KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled() );
d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
setUrl(url);
d->m_restoreScrollPosition = true;
disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
KHTMLPageCache::self()->fetchData( d->m_cacheId, this, SLOT(slotRestoreData(QByteArray)));
emit started( 0L );
return true;
}
bool KHTMLPartPrivate::isLocalAnchorJump( const KUrl& url )
{
// kio_help actually uses fragments to identify different pages, so
// always reload with it.
if (url.scheme() == QLatin1String("help"))
return false;
return url.hasFragment() && url.equals( q->url(),
KUrl::CompareWithoutTrailingSlash | KUrl::CompareWithoutFragment | KUrl::AllowEmptyPath );
}
void KHTMLPartPrivate::executeAnchorJump( const KUrl& url, bool lockHistory )
{
// Note: we want to emit openUrlNotify first thing, to make the history capture the old state.
if (!lockHistory)
emit m_extension->openUrlNotify();
const QString &oldRef = KUrl(q->url()).ref();
const QString &newRef = KUrl(url).ref();
if ((oldRef != newRef) || (oldRef.isNull() && newRef.isEmpty())) {
DOM::HashChangeEventImpl *evImpl = new DOM::HashChangeEventImpl();
evImpl->initHashChangeEvent("hashchange",
true, //bubble
false, //cancelable
q->url().toString(), //oldURL
url.toString() //newURL
);
m_doc->dispatchWindowEvent(evImpl);
}
if ( !q->gotoAnchor( url.encodedHtmlRef()) )
q->gotoAnchor( url.htmlRef() );
q->setUrl(url);
emit m_extension->setLocationBarUrl( url.toDisplayString() );
}
bool KHTMLPart::openUrl(const QUrl &_url)
{
KUrl url(_url);
kDebug( 6050 ) << this << "opening" << url;
// Wallet forms are per page, so clear it when loading a different page if we
// are not an iframe (because we store walletforms only on the topmost part).
if(!parentPart())
d->m_walletForms.clear();
d->m_redirectionTimer.stop();
// check to see if this is an "error://" URL. This is caused when an error
// occurs before this part was loaded (e.g. KonqRun), and is passed to
// khtmlpart so that it can display the error.
if ( url.scheme() == "error" ) {
closeUrl();
if( d->m_bJScriptEnabled ) {
d->m_statusBarText[BarOverrideText].clear();
d->m_statusBarText[BarDefaultText].clear();
}
/**
* The format of the error url is that two variables are passed in the query:
* error = int kio error code, errText = QString error text from kio
* and the URL where the error happened is passed as a sub URL.
*/
QList<QUrl> urls = KUrl::split( url );
//kDebug(6050) << "Handling error URL. URL count:" << urls.count();
if ( !urls.isEmpty() ) {
const KUrl mainURL = urls.first();
int error = mainURL.queryItem( "error" ).toInt();
// error=0 isn't a valid error code, so 0 means it's missing from the URL
if ( error == 0 ) error = KIO::ERR_UNKNOWN;
const QString errorText = mainURL.queryItem( "errText" );
urls.pop_front();
d->m_workingURL = KUrl::join( urls );
//kDebug(6050) << "Emitting fixed URL " << d->m_workingURL;
emit d->m_extension->setLocationBarUrl( d->m_workingURL.toDisplayString() );
htmlError( error, errorText, d->m_workingURL );
return true;
}
}
if (!parentPart()) { // only do it for toplevel part
QString host = url.isLocalFile() ? "localhost" : url.host();
QString userAgent = KProtocolManager::userAgentForHost(host);
if (userAgent != KProtocolManager::userAgentForHost(QString())) {
if (!d->m_statusBarUALabel) {
d->m_statusBarUALabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
d->m_statusBarUALabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
d->m_statusBarUALabel->setUseCursor(false);
d->m_statusBarExtension->addStatusBarItem(d->m_statusBarUALabel, 0, false);
d->m_statusBarUALabel->setPixmap(SmallIcon("preferences-web-browser-identification"));
}
d->m_statusBarUALabel->setToolTip(i18n("The fake user-agent '%1' is in use.", userAgent));
} else if (d->m_statusBarUALabel) {
d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarUALabel);
delete d->m_statusBarUALabel;
d->m_statusBarUALabel = 0L;
}
}
KParts::BrowserArguments browserArgs( d->m_extension->browserArguments() );
KParts::OpenUrlArguments args( arguments() );
// in case
// a) we have no frameset (don't test m_frames.count(), iframes get in there)
// b) the url is identical with the currently displayed one (except for the htmlref!)
// c) the url request is not a POST operation and
// d) the caller did not request to reload the page
// e) there was no HTTP redirection meanwhile (testcase: webmin's software/tree.cgi)
// => we don't reload the whole document and
// we just jump to the requested html anchor
bool isFrameSet = false;
if ( d->m_doc && d->m_doc->isHTMLDocument() ) {
HTMLDocumentImpl* htmlDoc = static_cast<HTMLDocumentImpl*>(d->m_doc);
isFrameSet = htmlDoc->body() && (htmlDoc->body()->id() == ID_FRAMESET);
}
if (isFrameSet && d->isLocalAnchorJump(url) && browserArgs.softReload)
{
QList<khtml::ChildFrame*>::Iterator it = d->m_frames.begin();
const QList<khtml::ChildFrame*>::Iterator end = d->m_frames.end();
for (; it != end; ++it) {
KHTMLPart* const part = qobject_cast<KHTMLPart *>( (*it)->m_part.data() );
if (part)
{
// We are reloading frames to make them jump into offsets.
KParts::OpenUrlArguments partargs( part->arguments() );
partargs.setReload( true );
part->setArguments( partargs );
part->openUrl( part->url() );
}
}/*next it*/
return true;
}
if ( url.hasFragment() && !isFrameSet )
{
bool noReloadForced = !args.reload() && !browserArgs.redirectedRequest() && !browserArgs.doPost();
if ( noReloadForced && d->isLocalAnchorJump(url) )
{
kDebug( 6050 ) << "jumping to anchor. m_url = " << url;
setUrl(url);
emit started( 0 );
if ( !gotoAnchor( url.encodedHtmlRef()) )
gotoAnchor( url.htmlRef() );
d->m_bComplete = true;
if (d->m_doc)
d->m_doc->setParsing(false);
kDebug( 6050 ) << "completed...";
emit completed();
return true;
}
}
// Save offset of viewport when page is reloaded to be compliant
// to every other capable browser out there.
if (args.reload()) {
args.setXOffset( d->m_view->contentsX() );
args.setYOffset( d->m_view->contentsY() );
setArguments(args);
}
if (!d->m_restored)
closeUrl();
d->m_restoreScrollPosition = d->m_restored;
disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
// Classify the mimetype. Some, like images and plugins are handled
// by wrapping things up in tags, so we want to plain output the HTML,
// and not start the job and all that (since we would want the
// KPart or whatever to load it).
// This is also the only place we need to do this, as it's for
// internal iframe use, not any other clients.
MimeType type = d->classifyMimeType(args.mimeType());
if (type == MimeImage || type == MimeOther) {
begin(url, args.xOffset(), args.yOffset());
write(QString::fromLatin1("<html><head></head><body>"));
if (type == MimeImage)
write(QString::fromLatin1("<img "));
else
write(QString::fromLatin1("<embed "));
write(QString::fromLatin1("src=\""));
assert(url.toString().indexOf('"') == -1);
write(url.toString());
write(QString::fromLatin1("\">"));
end();
return true;
}
// initializing m_url to the new url breaks relative links when opening such a link after this call and _before_ begin() is called (when the first
// data arrives) (Simon)
d->m_workingURL = url;
if(url.scheme().startsWith( "http" ) && !url.host().isEmpty() &&
url.path().isEmpty()) {
d->m_workingURL.setPath("/");
emit d->m_extension->setLocationBarUrl( d->m_workingURL.toDisplayString() );
}
setUrl(d->m_workingURL);
QMap<QString,QString>& metaData = args.metaData();
metaData.insert("main_frame_request", parentPart() == 0 ? "TRUE" : "FALSE" );
metaData.insert("ssl_parent_ip", d->m_ssl_parent_ip);
metaData.insert("ssl_parent_cert", d->m_ssl_parent_cert);
metaData.insert("PropagateHttpHeader", "true");
metaData.insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE" );
metaData.insert("ssl_activate_warnings", "TRUE" );
metaData.insert("cross-domain", toplevelURL().toString());
if (d->m_restored)
{
metaData.insert("referrer", d->m_pageReferrer);
d->m_cachePolicy = KIO::CC_Cache;
}
else if (args.reload() && !browserArgs.softReload)
d->m_cachePolicy = KIO::CC_Reload;
else
d->m_cachePolicy = KProtocolManager::cacheControl();
if ( browserArgs.doPost() && (url.scheme().startsWith("http")) )
{
d->m_job = KIO::http_post( url, browserArgs.postData, KIO::HideProgressInfo );
d->m_job->addMetaData("content-type", browserArgs.contentType() );
}
else
{
d->m_job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
d->m_job->addMetaData("cache", KIO::getCacheControlString(d->m_cachePolicy));
}
if (widget())
d->m_job->ui()->setWindow(widget()->topLevelWidget());
d->m_job->addMetaData(metaData);
connect( d->m_job, SIGNAL(result(KJob*)),
SLOT(slotFinished(KJob*)) );
connect( d->m_job, SIGNAL(data(KIO::Job*,QByteArray)),
SLOT(slotData(KIO::Job*,QByteArray)) );
connect ( d->m_job, SIGNAL(infoMessage(KJob*,QString,QString)),
SLOT(slotInfoMessage(KJob*,QString)) );
connect( d->m_job, SIGNAL(redirection(KIO::Job*,KUrl)),
SLOT(slotRedirection(KIO::Job*,KUrl)) );
d->m_bComplete = false;
d->m_bLoadEventEmitted = false;
// delete old status bar msg's from kjs (if it _was_ activated on last URL)
if( d->m_bJScriptEnabled ) {
d->m_statusBarText[BarOverrideText].clear();
d->m_statusBarText[BarDefaultText].clear();
}
// set the javascript flags according to the current url
d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
setDebugScript( KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled() );
d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
connect( d->m_job, SIGNAL(speed(KJob*,ulong)),
this, SLOT(slotJobSpeed(KJob*,ulong)) );
connect( d->m_job, SIGNAL(percent(KJob*,ulong)),
this, SLOT(slotJobPercent(KJob*,ulong)) );
connect( d->m_job, SIGNAL(result(KJob*)),
this, SLOT(slotJobDone(KJob*)) );
d->m_jobspeed = 0;
// If this was an explicit reload and the user style sheet should be used,
// do a stat to see whether the stylesheet was changed in the meanwhile.
if ( args.reload() && !settings()->userStyleSheet().isEmpty() ) {
KUrl url( settings()->userStyleSheet() );
KIO::StatJob *job = KIO::stat( url, KIO::HideProgressInfo );
connect( job, SIGNAL(result(KJob*)),
this, SLOT(slotUserSheetStatDone(KJob*)) );
}
startingJob( d->m_job );
emit started( 0L );
return true;
}
bool KHTMLPart::closeUrl()
{
if ( d->m_job )
{
KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
d->m_job->kill();
d->m_job = 0;
}
if ( d->m_doc && d->m_doc->isHTMLDocument() ) {
HTMLDocumentImpl* hdoc = static_cast<HTMLDocumentImpl*>( d->m_doc );
if ( hdoc->body() && d->m_bLoadEventEmitted ) {
hdoc->body()->dispatchWindowEvent( EventImpl::UNLOAD_EVENT, false, false );
if ( d->m_doc )
d->m_doc->updateRendering();
d->m_bLoadEventEmitted = false;
}
}
d->m_bComplete = true; // to avoid emitting completed() in slotFinishedParsing() (David)
d->m_bLoadEventEmitted = true; // don't want that one either
d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
KHTMLPageCache::self()->cancelFetch(this);
if ( d->m_doc && d->m_doc->parsing() )
{
kDebug( 6050 ) << " was still parsing... calling end ";
slotFinishedParsing();
d->m_doc->setParsing(false);
}
if ( !d->m_workingURL.isEmpty() )
{
// Aborted before starting to render
kDebug( 6050 ) << "Aborted before starting to render, reverting location bar to " << url();
emit d->m_extension->setLocationBarUrl( url().toDisplayString() );
}
d->m_workingURL = KUrl();
if ( d->m_doc && d->m_doc->docLoader() )
khtml::Cache::loader()->cancelRequests( d->m_doc->docLoader() );
// tell all subframes to stop as well
{
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it )
{
if ( (*it)->m_run )
(*it)->m_run.data()->abort();
if ( !( *it )->m_part.isNull() )
( *it )->m_part.data()->closeUrl();
}
}
// tell all objects to stop as well
{
ConstFrameIt it = d->m_objects.constBegin();
const ConstFrameIt end = d->m_objects.constEnd();
for (; it != end; ++it)
{
if ( !( *it )->m_part.isNull() )
( *it )->m_part.data()->closeUrl();
}
}
// Stop any started redirections as well!! (DA)
if ( d && d->m_redirectionTimer.isActive() )
d->m_redirectionTimer.stop();
// null node activated.
emit nodeActivated(Node());
// make sure before clear() runs, we pop out of a dialog's message loop
if ( d->m_view )
d->m_view->closeChildDialogs();
return true;
}
DOM::HTMLDocument KHTMLPart::htmlDocument() const
{
if (d->m_doc && d->m_doc->isHTMLDocument())
return static_cast<HTMLDocumentImpl*>(d->m_doc);
else
return static_cast<HTMLDocumentImpl*>(0);
}
DOM::Document KHTMLPart::document() const
{
return d->m_doc;
}
QString KHTMLPart::documentSource() const
{
QString sourceStr;
if ( !( url().isLocalFile() ) && KHTMLPageCache::self()->isComplete( d->m_cacheId ) )
{
QByteArray sourceArray;
QDataStream dataStream( &sourceArray, QIODevice::WriteOnly );
KHTMLPageCache::self()->saveData( d->m_cacheId, &dataStream );
QTextStream stream( sourceArray, QIODevice::ReadOnly );
stream.setCodec( QTextCodec::codecForName( encoding().toLatin1().constData() ) );
sourceStr = stream.readAll();
} else
{
QString tmpFile;
if( KIO::NetAccess::download( url(), tmpFile, NULL ) )
{
QFile f( tmpFile );
if ( f.open( QIODevice::ReadOnly ) )
{
QTextStream stream( &f );
stream.setCodec( QTextCodec::codecForName( encoding().toLatin1().constData() ) );
sourceStr = stream.readAll();
f.close();
}
KIO::NetAccess::removeTempFile( tmpFile );
}
}
return sourceStr;
}
KParts::BrowserExtension *KHTMLPart::browserExtension() const
{
return d->m_extension;
}
KParts::BrowserHostExtension *KHTMLPart::browserHostExtension() const
{
return d->m_hostExtension;
}
KHTMLView *KHTMLPart::view() const
{
return d->m_view;
}
KHTMLViewBar *KHTMLPart::pTopViewBar() const
{
if (const_cast<KHTMLPart*>(this)->parentPart())
return const_cast<KHTMLPart*>(this)->parentPart()->pTopViewBar();
return d->m_topViewBar;
}
KHTMLViewBar *KHTMLPart::pBottomViewBar() const
{
if (const_cast<KHTMLPart*>(this)->parentPart())
return const_cast<KHTMLPart*>(this)->parentPart()->pBottomViewBar();
return d->m_bottomViewBar;
}
void KHTMLPart::setStatusMessagesEnabled( bool enable )
{
d->m_statusMessagesEnabled = enable;
}
KJS::Interpreter *KHTMLPart::jScriptInterpreter()
{
KJSProxy *proxy = jScript();
if (!proxy || proxy->paused())
return 0;
return proxy->interpreter();
}
bool KHTMLPart::statusMessagesEnabled() const
{
return d->m_statusMessagesEnabled;
}
void KHTMLPart::setJScriptEnabled( bool enable )
{
if ( !enable && jScriptEnabled() && d->m_frame && d->m_frame->m_jscript ) {
d->m_frame->m_jscript->clear();
}
d->m_bJScriptForce = enable;
d->m_bJScriptOverride = true;
}
bool KHTMLPart::jScriptEnabled() const
{
if(onlyLocalReferences()) return false;
if ( d->m_bJScriptOverride )
return d->m_bJScriptForce;
return d->m_bJScriptEnabled;
}
void KHTMLPart::setDNSPrefetch( DNSPrefetch pmode )
{
d->m_bDNSPrefetch = pmode;
d->m_bDNSPrefetchIsDefault = false;
}
KHTMLPart::DNSPrefetch KHTMLPart::dnsPrefetch() const
{
if (onlyLocalReferences())
return DNSPrefetchDisabled;
return d->m_bDNSPrefetch;
}
void KHTMLPart::setMetaRefreshEnabled( bool enable )
{
d->m_metaRefreshEnabled = enable;
}
bool KHTMLPart::metaRefreshEnabled() const
{
return d->m_metaRefreshEnabled;
}
KJSProxy *KHTMLPart::jScript()
{
if (!jScriptEnabled()) return 0;
if ( !d->m_frame ) {
KHTMLPart * p = parentPart();
if (!p) {
d->m_frame = new khtml::ChildFrame;
d->m_frame->m_part = this;
} else {
ConstFrameIt it = p->d->m_frames.constBegin();
const ConstFrameIt end = p->d->m_frames.constEnd();
for (; it != end; ++it)
if ((*it)->m_part.data() == this) {
d->m_frame = *it;
break;
}
}
if ( !d->m_frame )
return 0;
}
if ( !d->m_frame->m_jscript )
d->m_frame->m_jscript = new KJSProxy(d->m_frame);
d->m_frame->m_jscript->setDebugEnabled(d->m_bJScriptDebugEnabled);
return d->m_frame->m_jscript;
}
QVariant KHTMLPart::crossFrameExecuteScript(const QString& target, const QString& script)
{
KHTMLPart* destpart = this;
QString trg = target.toLower();
if (target == "_top") {
while (destpart->parentPart())
destpart = destpart->parentPart();
}
else if (target == "_parent") {
if (parentPart())
destpart = parentPart();
}
else if (target == "_self" || target == "_blank") {
// we always allow these
}
else {
destpart = findFrame(target);
if (!destpart)
destpart = this;
}
// easy way out?
if (destpart == this)
return executeScript(DOM::Node(), script);
// now compare the domains
if (destpart->checkFrameAccess(this))
return destpart->executeScript(DOM::Node(), script);
// eww, something went wrong. better execute it in our frame
return executeScript(DOM::Node(), script);
}
//Enable this to see all JS scripts being executed
//#define KJS_VERBOSE
KJSErrorDlg *KHTMLPart::jsErrorExtension() {
if (!d->m_settings->jsErrorsEnabled()) {
return 0L;
}
if (parentPart()) {
return parentPart()->jsErrorExtension();
}
if (!d->m_statusBarJSErrorLabel) {
d->m_statusBarJSErrorLabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
d->m_statusBarJSErrorLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
d->m_statusBarJSErrorLabel->setUseCursor(false);
d->m_statusBarExtension->addStatusBarItem(d->m_statusBarJSErrorLabel, 0, false);
d->m_statusBarJSErrorLabel->setToolTip(i18n("This web page contains coding errors."));
d->m_statusBarJSErrorLabel->setPixmap(SmallIcon("script-error"));
connect(d->m_statusBarJSErrorLabel, SIGNAL(leftClickedUrl()), SLOT(launchJSErrorDialog()));
connect(d->m_statusBarJSErrorLabel, SIGNAL(rightClickedUrl()), SLOT(jsErrorDialogContextMenu()));
}
if (!d->m_jsedlg) {
d->m_jsedlg = new KJSErrorDlg;
d->m_jsedlg->setURL(url().toDisplayString());
if (widget()->style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons, 0, widget())) {
d->m_jsedlg->_clear->setIcon(KDE::icon("edit-clear-locationbar-ltr"));
d->m_jsedlg->_close->setIcon(KDE::icon("window-close"));
}
}
return d->m_jsedlg;
}
void KHTMLPart::removeJSErrorExtension() {
if (parentPart()) {
parentPart()->removeJSErrorExtension();
return;
}
if (d->m_statusBarJSErrorLabel != 0) {
d->m_statusBarExtension->removeStatusBarItem( d->m_statusBarJSErrorLabel );
delete d->m_statusBarJSErrorLabel;
d->m_statusBarJSErrorLabel = 0;
}
delete d->m_jsedlg;
d->m_jsedlg = 0;
}
void KHTMLPart::disableJSErrorExtension() {
removeJSErrorExtension();
// These two lines are really kind of hacky, and it sucks to do this inside
// KHTML but I don't know of anything that's reasonably easy as an alternative
// right now. It makes me wonder if there should be a more clean way to
// contact all running "KHTML" instance as opposed to Konqueror instances too.
d->m_settings->setJSErrorsEnabled(false);
emit configurationChanged();
}
void KHTMLPart::jsErrorDialogContextMenu() {
KMenu *m = new KMenu(0L);
m->addAction(i18n("&Hide Errors"), this, SLOT(removeJSErrorExtension()));
m->addAction(i18n("&Disable Error Reporting"), this, SLOT(disableJSErrorExtension()));
m->popup(QCursor::pos());
}
void KHTMLPart::launchJSErrorDialog() {
KJSErrorDlg *dlg = jsErrorExtension();
if (dlg) {
dlg->show();
dlg->raise();
}
}
void KHTMLPart::launchJSConfigDialog() {
QStringList args;
args << "khtml_java_js";
KToolInvocation::kdeinitExec( "kcmshell4", args );
}
QVariant KHTMLPart::executeScript(const QString& filename, int baseLine, const DOM::Node& n, const QString& script)
{
#ifdef KJS_VERBOSE
// The script is now printed by KJS's Parser::parse
kDebug(6070) << "executeScript: caller='" << objectName() << "' filename=" << filename << " baseLine=" << baseLine /*<< " script=" << script*/;
#endif
KJSProxy *proxy = jScript();
if (!proxy || proxy->paused())
return QVariant();
//Make sure to initialize the interpreter before creating Completion
(void)proxy->interpreter();
KJS::Completion comp;
QVariant ret = proxy->evaluate(filename, baseLine, script, n, &comp);
/*
* Error handling
*/
if (comp.complType() == KJS::Throw && comp.value()) {
KJSErrorDlg *dlg = jsErrorExtension();
if (dlg) {
QString msg = KJS::exceptionToString(
proxy->interpreter()->globalExec(), comp.value());
dlg->addError(i18n("<qt><b>Error</b>: %1: %2</qt>",
Qt::escape(filename), Qt::escape(msg)));
}
}
// Handle immediate redirects now (e.g. location='foo')
if ( !d->m_redirectURL.isEmpty() && d->m_delayRedirect == -1 )
{
kDebug(6070) << "executeScript done, handling immediate redirection NOW";
// Must abort tokenizer, no further script must execute.
khtml::Tokenizer* t = d->m_doc->tokenizer();
if(t)
t->abort();
d->m_redirectionTimer.setSingleShot( true );
d->m_redirectionTimer.start( 0 );
}
return ret;
}
QVariant KHTMLPart::executeScript( const QString &script )
{
return executeScript( DOM::Node(), script );
}
QVariant KHTMLPart::executeScript( const DOM::Node &n, const QString &script )
{
#ifdef KJS_VERBOSE
kDebug(6070) << "caller=" << objectName() << "node=" << n.nodeName().string().toLatin1().constData() << "(" << (n.isNull() ? 0 : n.nodeType()) << ") " /* << script */;
#endif
KJSProxy *proxy = jScript();
if (!proxy || proxy->paused())
return QVariant();
(void)proxy->interpreter();//Make sure stuff is initialized
++(d->m_runningScripts);
KJS::Completion comp;
const QVariant ret = proxy->evaluate( QString(), 1, script, n, &comp );
--(d->m_runningScripts);
/*
* Error handling
*/
if (comp.complType() == KJS::Throw && comp.value()) {
KJSErrorDlg *dlg = jsErrorExtension();
if (dlg) {
QString msg = KJS::exceptionToString(
proxy->interpreter()->globalExec(), comp.value());
dlg->addError(i18n("<qt><b>Error</b>: node %1: %2</qt>",
n.nodeName().string(), Qt::escape(msg)));
}
}
if (!d->m_runningScripts && d->m_doc && !d->m_doc->parsing() && d->m_submitForm )
submitFormAgain();
#ifdef KJS_VERBOSE
kDebug(6070) << "done";
#endif
return ret;
}
void KHTMLPart::setJavaEnabled( bool enable )
{
d->m_bJavaForce = enable;
d->m_bJavaOverride = true;
}
bool KHTMLPart::javaEnabled() const
{
if (onlyLocalReferences()) return false;
if( d->m_bJavaOverride )
return d->m_bJavaForce;
return d->m_bJavaEnabled;
}
void KHTMLPart::setPluginsEnabled( bool enable )
{
d->m_bPluginsForce = enable;
d->m_bPluginsOverride = true;
}
bool KHTMLPart::pluginsEnabled() const
{
if (onlyLocalReferences()) return false;
if ( d->m_bPluginsOverride )
return d->m_bPluginsForce;
return d->m_bPluginsEnabled;
}
static int s_DOMTreeIndentLevel = 0;
void KHTMLPart::slotDebugDOMTree()
{
if ( d->m_doc )
qDebug("%s", d->m_doc->toString().string().toLatin1().constData());
// Now print the contents of the frames that contain HTML
const int indentLevel = s_DOMTreeIndentLevel++;
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it )
if ( !( *it )->m_part.isNull() && (*it)->m_part.data()->inherits( "KHTMLPart" ) ) {
KParts::ReadOnlyPart* const p = ( *it )->m_part.data();
kDebug(6050) << QString().leftJustified(s_DOMTreeIndentLevel*4,' ') << "FRAME " << p->objectName() << " ";
static_cast<KHTMLPart*>( p )->slotDebugDOMTree();
}
s_DOMTreeIndentLevel = indentLevel;
}
void KHTMLPart::slotDebugScript()
{
if (jScript())
jScript()->showDebugWindow();
}
void KHTMLPart::slotDebugRenderTree()
{
#ifndef NDEBUG
if ( d->m_doc ) {
d->m_doc->renderer()->printTree();
// dump out the contents of the rendering & DOM trees
// QString dumps;
// QTextStream outputStream(&dumps,QIODevice::WriteOnly);
// d->m_doc->renderer()->layer()->dump( outputStream );
// kDebug() << "dump output:" << "\n" + dumps;
// d->m_doc->renderer()->printLineBoxTree();
}
#endif
}
void KHTMLPart::slotDebugFrameTree()
{
khtml::ChildFrame::dumpFrameTree(this);
}
void KHTMLPart::slotStopAnimations()
{
stopAnimations();
}
void KHTMLPart::setAutoloadImages( bool enable )
{
if ( d->m_doc && d->m_doc->docLoader()->autoloadImages() == enable )
return;
if ( d->m_doc )
d->m_doc->docLoader()->setAutoloadImages( enable );
unplugActionList( "loadImages" );
if ( enable ) {
delete d->m_paLoadImages;
d->m_paLoadImages = 0;
}
else if ( !d->m_paLoadImages ) {
d->m_paLoadImages = new KAction( i18n( "Display Images on Page" ), this );
actionCollection()->addAction( "loadImages", d->m_paLoadImages );
d->m_paLoadImages->setIcon( KDE::icon( "image-loading" ) );
connect( d->m_paLoadImages, SIGNAL(triggered(bool)), this, SLOT(slotLoadImages()) );
}
if ( d->m_paLoadImages ) {
QList<QAction*> lst;
lst.append( d->m_paLoadImages );
plugActionList( "loadImages", lst );
}
}
bool KHTMLPart::autoloadImages() const
{
if ( d->m_doc )
return d->m_doc->docLoader()->autoloadImages();
return true;
}
void KHTMLPart::clear()
{
if ( d->m_bCleared )
return;
d->m_bCleared = true;
d->m_bClearing = true;
{
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for(; it != end; ++it )
{
// Stop HTMLRun jobs for frames
if ( (*it)->m_run )
(*it)->m_run.data()->abort();
}
}
{
ConstFrameIt it = d->m_objects.constBegin();
const ConstFrameIt end = d->m_objects.constEnd();
for(; it != end; ++it )
{
// Stop HTMLRun jobs for objects
if ( (*it)->m_run )
(*it)->m_run.data()->abort();
}
}
findTextBegin(); // resets d->m_findNode and d->m_findPos
d->m_mousePressNode = DOM::Node();
if ( d->m_doc )
{
if (d->m_doc->attached()) //the view may have detached it already
d->m_doc->detach();
}
// Moving past doc so that onUnload works.
if ( d->m_frame && d->m_frame->m_jscript )
d->m_frame->m_jscript->clear();
// stopping marquees
if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->layer())
d->m_doc->renderer()->layer()->suspendMarquees();
if ( d->m_view )
d->m_view->clear();
// do not dereference the document before the jscript and view are cleared, as some destructors
// might still try to access the document.
if ( d->m_doc ) {
d->m_doc->deref();
}
d->m_doc = 0;
delete d->m_decoder;
d->m_decoder = 0;
// We don't want to change between parts if we are going to delete all of them anyway
if (partManager()) {
disconnect( partManager(), SIGNAL(activePartChanged(KParts::Part*)),
this, SLOT(slotActiveFrameChanged(KParts::Part*)) );
}
if (d->m_frames.count())
{
const KHTMLFrameList frames = d->m_frames;
d->m_frames.clear();
ConstFrameIt it = frames.begin();
const ConstFrameIt end = frames.end();
for(; it != end; ++it )
{
if ( (*it)->m_part )
{
partManager()->removePart( (*it)->m_part.data() );
delete (*it)->m_part.data();
}
delete *it;
}
}
d->m_suppressedPopupOriginParts.clear();
if (d->m_objects.count())
{
KHTMLFrameList objects = d->m_objects;
d->m_objects.clear();
ConstFrameIt oi = objects.constBegin();
const ConstFrameIt oiEnd = objects.constEnd();
for (; oi != oiEnd; ++oi )
{
delete (*oi)->m_part.data();
delete *oi;
}
}
// Listen to part changes again
if (partManager()) {
connect( partManager(), SIGNAL(activePartChanged(KParts::Part*)),
this, SLOT(slotActiveFrameChanged(KParts::Part*)) );
}
d->clearRedirection();
d->m_redirectLockHistory = true;
d->m_bClearing = false;
d->m_frameNameId = 1;
d->m_bFirstData = true;
d->m_bMousePressed = false;
if (d->editor_context.m_caretBlinkTimer >= 0)
killTimer(d->editor_context.m_caretBlinkTimer);
d->editor_context.reset();
#ifndef QT_NO_CLIPBOARD
connect( qApp->clipboard(), SIGNAL(selectionChanged()), SLOT(slotClearSelection()));
#endif
d->m_jobPercent = 0;
if ( !d->m_haveEncoding )
d->m_encoding.clear();
d->m_DNSPrefetchQueue.clear();
if (d->m_DNSPrefetchTimer > 0)
killTimer(d->m_DNSPrefetchTimer);
d->m_DNSPrefetchTimer = -1;
d->m_lookedupHosts.clear();
if (d->m_DNSTTLTimer > 0)
killTimer(d->m_DNSTTLTimer);
d->m_DNSTTLTimer = -1;
d->m_numDNSPrefetchedNames = 0;
#ifdef SPEED_DEBUG
d->m_parsetime.restart();
#endif
}
bool KHTMLPart::openFile()
{
return true;
}
DOM::HTMLDocumentImpl *KHTMLPart::docImpl() const
{
if ( d && d->m_doc && d->m_doc->isHTMLDocument() )
return static_cast<HTMLDocumentImpl*>(d->m_doc);
return 0;
}
DOM::DocumentImpl *KHTMLPart::xmlDocImpl() const
{
if ( d )
return d->m_doc;
return 0;
}
void KHTMLPart::slotInfoMessage(KJob* kio_job, const QString& msg)
{
assert(d->m_job == kio_job);
Q_ASSERT(kio_job);
Q_UNUSED(kio_job);
if (!parentPart())
setStatusBarText(msg, BarDefaultText);
}
void KHTMLPart::setPageSecurity( PageSecurity sec )
{
emit d->m_extension->setPageSecurity( sec );
}
void KHTMLPart::slotData( KIO::Job* kio_job, const QByteArray &data )
{
assert ( d->m_job == kio_job );
Q_ASSERT(kio_job);
Q_UNUSED(kio_job);
//kDebug( 6050 ) << "slotData: " << data.size();
// The first data ?
if ( !d->m_workingURL.isEmpty() )
{
//kDebug( 6050 ) << "begin!";
// We must suspend KIO while we're inside begin() because it can cause
// crashes if a window (such as kjsdebugger) goes back into the event loop,
// more data arrives, and begin() gets called again (re-entered).
d->m_job->suspend();
begin( d->m_workingURL, arguments().xOffset(), arguments().yOffset() );
d->m_job->resume();
// CC_Refresh means : always send the server an If-Modified-Since conditional request.
// This is the default cache setting and correspond to the KCM's "Keep cache in sync".
// CC_Verify means : only send a conditional request if the cache expiry date is passed.
// It doesn't have a KCM setter.
// We override the first to the second, except when doing a soft-reload.
if (d->m_cachePolicy == KIO::CC_Refresh && !d->m_extension->browserArguments().softReload)
d->m_doc->docLoader()->setCachePolicy(KIO::CC_Verify);
else
d->m_doc->docLoader()->setCachePolicy(d->m_cachePolicy);
d->m_workingURL = KUrl();
d->m_cacheId = KHTMLPageCache::self()->createCacheEntry();
// When the first data arrives, the metadata has just been made available
d->m_httpHeaders = d->m_job->queryMetaData("HTTP-Headers");
time_t cacheCreationDate = d->m_job->queryMetaData("cache-creation-date").toLong();
d->m_doc->docLoader()->setCacheCreationDate(cacheCreationDate);
d->m_pageServices = d->m_job->queryMetaData("PageServices");
d->m_pageReferrer = d->m_job->queryMetaData("referrer");
d->m_ssl_in_use = (d->m_job->queryMetaData("ssl_in_use") == "TRUE");
{
KHTMLPart *p = parentPart();
if (p && p->d->m_ssl_in_use != d->m_ssl_in_use) {
while (p->parentPart()) p = p->parentPart();
p->setPageSecurity( NotCrypted );
}
}
setPageSecurity( d->m_ssl_in_use ? Encrypted : NotCrypted );
// Shouldn't all of this be done only if ssl_in_use == true ? (DF)
d->m_ssl_parent_ip = d->m_job->queryMetaData("ssl_parent_ip");
d->m_ssl_parent_cert = d->m_job->queryMetaData("ssl_parent_cert");
d->m_ssl_peer_chain = d->m_job->queryMetaData("ssl_peer_chain");
d->m_ssl_peer_ip = d->m_job->queryMetaData("ssl_peer_ip");
d->m_ssl_cipher = d->m_job->queryMetaData("ssl_cipher");
d->m_ssl_protocol_version = d->m_job->queryMetaData("ssl_protocol_version");
d->m_ssl_cipher_used_bits = d->m_job->queryMetaData("ssl_cipher_used_bits");
d->m_ssl_cipher_bits = d->m_job->queryMetaData("ssl_cipher_bits");
d->m_ssl_cert_errors = d->m_job->queryMetaData("ssl_cert_errors");
// Check for charset meta-data
QString qData = d->m_job->queryMetaData("charset");
if ( !qData.isEmpty() && !d->m_haveEncoding ) // only use information if the user didn't override the settings
d->m_encoding = qData;
// Support for http-refresh
qData = d->m_job->queryMetaData("http-refresh");
if( !qData.isEmpty())
d->m_doc->processHttpEquiv("refresh", qData);
// DISABLED: Support Content-Location per section 14.14 of RFC 2616.
// See BR# 51185,BR# 82747
/*
QString baseURL = d->m_job->queryMetaData ("content-location");
if (!baseURL.isEmpty())
d->m_doc->setBaseURL(KUrl( d->m_doc->completeURL(baseURL) ));
*/
// Support for Content-Language
QString language = d->m_job->queryMetaData("content-language");
if (!language.isEmpty())
d->m_doc->setContentLanguage(language);
if ( !url().isLocalFile() )
{
// Support for http last-modified
d->m_lastModified = d->m_job->queryMetaData("modified");
}
else
d->m_lastModified.clear(); // done on-demand by lastModified()
}
KHTMLPageCache::self()->addData(d->m_cacheId, data);
write( data.data(), data.size() );
}
void KHTMLPart::slotRestoreData(const QByteArray &data )
{
// The first data ?
if ( !d->m_workingURL.isEmpty() )
{
long saveCacheId = d->m_cacheId;
QString savePageReferrer = d->m_pageReferrer;
QString saveEncoding = d->m_encoding;
begin( d->m_workingURL, arguments().xOffset(), arguments().yOffset() );
d->m_encoding = saveEncoding;
d->m_pageReferrer = savePageReferrer;
d->m_cacheId = saveCacheId;
d->m_workingURL = KUrl();
}
//kDebug( 6050 ) << data.size();
write( data.data(), data.size() );
if (data.size() == 0)
{
//kDebug( 6050 ) << "<<end of data>>";
// End of data.
if (d->m_doc && d->m_doc->parsing())
end(); //will emit completed()
}
}
void KHTMLPart::showError( KJob* job )
{
kDebug(6050) << "d->m_bParsing=" << (d->m_doc && d->m_doc->parsing()) << " d->m_bComplete=" << d->m_bComplete
<< " d->m_bCleared=" << d->m_bCleared;
if (job->error() == KIO::ERR_NO_CONTENT)
return;
if ( (d->m_doc && d->m_doc->parsing()) || d->m_workingURL.isEmpty() ) // if we got any data already
job->uiDelegate()->showErrorMessage();
else
{
htmlError( job->error(), job->errorText(), d->m_workingURL );
}
}
// This is a protected method, placed here because of it's relevance to showError
void KHTMLPart::htmlError( int errorCode, const QString& text, const KUrl& reqUrl )
{
kDebug(6050) << "errorCode" << errorCode << "text" << text;
// make sure we're not executing any embedded JS
bool bJSFO = d->m_bJScriptForce;
bool bJSOO = d->m_bJScriptOverride;
d->m_bJScriptForce = false;
d->m_bJScriptOverride = true;
begin();
QString errorName, techName, description;
QStringList causes, solutions;
QByteArray raw = KIO::rawErrorDetail( errorCode, text, &reqUrl );
QDataStream stream(raw);
stream >> errorName >> techName >> description >> causes >> solutions;
QString url, protocol, datetime;
// This is somewhat confusing, but we have to escape the externally-
// controlled URL twice: once for i18n, and once for HTML.
url = Qt::escape( Qt::escape( reqUrl.toDisplayString() ) );
protocol = reqUrl.scheme();
datetime = KLocale::global()->formatDateTime( QDateTime::currentDateTime(),
KLocale::LongDate );
QString filename( QStandardPaths::locate(QStandardPaths::GenericDataLocation, "khtml/error.html" ) );
QFile file( filename );
bool isOpened = file.open( QIODevice::ReadOnly );
if ( !isOpened )
kWarning(6050) << "Could not open error html template:" << filename;
QString html = QString( QLatin1String( file.readAll() ) );
html.replace( QLatin1String( "TITLE" ), i18n( "Error: %1 - %2", errorName, url ) );
html.replace( QLatin1String( "DIRECTION" ), QApplication::isRightToLeft() ? "rtl" : "ltr" );
html.replace( QLatin1String( "ICON_PATH" ), KIconLoader::global()->iconPath( "dialog-warning", -KIconLoader::SizeHuge ) );
QString doc = QLatin1String( "<h1>" );
doc += i18n( "The requested operation could not be completed" );
doc += QLatin1String( "</h1><h2>" );
doc += errorName;
doc += QLatin1String( "</h2>" );
if ( !techName.isNull() ) {
doc += QLatin1String( "<h2>" );
doc += i18n( "Technical Reason: " );
doc += techName;
doc += QLatin1String( "</h2>" );
}
doc += QLatin1String( "<br clear=\"all\">" );
doc += QLatin1String( "<h3>" );
doc += i18n( "Details of the Request:" );
doc += QLatin1String( "</h3><ul><li>" );
doc += i18n( "URL: %1" , url );
doc += QLatin1String( "</li><li>" );
if ( !protocol.isNull() ) {
doc += i18n( "Protocol: %1", protocol );
doc += QLatin1String( "</li><li>" );
}
doc += i18n( "Date and Time: %1" , datetime );
doc += QLatin1String( "</li><li>" );
doc += i18n( "Additional Information: %1" , text );
doc += QLatin1String( "</li></ul><h3>" );
doc += i18n( "Description:" );
doc += QLatin1String( "</h3><p>" );
doc += description;
doc += QLatin1String( "</p>" );
if ( causes.count() ) {
doc += QLatin1String( "<h3>" );
doc += i18n( "Possible Causes:" );
doc += QLatin1String( "</h3><ul><li>" );
doc += causes.join( "</li><li>" );
doc += QLatin1String( "</li></ul>" );
}
if ( solutions.count() ) {
doc += QLatin1String( "<h3>" );
doc += i18n( "Possible Solutions:" );
doc += QLatin1String( "</h3><ul><li>" );
doc += solutions.join( "</li><li>" );
doc += QLatin1String( "</li></ul>" );
}
html.replace( QLatin1String("TEXT"), doc );
write( html );
end();
d->m_bJScriptForce = bJSFO;
d->m_bJScriptOverride = bJSOO;
// make the working url the current url, so that reload works and
// emit the progress signals to advance one step in the history
// (so that 'back' works)
setUrl(reqUrl); // same as d->m_workingURL
d->m_workingURL = KUrl();
emit started( 0 );
emit completed();
}
void KHTMLPart::slotFinished( KJob * job )
{
d->m_job = 0L;
d->m_jobspeed = 0L;
if (job->error())
{
KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
// The following catches errors that occur as a result of HTTP
// to FTP redirections where the FTP URL is a directory. Since
// KIO cannot change a redirection request from GET to LISTDIR,
// we have to take care of it here once we know for sure it is
// a directory...
if (job->error() == KIO::ERR_IS_DIRECTORY)
{
emit canceled( job->errorString() );
emit d->m_extension->openUrlRequest( d->m_workingURL );
}
else
{
emit canceled( job->errorString() );
// TODO: what else ?
checkCompleted();
showError( job );
}
return;
}
KIO::TransferJob *tjob = ::qobject_cast<KIO::TransferJob*>(job);
if (tjob && tjob->isErrorPage()) {
HTMLPartContainerElementImpl *elt = d->m_frame ?
d->m_frame->m_partContainerElement.data() : 0;
if (!elt)
return;
elt->partLoadingErrorNotify();
checkCompleted();
if (d->m_bComplete) return;
}
//kDebug( 6050 ) << "slotFinished";
KHTMLPageCache::self()->endData(d->m_cacheId);
if ( d->m_doc && d->m_doc->docLoader()->expireDate() && url().scheme().toLower().startsWith("http"))
KIO::http_update_cache(url(), false, d->m_doc->docLoader()->expireDate());
d->m_workingURL = KUrl();
if ( d->m_doc && d->m_doc->parsing())
end(); //will emit completed()
}
MimeType KHTMLPartPrivate::classifyMimeType(const QString& mimeStr)
{
// See HTML5's "5.5.1 Navigating across documents" section.
if (mimeStr == "application/xhtml+xml")
return MimeXHTML;
if (mimeStr == "image/svg+xml")
return MimeSVG;
if (mimeStr == "text/html" || mimeStr.isEmpty())
return MimeHTML;
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeStr);
if (mime.inherits("text/xml") || mimeStr.endsWith("+xml"))
return MimeXML;
if (mime.inherits("text/plain"))
return MimeText;
if (khtmlImLoad::ImageManager::loaderDatabase()->supportedMimeTypes().contains(mimeStr))
return MimeImage;
// Sometimes our subclasses like to handle custom mimetypes. In that case,
// we want to handle them as HTML. We do that in the following cases:
// 1) We're at top-level, so we were forced to open something
// 2) We're an object --- this again means we were forced to open something,
// as an iframe-generating-an-embed case would have us as an iframe
if (!q->parentPart() || (m_frame && m_frame->m_type == khtml::ChildFrame::Object))
return MimeHTML;
return MimeOther;
}
void KHTMLPart::begin( const KUrl &url, int xOffset, int yOffset )
{
if ( d->m_view->underMouse() )
QToolTip::hideText(); // in case a previous tooltip is still shown
// No need to show this for a new page until an error is triggered
if (!parentPart()) {
removeJSErrorExtension();
setSuppressedPopupIndicator( false );
d->m_openableSuppressedPopups = 0;
foreach ( KHTMLPart* part, d->m_suppressedPopupOriginParts ) {
if (part) {
KJS::Window *w = KJS::Window::retrieveWindow( part );
if (w)
w->forgetSuppressedWindows();
}
}
}
d->m_bCleared = false;
d->m_cacheId = 0;
d->m_bComplete = false;
d->m_bLoadEventEmitted = false;
clear();
d->m_bCleared = false;
if(url.isValid()) {
QString urlString = url.toString();
KHTMLGlobal::vLinks()->insert( urlString );
QString urlString2 = url.toDisplayString();
if ( urlString != urlString2 ) {
KHTMLGlobal::vLinks()->insert( urlString2 );
}
}
// ###
//stopParser();
KParts::OpenUrlArguments args = arguments();
args.setXOffset(xOffset);
args.setYOffset(yOffset);
setArguments(args);
d->m_pageReferrer.clear();
KUrl ref(url);
d->m_referrer = ref.scheme().startsWith("http") ? ref.toString() : "";
setUrl(url);
// Note: by now, any special mimetype besides plaintext would have been
// handled specially inside openURL, so we handle their cases the same
// as HTML.
MimeType type = d->classifyMimeType(args.mimeType());
switch (type) {
case MimeSVG:
d->m_doc = DOMImplementationImpl::createSVGDocument( d->m_view );
break;
case MimeXML: // any XML derivative, except XHTML or SVG
// ### not sure if XHTML documents served as text/xml should use DocumentImpl or HTMLDocumentImpl
d->m_doc = DOMImplementationImpl::createXMLDocument( d->m_view );
break;
case MimeText:
d->m_doc = new HTMLTextDocumentImpl( d->m_view );
break;
case MimeXHTML:
case MimeHTML:
default:
d->m_doc = DOMImplementationImpl::createHTMLDocument( d->m_view );
// HTML or XHTML? (#86446)
static_cast<HTMLDocumentImpl *>(d->m_doc)->setHTMLRequested( type != MimeXHTML );
}
d->m_doc->ref();
d->m_doc->setURL( url.toString() );
d->m_doc->open( );
if (!d->m_doc->attached())
d->m_doc->attach( );
d->m_doc->setBaseURL( KUrl() );
d->m_doc->docLoader()->setShowAnimations( KHTMLGlobal::defaultHTMLSettings()->showAnimations() );
emit docCreated();
d->m_paUseStylesheet->setItems(QStringList());
d->m_paUseStylesheet->setEnabled( false );
setAutoloadImages( KHTMLGlobal::defaultHTMLSettings()->autoLoadImages() );
QString userStyleSheet = KHTMLGlobal::defaultHTMLSettings()->userStyleSheet();
if ( !userStyleSheet.isEmpty() )
setUserStyleSheet( KUrl( userStyleSheet ) );
d->m_doc->setRestoreState(d->m_extension->browserArguments().docState);
connect(d->m_doc,SIGNAL(finishedParsing()),this,SLOT(slotFinishedParsing()));
emit d->m_extension->enableAction( "print", true );
d->m_doc->setParsing(true);
}
void KHTMLPart::write( const char *data, int len )
{
if ( !d->m_decoder )
d->m_decoder = createDecoder();
if ( len == -1 )
len = strlen( data );
if ( len == 0 )
return;
QString decoded=d->m_decoder->decodeWithBuffering(data,len);
if(decoded.isEmpty())
return;
if(d->m_bFirstData)
onFirstData();
khtml::Tokenizer* t = d->m_doc->tokenizer();
if(t)
t->write( decoded, true );
}
// ### KDE5: remove
void KHTMLPart::setAlwaysHonourDoctype( bool b )
{
d->m_bStrictModeQuirk = !b;
}
void KHTMLPart::write( const QString &str )
{
if ( str.isNull() )
return;
if(d->m_bFirstData) {
// determine the parse mode
if (d->m_bStrictModeQuirk) {
d->m_doc->setParseMode( DocumentImpl::Strict );
d->m_bFirstData = false;
} else {
onFirstData();
}
}
khtml::Tokenizer* t = d->m_doc->tokenizer();
if(t)
t->write( str, true );
}
void KHTMLPart::end()
{
if (d->m_doc) {
if (d->m_decoder)
{
QString decoded=d->m_decoder->flush();
if (d->m_bFirstData)
onFirstData();
if (!decoded.isEmpty())
write(decoded);
}
d->m_doc->finishParsing();
}
}
void KHTMLPart::onFirstData()
{
assert( d->m_bFirstData );
// determine the parse mode
d->m_doc->determineParseMode();
d->m_bFirstData = false;
// ### this is still quite hacky, but should work a lot better than the old solution
// Note: decoder may be null if only write(QString) is used.
if (d->m_decoder && d->m_decoder->visuallyOrdered())
d->m_doc->setVisuallyOrdered();
// ensure part and view shares zoom-level before styling
updateZoomFactor();
d->m_doc->recalcStyle( NodeImpl::Force );
}
bool KHTMLPart::doOpenStream( const QString& mimeType )
{
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
if ( mime.inherits( "text/html" ) || mime.inherits( "text/xml" ) )
{
begin( url() );
return true;
}
return false;
}
bool KHTMLPart::doWriteStream( const QByteArray& data )
{
write( data.data(), data.size() );
return true;
}
bool KHTMLPart::doCloseStream()
{
end();
return true;
}
void KHTMLPart::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
{
if (!d->m_view) return;
d->m_view->paint(p, rc, yOff, more);
}
void KHTMLPart::stopAnimations()
{
if ( d->m_doc )
d->m_doc->docLoader()->setShowAnimations( KHTMLSettings::KAnimationDisabled );
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it ) {
if ( KHTMLPart* p = qobject_cast<KHTMLPart*>((*it)->m_part.data()) )
p->stopAnimations();
}
}
void KHTMLPart::resetFromScript()
{
closeUrl();
d->m_bComplete = false;
d->m_bLoadEventEmitted = false;
disconnect(d->m_doc,SIGNAL(finishedParsing()),this,SLOT(slotFinishedParsing()));
connect(d->m_doc,SIGNAL(finishedParsing()),this,SLOT(slotFinishedParsing()));
d->m_doc->setParsing(true);
emit started( 0L );
}
void KHTMLPart::slotFinishedParsing()
{
d->m_doc->setParsing(false);
d->m_doc->dispatchHTMLEvent(EventImpl::KHTML_CONTENTLOADED_EVENT, true, false);
checkEmitLoadEvent();
disconnect(d->m_doc,SIGNAL(finishedParsing()),this,SLOT(slotFinishedParsing()));
if (!d->m_view)
return; // We are probably being destructed.
checkCompleted();
}
void KHTMLPart::slotLoaderRequestStarted( khtml::DocLoader* dl, khtml::CachedObject *obj )
{
if ( obj && obj->type() == khtml::CachedObject::Image && d->m_doc && d->m_doc->docLoader() == dl ) {
KHTMLPart* p = this;
while ( p ) {
KHTMLPart* const op = p;
++(p->d->m_totalObjectCount);
p = p->parentPart();
if ( !p && op->d->m_loadedObjects <= op->d->m_totalObjectCount
&& !op->d->m_progressUpdateTimer.isActive()) {
op->d->m_progressUpdateTimer.setSingleShot( true );
op->d->m_progressUpdateTimer.start( 200 );
}
}
}
}
static bool isAncestorOrSamePart(KHTMLPart* p1, KHTMLPart* p2)
{
KHTMLPart* p = p2;
do {
if (p == p1)
return true;
} while ((p = p->parentPart()));
return false;
}
void KHTMLPart::slotLoaderRequestDone( khtml::DocLoader* dl, khtml::CachedObject *obj )
{
if ( obj && obj->type() == khtml::CachedObject::Image && d->m_doc && d->m_doc->docLoader() == dl ) {
KHTMLPart* p = this;
while ( p ) {
KHTMLPart* const op = p;
++(p->d->m_loadedObjects);
p = p->parentPart();
if ( !p && op->d->m_loadedObjects <= op->d->m_totalObjectCount && op->d->m_jobPercent <= 100
&& !op->d->m_progressUpdateTimer.isActive()) {
op->d->m_progressUpdateTimer.setSingleShot( true );
op->d->m_progressUpdateTimer.start( 200 );
}
}
}
/// if we have no document, or the object is not a request of one of our children,
// then our loading state can't possibly be affected : don't waste time checking for completion.
if (!d->m_doc || !dl->doc()->part() || !isAncestorOrSamePart(this, dl->doc()->part()))
return;
checkCompleted();
}
void KHTMLPart::slotProgressUpdate()
{
int percent;
if ( d->m_loadedObjects < d->m_totalObjectCount )
percent = d->m_jobPercent / 4 + ( d->m_loadedObjects*300 ) / ( 4*d->m_totalObjectCount );
else
percent = d->m_jobPercent;
if( d->m_bComplete )
percent = 100;
if (d->m_statusMessagesEnabled) {
if( d->m_bComplete )
emit d->m_extension->infoMessage( i18n( "Page loaded." ));
else if ( d->m_loadedObjects < d->m_totalObjectCount && percent >= 75 )
emit d->m_extension->infoMessage( i18np( "%1 Image of %2 loaded.", "%1 Images of %2 loaded.", d->m_loadedObjects, d->m_totalObjectCount) );
}
emit d->m_extension->loadingProgress( percent );
}
void KHTMLPart::slotJobSpeed( KJob* /*job*/, unsigned long speed )
{
d->m_jobspeed = speed;
if (!parentPart())
setStatusBarText(jsStatusBarText(), BarOverrideText);
}
void KHTMLPart::slotJobPercent( KJob* /*job*/, unsigned long percent )
{
d->m_jobPercent = percent;
if ( !parentPart() ) {
d->m_progressUpdateTimer.setSingleShot( true );
d->m_progressUpdateTimer.start( 0 );
}
}
void KHTMLPart::slotJobDone( KJob* /*job*/ )
{
d->m_jobPercent = 100;
if ( !parentPart() ) {
d->m_progressUpdateTimer.setSingleShot( true );
d->m_progressUpdateTimer.start( 0 );
}
}
void KHTMLPart::slotUserSheetStatDone( KJob *_job )
{
using namespace KIO;
if ( _job->error() ) {
showError( _job );
return;
}
const UDSEntry entry = dynamic_cast<KIO::StatJob *>( _job )->statResult();
const time_t lastModified = entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
// If the filesystem supports modification times, only reload the
// user-defined stylesheet if necessary - otherwise always reload.
if ( lastModified != static_cast<time_t>(-1) ) {
if ( d->m_userStyleSheetLastModified >= lastModified ) {
return;
}
d->m_userStyleSheetLastModified = lastModified;
}
setUserStyleSheet( KUrl( settings()->userStyleSheet() ) );
}
bool KHTMLPartPrivate::isFullyLoaded(bool* pendingRedirections) const
{
*pendingRedirections = false;
// Any frame that hasn't completed yet ?
ConstFrameIt it = m_frames.constBegin();
const ConstFrameIt end = m_frames.constEnd();
for (; it != end; ++it ) {
if ( !(*it)->m_bCompleted || (*it)->m_run )
{
//kDebug( 6050 ) << this << " is waiting for " << (*it)->m_part;
return false;
}
// Check for frames with pending redirections
if ( (*it)->m_bPendingRedirection )
*pendingRedirections = true;
}
// Any object that hasn't completed yet ?
{
ConstFrameIt oi = m_objects.constBegin();
const ConstFrameIt oiEnd = m_objects.constEnd();
for (; oi != oiEnd; ++oi )
if ( !(*oi)->m_bCompleted )
return false;
}
// Are we still parsing
if ( m_doc && m_doc->parsing() )
return false;
// Still waiting for images/scripts from the loader ?
int requests = 0;
if ( m_doc && m_doc->docLoader() )
requests = khtml::Cache::loader()->numRequests( m_doc->docLoader() );
if ( requests > 0 )
{
//kDebug(6050) << "still waiting for images/scripts from the loader - requests:" << requests;
return false;
}
return true;
}
void KHTMLPart::checkCompleted()
{
// kDebug( 6050 ) << this;
// kDebug( 6050 ) << " parsing: " << (d->m_doc && d->m_doc->parsing());
// kDebug( 6050 ) << " complete: " << d->m_bComplete;
// restore the cursor position
if (d->m_doc && !d->m_doc->parsing() && !d->m_focusNodeRestored)
{
if (d->m_focusNodeNumber >= 0)
d->m_doc->setFocusNode(d->m_doc->nodeWithAbsIndex(d->m_focusNodeNumber));
d->m_focusNodeRestored = true;
}
bool fullyLoaded, pendingChildRedirections;
fullyLoaded = d->isFullyLoaded(&pendingChildRedirections);
// Are we still loading, or already have done the relevant work?
if (!fullyLoaded || d->m_bComplete)
return;
// OK, completed.
// Now do what should be done when we are really completed.
d->m_bComplete = true;
d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
d->m_totalObjectCount = 0;
d->m_loadedObjects = 0;
KHTMLPart* p = this;
while ( p ) {
KHTMLPart* op = p;
p = p->parentPart();
if ( !p && !op->d->m_progressUpdateTimer.isActive()) {
op->d->m_progressUpdateTimer.setSingleShot( true );
op->d->m_progressUpdateTimer.start( 0 );
}
}
checkEmitLoadEvent(); // if we didn't do it before
bool pendingAction = false;
if ( !d->m_redirectURL.isEmpty() )
{
// DA: Do not start redirection for frames here! That action is
// deferred until the parent emits a completed signal.
if ( parentPart() == 0 ) {
//kDebug(6050) << this << " starting redirection timer";
d->m_redirectionTimer.setSingleShot( true );
d->m_redirectionTimer.start( qMax(0, 1000 * d->m_delayRedirect) );
} else {
//kDebug(6050) << this << " not toplevel -> not starting redirection timer. Waiting for slotParentCompleted.";
}
pendingAction = true;
}
else if ( pendingChildRedirections )
{
pendingAction = true;
}
// the view will emit completed on our behalf,
// either now or at next repaint if one is pending
//kDebug(6050) << this << " asks the view to emit completed. pendingAction=" << pendingAction;
d->m_view->complete( pendingAction );
// find the alternate stylesheets
QStringList sheets;
if (d->m_doc)
sheets = d->m_doc->availableStyleSheets();
sheets.prepend( i18n( "Automatic Detection" ) );
d->m_paUseStylesheet->setItems( sheets );
d->m_paUseStylesheet->setEnabled( sheets.count() > 2);
if (sheets.count() > 2)
{
d->m_paUseStylesheet->setCurrentItem(qMax(sheets.indexOf(d->m_sheetUsed), 0));
slotUseStylesheet();
}
setJSDefaultStatusBarText(QString());
#ifdef SPEED_DEBUG
if (!parentPart())
kDebug(6080) << "DONE:" <<d->m_parsetime.elapsed();
#endif
}
void KHTMLPart::checkEmitLoadEvent()
{
bool fullyLoaded, pendingChildRedirections;
fullyLoaded = d->isFullyLoaded(&pendingChildRedirections);
// ### might want to wait on pendingChildRedirections here, too
if ( d->m_bLoadEventEmitted || !d->m_doc || !fullyLoaded ) return;
d->m_bLoadEventEmitted = true;
if (d->m_doc)
d->m_doc->close();
}
const KHTMLSettings *KHTMLPart::settings() const
{
return d->m_settings;
}
#ifndef KDE_NO_COMPAT // KDE5: remove this ifndef, keep the method (renamed to baseUrl)
KUrl KHTMLPart::baseURL() const
{
if ( !d->m_doc ) return KUrl();
return d->m_doc->baseURL();
}
#endif
KUrl KHTMLPart::completeURL( const QString &url )
{
if ( !d->m_doc ) return KUrl( url );
#if 0
if (d->m_decoder)
return KUrl(d->m_doc->completeURL(url), d->m_decoder->codec()->mibEnum());
#endif
return KUrl( d->m_doc->completeURL( url ) );
}
QString KHTMLPartPrivate::codeForJavaScriptURL(const QString &u)
{
return KUrl::fromPercentEncoding( u.right( u.length() - 11 ).toUtf8() );
}
void KHTMLPartPrivate::executeJavascriptURL(const QString &u)
{
QString script = codeForJavaScriptURL(u);
kDebug( 6050 ) << "script=" << script;
QVariant res = q->executeScript( DOM::Node(), script );
if ( res.type() == QVariant::String ) {
q->begin( q->url() );
q->setAlwaysHonourDoctype(); // Disable public API compat; it messes with doctype
q->write( res.toString() );
q->end();
}
emit q->completed();
}
bool KHTMLPartPrivate::isJavaScriptURL(const QString& url)
{
return url.indexOf( QLatin1String( "javascript:" ), 0, Qt::CaseInsensitive ) == 0;
}
// Called by ecma/kjs_window in case of redirections from Javascript,
// and by xml/dom_docimpl.cpp in case of http-equiv meta refresh.
void KHTMLPart::scheduleRedirection( int delay, const QString &url, bool doLockHistory )
{
kDebug(6050) << "delay=" << delay << " url=" << url << " from=" << this->url() << "parent=" << parentPart();
kDebug(6050) << "current redirectURL=" << d->m_redirectURL << " with delay " << d->m_delayRedirect;
// In case of JS redirections, some, such as jump to anchors, and javascript:
// evaluation should actually be handled immediately, and not waiting until
// the end of the script. (Besides, we don't want to abort the tokenizer for those)
if ( delay == -1 && d->isInPageURL(url) ) {
d->executeInPageURL(url, doLockHistory);
return;
}
if( delay < 24*60*60 &&
( d->m_redirectURL.isEmpty() || delay <= d->m_delayRedirect) ) {
d->m_delayRedirect = delay;
d->m_redirectURL = url;
d->m_redirectLockHistory = doLockHistory;
kDebug(6050) << " d->m_bComplete=" << d->m_bComplete;
if ( d->m_bComplete ) {
d->m_redirectionTimer.stop();
d->m_redirectionTimer.setSingleShot( true );
d->m_redirectionTimer.start( qMax(0, 1000 * d->m_delayRedirect) );
}
}
}
void KHTMLPartPrivate::clearRedirection()
{
m_delayRedirect = 0;
m_redirectURL.clear();
m_redirectionTimer.stop();
}
void KHTMLPart::slotRedirect()
{
kDebug(6050) << this;
QString u = d->m_redirectURL;
KUrl url( u );
d->clearRedirection();
if ( d->isInPageURL(u) )
{
d->executeInPageURL(u, d->m_redirectLockHistory);
return;
}
KParts::OpenUrlArguments args;
KUrl cUrl( this->url() );
// handle windows opened by JS
if ( openedByJS() && d->m_opener )
cUrl = d->m_opener->url();
if (!KAuthorized::authorizeUrlAction("redirect", cUrl, url))
{
kWarning(6050) << "KHTMLPart::scheduleRedirection: Redirection from " << cUrl << " to " << url << " REJECTED!";
emit completed();
return;
}
if ( url.equals(this->url(),
KUrl::CompareWithoutTrailingSlash | KUrl::CompareWithoutFragment | KUrl::AllowEmptyPath) )
{
args.metaData().insert("referrer", d->m_pageReferrer);
}
// For javascript and META-tag based redirections:
// - We don't take cross-domain-ness in consideration if we are the
// toplevel frame because the new URL may be in a different domain as the current URL
// but that's ok.
// - If we are not the toplevel frame then we check against the toplevelURL()
if (parentPart())
args.metaData().insert("cross-domain", toplevelURL().toString());
KParts::BrowserArguments browserArgs;
browserArgs.setLockHistory( d->m_redirectLockHistory );
// _self: make sure we don't use any <base target=>'s
if ( !urlSelected( u, 0, 0, "_self", args, browserArgs ) ) {
// urlSelected didn't open a url, so emit completed ourselves
emit completed();
}
}
void KHTMLPart::slotRedirection(KIO::Job*, const KUrl& url)
{
// the slave told us that we got redirected
//kDebug( 6050 ) << "redirection by KIO to" << url;
emit d->m_extension->setLocationBarUrl( url.toDisplayString() );
d->m_workingURL = url;
}
bool KHTMLPart::setEncoding( const QString &name, bool override )
{
d->m_encoding = name;
d->m_haveEncoding = override;
if( !url().isEmpty() ) {
// reload document
closeUrl();
KUrl oldUrl = url();
setUrl(KUrl());
d->m_restored = true;
openUrl(oldUrl);
d->m_restored = false;
}
return true;
}
QString KHTMLPart::encoding() const
{
if(d->m_haveEncoding && !d->m_encoding.isEmpty())
return d->m_encoding;
if(d->m_decoder && d->m_decoder->encoding())
return QString(d->m_decoder->encoding());
return defaultEncoding();
}
QString KHTMLPart::defaultEncoding() const
{
QString encoding = settings()->encoding();
if ( !encoding.isEmpty() )
return encoding;
// HTTP requires the default encoding to be latin1, when neither
// the user nor the page requested a particular encoding.
if ( url().scheme().startsWith( "http" ) )
return "iso-8859-1";
else
return KLocale::global()->encoding();
}
void KHTMLPart::setUserStyleSheet(const KUrl &url)
{
if ( d->m_doc && d->m_doc->docLoader() )
(void) new khtml::PartStyleSheetLoader(this, url.toString(), d->m_doc->docLoader());
}
void KHTMLPart::setUserStyleSheet(const QString &styleSheet)
{
if ( d->m_doc )
d->m_doc->setUserStyleSheet( styleSheet );
}
bool KHTMLPart::gotoAnchor( const QString &name )
{
if (!d->m_doc)
return false;
HTMLCollectionImpl *anchors =
new HTMLCollectionImpl( d->m_doc, HTMLCollectionImpl::DOC_ANCHORS);
anchors->ref();
NodeImpl *n = anchors->namedItem(name);
anchors->deref();
if(!n) {
n = d->m_doc->getElementById( name );
}
d->m_doc->setCSSTarget(n); // Setting to null will clear the current target.
// Implement the rule that "" and "top" both mean top of page as in other browsers.
bool quirkyName = !n && !d->m_doc->inStrictMode() && (name.isEmpty() || name.toLower() == "top");
if (quirkyName) {
d->m_view->setContentsPos( d->m_view->contentsX(), 0);
return true;
} else if (!n) {
kDebug(6050) << name << "not found";
return false;
}
int x = 0, y = 0;
int gox, dummy;
HTMLElementImpl *a = static_cast<HTMLElementImpl *>(n);
a->getUpperLeftCorner(x, y);
if (x <= d->m_view->contentsX())
gox = x - 10;
else {
gox = d->m_view->contentsX();
if ( x + 10 > d->m_view->contentsX()+d->m_view->visibleWidth()) {
a->getLowerRightCorner(x, dummy);
gox = x - d->m_view->visibleWidth() + 10;
}
}
d->m_view->setContentsPos(gox, y);
return true;
}
bool KHTMLPart::nextAnchor()
{
if (!d->m_doc)
return false;
d->m_view->focusNextPrevNode ( true );
return true;
}
bool KHTMLPart::prevAnchor()
{
if (!d->m_doc)
return false;
d->m_view->focusNextPrevNode ( false );
return true;
}
void KHTMLPart::setStandardFont( const QString &name )
{
d->m_settings->setStdFontName(name);
}
void KHTMLPart::setFixedFont( const QString &name )
{
d->m_settings->setFixedFontName(name);
}
void KHTMLPart::setURLCursor( const QCursor &c )
{
d->m_linkCursor = c;
}
QCursor KHTMLPart::urlCursor() const
{
return d->m_linkCursor;
}
bool KHTMLPart::onlyLocalReferences() const
{
return d->m_onlyLocalReferences;
}
void KHTMLPart::setOnlyLocalReferences(bool enable)
{
d->m_onlyLocalReferences = enable;
}
bool KHTMLPart::forcePermitLocalImages() const
{
return d->m_forcePermitLocalImages;
}
void KHTMLPart::setForcePermitLocalImages(bool enable)
{
d->m_forcePermitLocalImages = enable;
}
void KHTMLPartPrivate::setFlagRecursively(
bool KHTMLPartPrivate::*flag, bool value)
{
// first set it on the current one
this->*flag = value;
// descend into child frames recursively
{
QList<khtml::ChildFrame*>::Iterator it = m_frames.begin();
const QList<khtml::ChildFrame*>::Iterator itEnd = m_frames.end();
for (; it != itEnd; ++it) {
KHTMLPart* const part = qobject_cast<KHTMLPart *>( (*it)->m_part.data() );
if (part)
part->d->setFlagRecursively(flag, value);
}/*next it*/
}
// do the same again for objects
{
QList<khtml::ChildFrame*>::Iterator it = m_objects.begin();
const QList<khtml::ChildFrame*>::Iterator itEnd = m_objects.end();
for (; it != itEnd; ++it) {
KHTMLPart* const part = qobject_cast<KHTMLPart *>( (*it)->m_part.data() );
if (part)
part->d->setFlagRecursively(flag, value);
}/*next it*/
}
}
void KHTMLPart::initCaret()
{
// initialize caret if not used yet
if (d->editor_context.m_selection.state() == Selection::NONE) {
if (d->m_doc) {
NodeImpl *node;
if (d->m_doc->isHTMLDocument()) {
HTMLDocumentImpl* htmlDoc = static_cast<HTMLDocumentImpl*>(d->m_doc);
node = htmlDoc->body();
} else
node = d->m_doc;
if (!node) return;
d->editor_context.m_selection.moveTo(Position(node, 0));
d->editor_context.m_selection.setNeedsLayout();
d->editor_context.m_selection.needsCaretRepaint();
}
}
}
static void setCaretInvisibleIfNeeded(KHTMLPart *part)
{
// On contenteditable nodes, don't hide the caret
if (!khtml::KHTMLPartAccessor::caret(part).caretPos().node()->isContentEditable())
part->setCaretVisible(false);
}
void KHTMLPart::setCaretMode(bool enable)
{
kDebug(6200) << enable;
if (isCaretMode() == enable) return;
d->setFlagRecursively(&KHTMLPartPrivate::m_caretMode, enable);
// FIXME: this won't work on frames as expected
if (!isEditable()) {
if (enable) {
initCaret();
setCaretVisible(true);
// view()->ensureCaretVisible();
} else {
setCaretInvisibleIfNeeded(this);
}
}
}
bool KHTMLPart::isCaretMode() const
{
return d->m_caretMode;
}
void KHTMLPart::setEditable(bool enable)
{
if (isEditable() == enable) return;
d->setFlagRecursively(&KHTMLPartPrivate::m_designMode, enable);
// FIXME: this won't work on frames as expected
if (!isCaretMode()) {
if (enable) {
initCaret();
setCaretVisible(true);
// view()->ensureCaretVisible();
} else
setCaretInvisibleIfNeeded(this);
}
}
bool KHTMLPart::isEditable() const
{
return d->m_designMode;
}
khtml::EditorContext *KHTMLPart::editorContext() const {
return &d->editor_context;
}
void KHTMLPart::setCaretPosition(DOM::Node node, long offset, bool extendSelection)
{
Q_UNUSED(node);
Q_UNUSED(offset);
Q_UNUSED(extendSelection);
#ifndef KHTML_NO_CARET
#if 0
kDebug(6200) << "node: " << node.handle() << " nodeName: "
<< node.nodeName().string() << " offset: " << offset
<< " extendSelection " << extendSelection;
if (view()->moveCaretTo(node.handle(), offset, !extendSelection))
emitSelectionChanged();
view()->ensureCaretVisible();
#endif
#endif // KHTML_NO_CARET
}
KHTMLPart::CaretDisplayPolicy KHTMLPart::caretDisplayPolicyNonFocused() const
{
#if 0
#ifndef KHTML_NO_CARET
return (CaretDisplayPolicy)view()->caretDisplayPolicyNonFocused();
#else // KHTML_NO_CARET
return CaretInvisible;
#endif // KHTML_NO_CARET
#endif
return CaretInvisible;
}
void KHTMLPart::setCaretDisplayPolicyNonFocused(CaretDisplayPolicy policy)
{
Q_UNUSED(policy);
#if 0
#ifndef KHTML_NO_CARET
view()->setCaretDisplayPolicyNonFocused(policy);
#endif // KHTML_NO_CARET
#endif
}
void KHTMLPart::setCaretVisible(bool show)
{
if (show) {
NodeImpl *caretNode = d->editor_context.m_selection.caretPos().node();
if (isCaretMode() || (caretNode && caretNode->isContentEditable())) {
invalidateSelection();
enableFindAheadActions(false);
}
} else {
if (d->editor_context.m_caretBlinkTimer >= 0)
killTimer(d->editor_context.m_caretBlinkTimer);
clearCaretRectIfNeeded();
}
}
void KHTMLPart::findTextBegin()
{
d->m_find.findTextBegin();
}
bool KHTMLPart::initFindNode( bool selection, bool reverse, bool fromCursor )
{
return d->m_find.initFindNode(selection, reverse, fromCursor);
}
void KHTMLPart::slotFind()
{
KParts::ReadOnlyPart *part = currentFrame();
if (!part)
return;
if (!part->inherits("KHTMLPart") )
{
kError(6000) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
return;
}
static_cast<KHTMLPart *>( part )->findText();
}
void KHTMLPart::slotFindNext()
{
KParts::ReadOnlyPart *part = currentFrame();
if (!part)
return;
if (!part->inherits("KHTMLPart") )
{
kError(6000) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
return;
}
static_cast<KHTMLPart *>( part )->findTextNext();
}
void KHTMLPart::slotFindPrev()
{
KParts::ReadOnlyPart *part = currentFrame();
if (!part)
return;
if (!part->inherits("KHTMLPart") )
{
kError(6000) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
return;
}
static_cast<KHTMLPart *>( part )->findTextNext( true ); // reverse
}
void KHTMLPart::slotFindDone()
{
// ### remove me
}
void KHTMLPart::slotFindAheadText()
{
KHTMLPart *part = qobject_cast<KHTMLPart*>(currentFrame());
if (!part)
return;
part->findText();
KHTMLFindBar* findBar = part->d->m_find.findBar();
findBar->setOptions(findBar->options() & ~FindLinksOnly);
}
void KHTMLPart::slotFindAheadLink()
{
KHTMLPart *part = qobject_cast<KHTMLPart*>(currentFrame());
if (!part)
return;
part->findText();
KHTMLFindBar* findBar = part->d->m_find.findBar();
findBar->setOptions(findBar->options() | FindLinksOnly);
}
void KHTMLPart::enableFindAheadActions( bool )
{
// ### remove me
}
void KHTMLPart::slotFindDialogDestroyed()
{
// ### remove me
}
void KHTMLPart::findText()
{
if (parentPart())
return parentPart()->findText();
d->m_find.activate();
}
void KHTMLPart::findText( const QString &str, long options, QWidget *parent, KFindDialog *findDialog )
{
if (parentPart())
return parentPart()->findText(str, options, parent, findDialog);
d->m_find.createNewKFind(str, options, parent, findDialog );
}
// New method
bool KHTMLPart::findTextNext( bool reverse )
{
if (parentPart())
return parentPart()->findTextNext( reverse );
return d->m_find.findTextNext( reverse );
}
bool KHTMLPart::pFindTextNextInThisFrame( bool reverse )
{
return d->m_find.findTextNext( reverse );
}
QString KHTMLPart::selectedTextAsHTML() const
{
const Selection &sel = d->editor_context.m_selection;
if(!hasSelection()) {
kDebug() << "Selection is not valid. Returning empty selection";
return QString();
}
if(sel.start().offset() < 0 || sel.end().offset() < 0) {
kDebug() << "invalid values for end/startOffset " << sel.start().offset() << " " << sel.end().offset();
return QString();
}
DOM::Range r = selection();
if(r.isNull() || r.isDetached())
return QString();
int exceptioncode = 0; //ignore the result
return r.handle()->toHTML(exceptioncode).string();
}
QString KHTMLPart::selectedText() const
{
bool hasNewLine = true;
bool seenTDTag = false;
QString text;
const Selection &sel = d->editor_context.m_selection;
DOM::Node n = sel.start().node();
while(!n.isNull()) {
if(n.nodeType() == DOM::Node::TEXT_NODE && n.handle()->renderer()) {
DOM::DOMStringImpl *dstr = static_cast<DOM::TextImpl*>(n.handle())->renderString();
QString str(dstr->s, dstr->l);
if(!str.isEmpty()) {
if(seenTDTag) {
text += " ";
seenTDTag = false;
}
hasNewLine = false;
if(n == sel.start().node() && n == sel.end().node()) {
int s = khtml::RenderPosition::fromDOMPosition(sel.start()).renderedOffset();
int e = khtml::RenderPosition::fromDOMPosition(sel.end()).renderedOffset();
text = str.mid(s, e-s);
} else if(n == sel.start().node()) {
text = str.mid(khtml::RenderPosition::fromDOMPosition(sel.start()).renderedOffset());
} else if(n == sel.end().node()) {
text += str.left(khtml::RenderPosition::fromDOMPosition(sel.end()).renderedOffset());
} else
text += str;
}
}
else {
// This is our simple HTML -> ASCII transformation:
unsigned short id = n.elementId();
switch(id) {
case ID_TEXTAREA:
text += static_cast<HTMLTextAreaElementImpl*>(n.handle())->value().string();
break;
case ID_INPUT:
if (static_cast<HTMLInputElementImpl*>(n.handle())->inputType() != HTMLInputElementImpl::PASSWORD)
text += static_cast<HTMLInputElementImpl*>(n.handle())->value().string();
break;
case ID_SELECT:
text += static_cast<HTMLSelectElementImpl*>(n.handle())->value().string();
break;
case ID_BR:
text += "\n";
hasNewLine = true;
break;
case ID_IMG:
text += static_cast<HTMLImageElementImpl*>(n.handle())->altText().string();
break;
case ID_TD:
break;
case ID_TH:
case ID_HR:
case ID_OL:
case ID_UL:
case ID_LI:
case ID_DD:
case ID_DL:
case ID_DT:
case ID_PRE:
case ID_LISTING:
case ID_BLOCKQUOTE:
case ID_DIV:
if (!hasNewLine)
text += "\n";
hasNewLine = true;
break;
case ID_P:
case ID_TR:
case ID_H1:
case ID_H2:
case ID_H3:
case ID_H4:
case ID_H5:
case ID_H6:
if (!hasNewLine)
text += "\n";
hasNewLine = true;
break;
}
}
if(n == sel.end().node()) break;
DOM::Node next = n.firstChild();
if(next.isNull()) next = n.nextSibling();
while( next.isNull() && !n.parentNode().isNull() ) {
n = n.parentNode();
next = n.nextSibling();
unsigned short id = n.elementId();
switch(id) {
case ID_TD:
seenTDTag = true; //Add two spaces after a td if then followed by text.
break;
case ID_TH:
case ID_HR:
case ID_OL:
case ID_UL:
case ID_LI:
case ID_DD:
case ID_DL:
case ID_DT:
case ID_PRE:
case ID_LISTING:
case ID_BLOCKQUOTE:
case ID_DIV:
seenTDTag = false;
if (!hasNewLine)
text += "\n";
hasNewLine = true;
break;
case ID_P:
case ID_TR:
case ID_H1:
case ID_H2:
case ID_H3:
case ID_H4:
case ID_H5:
case ID_H6:
if (!hasNewLine)
text += "\n";
// text += "\n";
hasNewLine = true;
break;
}
}
n = next;
}
if(text.isEmpty())
return QString();
int start = 0;
int end = text.length();
// Strip leading LFs
while ((start < end) && (text[start] == '\n'))
++start;
// Strip excessive trailing LFs
while ((start < (end-1)) && (text[end-1] == '\n') && (text[end-2] == '\n'))
--end;
return text.mid(start, end-start);
}
QString KHTMLPart::simplifiedSelectedText() const
{
QString text = selectedText();
text.replace(QChar(0xa0), ' ');
// remove leading and trailing whitespace
while (!text.isEmpty() && text[0].isSpace())
text = text.mid(1);
while (!text.isEmpty() && text[text.length()-1].isSpace())
text.truncate(text.length()-1);
return text;
}
bool KHTMLPart::hasSelection() const
{
return !d->editor_context.m_selection.isEmpty() && !d->editor_context.m_selection.isCollapsed();
}
DOM::Range KHTMLPart::selection() const
{
return d->editor_context.m_selection.toRange();
}
void KHTMLPart::selection(DOM::Node &s, long &so, DOM::Node &e, long &eo) const
{
DOM::Range r = d->editor_context.m_selection.toRange();
s = r.startContainer();
so = r.startOffset();
e = r.endContainer();
eo = r.endOffset();
}
void KHTMLPart::setSelection( const DOM::Range &r )
{
setCaret(r);
}
const Selection &KHTMLPart::caret() const
{
return d->editor_context.m_selection;
}
const Selection &KHTMLPart::dragCaret() const
{
return d->editor_context.m_dragCaret;
}
void KHTMLPart::setCaret(const Selection &s, bool closeTyping)
{
if (d->editor_context.m_selection != s) {
clearCaretRectIfNeeded();
setFocusNodeIfNeeded(s);
d->editor_context.m_selection = s;
notifySelectionChanged(closeTyping);
}
}
void KHTMLPart::setDragCaret(const DOM::Selection &dragCaret)
{
if (d->editor_context.m_dragCaret != dragCaret) {
d->editor_context.m_dragCaret.needsCaretRepaint();
d->editor_context.m_dragCaret = dragCaret;
d->editor_context.m_dragCaret.needsCaretRepaint();
}
}
void KHTMLPart::clearSelection()
{
clearCaretRectIfNeeded();
setFocusNodeIfNeeded(d->editor_context.m_selection);
#ifdef APPLE_CHANGES
d->editor_context.m_selection.clear();
#else
d->editor_context.m_selection.collapse();
#endif
notifySelectionChanged();
}
void KHTMLPart::invalidateSelection()
{
clearCaretRectIfNeeded();
d->editor_context.m_selection.setNeedsLayout();
selectionLayoutChanged();
}
void KHTMLPart::setSelectionVisible(bool flag)
{
if (d->editor_context.m_caretVisible == flag)
return;
clearCaretRectIfNeeded();
setFocusNodeIfNeeded(d->editor_context.m_selection);
d->editor_context.m_caretVisible = flag;
// notifySelectionChanged();
}
#if 1
void KHTMLPart::slotClearSelection()
{
if (!isCaretMode()
&& d->editor_context.m_selection.state() != Selection::NONE
&& !d->editor_context.m_selection.caretPos().node()->isContentEditable())
clearCaretRectIfNeeded();
bool hadSelection = hasSelection();
#ifdef APPLE_CHANGES
d->editor_context.m_selection.clear();
#else
d->editor_context.m_selection.collapse();
#endif
if (hadSelection)
notifySelectionChanged();
}
#endif
void KHTMLPart::clearCaretRectIfNeeded()
{
if (d->editor_context.m_caretPaint) {
d->editor_context.m_caretPaint = false;
d->editor_context.m_selection.needsCaretRepaint();
}
}
void KHTMLPart::setFocusNodeIfNeeded(const Selection &s)
{
if (!xmlDocImpl() || s.state() == Selection::NONE)
return;
NodeImpl *n = s.start().node();
NodeImpl *target = (n && n->isContentEditable()) ? n : 0;
if (!target) {
while (n && n != s.end().node()) {
if (n->isContentEditable()) {
target = n;
break;
}
n = n->traverseNextNode();
}
}
assert(target == 0 || target->isContentEditable());
if (target) {
for ( ; target && !target->isFocusable(); target = target->parentNode())
{}
if (target && target->isMouseFocusable())
xmlDocImpl()->setFocusNode(target);
else if (!target || !target->focused())
xmlDocImpl()->setFocusNode(0);
}
}
void KHTMLPart::selectionLayoutChanged()
{
// kill any caret blink timer now running
if (d->editor_context.m_caretBlinkTimer >= 0) {
killTimer(d->editor_context.m_caretBlinkTimer);
d->editor_context.m_caretBlinkTimer = -1;
}
// see if a new caret blink timer needs to be started
if (d->editor_context.m_caretVisible
&& d->editor_context.m_selection.state() != Selection::NONE) {
d->editor_context.m_caretPaint = isCaretMode()
|| d->editor_context.m_selection.caretPos().node()->isContentEditable();
if (d->editor_context.m_caretBlinks && d->editor_context.m_caretPaint)
d->editor_context.m_caretBlinkTimer = startTimer(qApp->cursorFlashTime() / 2);
d->editor_context.m_selection.needsCaretRepaint();
// make sure that caret is visible
QRect r(d->editor_context.m_selection.getRepaintRect());
if (d->editor_context.m_caretPaint)
d->m_view->ensureVisible(r.x(), r.y());
}
if (d->m_doc)
d->m_doc->updateSelection();
// Always clear the x position used for vertical arrow navigation.
// It will be restored by the vertical arrow navigation code if necessary.
d->editor_context.m_xPosForVerticalArrowNavigation = d->editor_context.NoXPosForVerticalArrowNavigation;
}
void KHTMLPart::notifySelectionChanged(bool closeTyping)
{
Editor *ed = d->editor_context.m_editor;
selectionLayoutChanged();
if (ed) {
ed->clearTypingStyle();
if (closeTyping)
ed->closeTyping();
}
emitSelectionChanged();
}
void KHTMLPart::timerEvent(QTimerEvent *e)
{
if (e->timerId() == d->editor_context.m_caretBlinkTimer) {
if (d->editor_context.m_caretBlinks &&
d->editor_context.m_selection.state() != Selection::NONE) {
d->editor_context.m_caretPaint = !d->editor_context.m_caretPaint;
d->editor_context.m_selection.needsCaretRepaint();
}
} else if (e->timerId() == d->m_DNSPrefetchTimer) {
// kDebug( 6050 ) << "will lookup " << d->m_DNSPrefetchQueue.head() << d->m_numDNSPrefetchedNames;
KIO::HostInfo::prefetchHost( d->m_DNSPrefetchQueue.dequeue() );
if (d->m_DNSPrefetchQueue.isEmpty()) {
killTimer( d->m_DNSPrefetchTimer );
d->m_DNSPrefetchTimer = -1;
}
} else if (e->timerId() == d->m_DNSTTLTimer) {
foreach (const QString &name, d->m_lookedupHosts)
d->m_DNSPrefetchQueue.enqueue(name);
if (d->m_DNSPrefetchTimer <= 0)
d->m_DNSPrefetchTimer = startTimer( sDNSPrefetchTimerDelay );
}
}
bool KHTMLPart::mayPrefetchHostname( const QString& name )
{
if (d->m_bDNSPrefetch == DNSPrefetchDisabled)
return false;
if (d->m_numDNSPrefetchedNames >= sMaxDNSPrefetchPerPage)
return false;
if (d->m_bDNSPrefetch == DNSPrefetchOnlyWWWAndSLD) {
int dots = name.count('.');
if (dots > 2 || (dots == 2 && !name.startsWith("www.")))
return false;
}
if ( d->m_lookedupHosts.contains( name ) )
return false;
d->m_DNSPrefetchQueue.enqueue( name );
d->m_lookedupHosts.insert( name );
d->m_numDNSPrefetchedNames++;
if (d->m_DNSPrefetchTimer < 1)
d->m_DNSPrefetchTimer = startTimer( sDNSPrefetchTimerDelay );
if (d->m_DNSTTLTimer < 1)
d->m_DNSTTLTimer = startTimer( sDNSTTLSeconds*1000 + 1 );
return true;
}
void KHTMLPart::paintCaret(QPainter *p, const QRect &rect) const
{
if (d->editor_context.m_caretPaint)
d->editor_context.m_selection.paintCaret(p, rect);
}
void KHTMLPart::paintDragCaret(QPainter *p, const QRect &rect) const
{
d->editor_context.m_dragCaret.paintCaret(p, rect);
}
DOM::Editor *KHTMLPart::editor() const {
if (!d->editor_context.m_editor)
const_cast<KHTMLPart *>(this)->d->editor_context.m_editor = new DOM::Editor(const_cast<KHTMLPart *>(this));
return d->editor_context.m_editor;
}
void KHTMLPart::resetHoverText()
{
if( !d->m_overURL.isEmpty() ) // Only if we were showing a link
{
d->m_overURL.clear();
d->m_overURLTarget.clear();
emit onURL( QString() );
// revert to default statusbar text
setStatusBarText(QString(), BarHoverText);
emit d->m_extension->mouseOverInfo(KFileItem());
}
}
void KHTMLPart::overURL( const QString &url, const QString &target, bool /*shiftPressed*/ )
{
KUrl u = completeURL(url);
// special case for <a href="">
if ( url.isEmpty() )
u.setFileName( url );
emit onURL( url );
if ( url.isEmpty() ) {
setStatusBarText(Qt::escape(u.toDisplayString()), BarHoverText);
return;
}
if ( d->isJavaScriptURL(url) ) {
QString jscode = d->codeForJavaScriptURL( url );
jscode = KStringHandler::rsqueeze( jscode, 80 ); // truncate if too long
if (url.startsWith("javascript:window.open"))
jscode += i18n(" (In new window)");
setStatusBarText( Qt::escape( jscode ), BarHoverText );
return;
}
KFileItem item(u, QString(), KFileItem::Unknown);
emit d->m_extension->mouseOverInfo(item);
const QString com = item.mimeComment();
if ( !u.isValid() ) {
setStatusBarText(Qt::escape(u.toDisplayString()), BarHoverText);
return;
}
if ( u.isLocalFile() )
{
// TODO : use KIO::stat() and create a KFileItem out of its result,
// to use KFileItem::statusBarText()
const QString path = QFile::encodeName( u.toLocalFile() );
KDE_struct_stat buff;
bool ok = !KDE::stat( path, &buff );
KDE_struct_stat lbuff;
if (ok) ok = !KDE::lstat( path, &lbuff );
QString text = Qt::escape(u.toDisplayString());
QString text2 = text;
if (ok && S_ISLNK( lbuff.st_mode ) )
{
QString tmp;
if ( com.isEmpty() )
tmp = i18n( "Symbolic Link");
else
tmp = i18n("%1 (Link)", com);
char buff_two[1024];
text += " -> ";
int n = readlink ( path.toLocal8Bit().data(), buff_two, 1022);
if (n == -1)
{
text2 += " ";
text2 += tmp;
setStatusBarText(text2, BarHoverText);
return;
}
buff_two[n] = 0;
text += buff_two;
text += " ";
text += tmp;
}
else if ( ok && S_ISREG( buff.st_mode ) )
{
if (buff.st_size < 1024)
text = i18np("%2 (%1 byte)", "%2 (%1 bytes)", (long) buff.st_size, text2); // always put the URL last, in case it contains '%'
else
{
float d = (float) buff.st_size/1024.0;
text = i18n("%2 (%1 K)", KLocale::global()->formatNumber(d, 2), text2); // was %.2f
}
text += " ";
text += com;
}
else if ( ok && S_ISDIR( buff.st_mode ) )
{
text += " ";
text += com;
}
else
{
text += " ";
text += com;
}
setStatusBarText(text, BarHoverText);
}
else
{
QString extra;
if (target.toLower() == "_blank")
{
extra = i18n(" (In new window)");
}
else if (!target.isEmpty() &&
(target.toLower() != "_top") &&
(target.toLower() != "_self") &&
(target.toLower() != "_parent"))
{
KHTMLPart *p = this;
while (p->parentPart())
p = p->parentPart();
if (!p->frameExists(target))
extra = i18n(" (In new window)");
else
extra = i18n(" (In other frame)");
}
if (u.scheme() == QLatin1String("mailto")) {
QString mailtoMsg /* = QString::fromLatin1("<img src=%1>").arg(locate("icon", QString::fromLatin1("locolor/16x16/actions/mail_send.png")))*/;
mailtoMsg += i18n("Email to: ") + KUrl::fromPercentEncoding(u.path().toLatin1());
const QStringList queries = u.query().mid(1).split('&');
QStringList::ConstIterator it = queries.begin();
const QStringList::ConstIterator itEnd = queries.end();
for (; it != itEnd; ++it)
if ((*it).startsWith(QLatin1String("subject=")))
mailtoMsg += i18n(" - Subject: ") + KUrl::fromPercentEncoding((*it).mid(8).toLatin1());
else if ((*it).startsWith(QLatin1String("cc=")))
mailtoMsg += i18n(" - CC: ") + KUrl::fromPercentEncoding((*it).mid(3).toLatin1());
else if ((*it).startsWith(QLatin1String("bcc=")))
mailtoMsg += i18n(" - BCC: ") + KUrl::fromPercentEncoding((*it).mid(4).toLatin1());
mailtoMsg = Qt::escape(mailtoMsg);
mailtoMsg.replace(QRegExp("([\n\r\t]|[ ]{10})"), QString());
setStatusBarText("<qt>"+mailtoMsg, BarHoverText);
return;
}
// Is this check necessary at all? (Frerich)
#if 0
else if (u.scheme() == QLatin1String("http")) {
DOM::Node hrefNode = nodeUnderMouse().parentNode();
while (hrefNode.nodeName().string() != QLatin1String("A") && !hrefNode.isNull())
hrefNode = hrefNode.parentNode();
if (!hrefNode.isNull()) {
DOM::Node hreflangNode = hrefNode.attributes().getNamedItem("HREFLANG");
if (!hreflangNode.isNull()) {
QString countryCode = hreflangNode.nodeValue().string().toLower();
// Map the language code to an appropriate country code.
if (countryCode == QLatin1String("en"))
countryCode = QLatin1String("gb");
QString flagImg = QLatin1String("<img src=%1>").arg(
locate("locale", QLatin1String("l10n/")
+ countryCode
+ QLatin1String("/flag.png")));
emit setStatusBarText(flagImg + u.toDisplayString() + extra);
}
}
}
#endif
setStatusBarText(Qt::escape(u.toDisplayString()) + extra, BarHoverText);
}
}
//
// This executes in the active part on a click or other url selection action in
// that active part.
//
bool KHTMLPart::urlSelected( const QString &url, int button, int state, const QString &_target, const KParts::OpenUrlArguments& _args, const KParts::BrowserArguments& _browserArgs )
{
KParts::OpenUrlArguments args = _args;
KParts::BrowserArguments browserArgs = _browserArgs;
bool hasTarget = false;
QString target = _target;
if ( target.isEmpty() && d->m_doc )
target = d->m_doc->baseTarget();
if ( !target.isEmpty() )
hasTarget = true;
if ( d->isJavaScriptURL(url) )
{
crossFrameExecuteScript( target, d->codeForJavaScriptURL(url) );
return false;
}
KUrl cURL = completeURL(url);
// special case for <a href=""> (IE removes filename, mozilla doesn't)
if ( url.isEmpty() )
cURL.setFileName( url ); // removes filename
if ( !cURL.isValid() )
// ### ERROR HANDLING
return false;
kDebug(6050) << this << "complete URL:" << cURL << "target=" << target;
if ( state & Qt::ControlModifier )
{
emit d->m_extension->createNewWindow( cURL, args, browserArgs );
return true;
}
if ( button == Qt::LeftButton && ( state & Qt::ShiftModifier ) )
{
KIO::MetaData metaData;
metaData.insert( "referrer", d->m_referrer );
KHTMLPopupGUIClient::saveURL( d->m_view, i18n( "Save As" ), cURL, metaData );
return false;
}
if (!checkLinkSecurity(cURL,
ki18n( "<qt>This untrusted page links to<br /><b>%1</b>.<br />Do you want to follow the link?</qt>" ),
i18n( "Follow" )))
return false;
browserArgs.frameName = target;
args.metaData().insert("main_frame_request",
parentPart() == 0 ? "TRUE":"FALSE");
args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
args.metaData().insert("PropagateHttpHeader", "true");
args.metaData().insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE":"FALSE");
args.metaData().insert("ssl_activate_warnings", "TRUE");
if ( hasTarget && target != "_self" && target != "_top" && target != "_blank" && target != "_parent" )
{
// unknown frame names should open in a new window.
khtml::ChildFrame *frame = recursiveFrameRequest( this, cURL, args, browserArgs, false );
if ( frame )
{
args.metaData()["referrer"] = d->m_referrer;
requestObject( frame, cURL, args, browserArgs );
return true;
}
}
if (!d->m_referrer.isEmpty() && !args.metaData().contains("referrer"))
args.metaData()["referrer"] = d->m_referrer;
if ( button == Qt::NoButton && (state & Qt::ShiftModifier) && (state & Qt::ControlModifier) )
{
emit d->m_extension->createNewWindow( cURL, args, browserArgs );
return true;
}
if ( state & Qt::ShiftModifier)
{
KParts::WindowArgs winArgs;
winArgs.setLowerWindow(true);
emit d->m_extension->createNewWindow( cURL, args, browserArgs, winArgs );
return true;
}
//If we're asked to open up an anchor in the current URL, in current window,
//merely gotoanchor, and do not reload the new page. Note that this does
//not apply if the URL is the same page, but without a ref
if (cURL.hasFragment() && (!hasTarget || target == "_self"))
{
if (d->isLocalAnchorJump(cURL))
{
d->executeAnchorJump(cURL, browserArgs.lockHistory() );
return false; // we jumped, but we didn't open a URL
}
}
if ( !d->m_bComplete && !hasTarget )
closeUrl();
view()->viewport()->unsetCursor();
emit d->m_extension->openUrlRequest( cURL, args, browserArgs );
return true;
}
void KHTMLPart::slotViewDocumentSource()
{
KUrl currentUrl(this->url());
bool isTempFile = false;
if (!(currentUrl.isLocalFile()) && KHTMLPageCache::self()->isComplete(d->m_cacheId))
{
QTemporaryFile sourceFile(QDir::tempPath() + QLatin1String("/XXXXXX") + defaultExtension());
sourceFile.setAutoRemove(false);
if (sourceFile.open()) {
QDataStream stream ( &sourceFile );
KHTMLPageCache::self()->saveData(d->m_cacheId, &stream);
currentUrl = QUrl::fromLocalFile(sourceFile.fileName());
isTempFile = true;
}
}
(void) KRun::runUrl( currentUrl, QLatin1String("text/plain"), view(), isTempFile );
}
void KHTMLPart::slotViewPageInfo()
{
Ui_KHTMLInfoDlg ui;
QDialog *dlg = new QDialog(0);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setObjectName("KHTML Page Info Dialog");
ui.setupUi(dlg);
KGuiItem::assign(ui._close, KStandardGuiItem::close());
connect(ui._close, SIGNAL(clicked()), dlg, SLOT(accept()));
if (d->m_doc)
ui._title->setText(d->m_doc->title().string());
// If it's a frame, set the caption to "Frame Information"
if ( parentPart() && d->m_doc && d->m_doc->isHTMLDocument() ) {
dlg->setWindowTitle(i18n("Frame Information"));
}
QString editStr;
if (!d->m_pageServices.isEmpty())
editStr = i18n(" <a href=\"%1\">[Properties]</a>", d->m_pageServices);
QString squeezedURL = KStringHandler::csqueeze( url().toDisplayString(), 80 );
ui._url->setText("<a href=\"" + url().toString() + "\">" + squeezedURL + "</a>" + editStr);
if (lastModified().isEmpty())
{
ui._lastModified->hide();
ui._lmLabel->hide();
}
else
ui._lastModified->setText(lastModified());
const QString& enc = encoding();
if (enc.isEmpty()) {
ui._eLabel->hide();
ui._encoding->hide();
} else {
ui._encoding->setText(enc);
}
if (!xmlDocImpl() || xmlDocImpl()->parseMode() == DOM::DocumentImpl::Unknown) {
ui._mode->hide();
ui._modeLabel->hide();
} else {
switch (xmlDocImpl()->parseMode()) {
case DOM::DocumentImpl::Compat:
ui._mode->setText(i18nc("HTML rendering mode (see http://en.wikipedia.org/wiki/Quirks_mode)", "Quirks"));
break;
case DOM::DocumentImpl::Transitional:
ui._mode->setText(i18nc("HTML rendering mode (see http://en.wikipedia.org/wiki/Quirks_mode)", "Almost standards"));
break;
case DOM::DocumentImpl::Strict:
default: // others handled above
ui._mode->setText(i18nc("HTML rendering mode (see http://en.wikipedia.org/wiki/Quirks_mode)", "Strict"));
break;
}
}
/* populate the list view now */
const QStringList headers = d->m_httpHeaders.split("\n");
QStringList::ConstIterator it = headers.begin();
const QStringList::ConstIterator itEnd = headers.end();
for (; it != itEnd; ++it) {
const QStringList header = (*it).split(QRegExp(":[ ]+"));
if (header.count() != 2)
continue;
QTreeWidgetItem *item = new QTreeWidgetItem(ui._headers);
item->setText(0, header[0]);
item->setText(1, header[1]);
}
dlg->show();
/* put no code here */
}
void KHTMLPart::slotViewFrameSource()
{
KParts::ReadOnlyPart *frame = currentFrame();
if ( !frame )
return;
KUrl url = frame->url();
bool isTempFile = false;
if (!(url.isLocalFile()) && frame->inherits("KHTMLPart"))
{
long cacheId = static_cast<KHTMLPart *>(frame)->d->m_cacheId;
if (KHTMLPageCache::self()->isComplete(cacheId))
{
QTemporaryFile sourceFile(QDir::tempPath() + QLatin1String("/XXXXXX") + defaultExtension());
sourceFile.setAutoRemove(false);
if (sourceFile.open()) {
QDataStream stream ( &sourceFile );
KHTMLPageCache::self()->saveData(cacheId, &stream);
url = KUrl();
url.setPath(sourceFile.fileName());
isTempFile = true;
}
}
}
(void) KRun::runUrl( url, QLatin1String("text/plain"), view(), isTempFile );
}
KUrl KHTMLPart::backgroundURL() const
{
// ### what about XML documents? get from CSS?
if (!d->m_doc || !d->m_doc->isHTMLDocument())
return KUrl();
QString relURL = static_cast<HTMLDocumentImpl*>(d->m_doc)->body()->getAttribute( ATTR_BACKGROUND ).string();
return KUrl( url(), relURL );
}
void KHTMLPart::slotSaveBackground()
{
KIO::MetaData metaData;
metaData["referrer"] = d->m_referrer;
KHTMLPopupGUIClient::saveURL( d->m_view, i18n("Save Background Image As"), backgroundURL(), metaData );
}
void KHTMLPart::slotSaveDocument()
{
KUrl srcURL( url() );
if ( srcURL.fileName(KUrl::ObeyTrailingSlash).isEmpty() )
srcURL.setFileName( "index" + defaultExtension() );
KIO::MetaData metaData;
// Referre unknown?
KHTMLPopupGUIClient::saveURL( d->m_view, i18n( "Save As" ), srcURL, metaData, "text/html", d->m_cacheId );
}
void KHTMLPart::slotSecurity()
{
// kDebug( 6050 ) << "Meta Data:" << endl
// << d->m_ssl_peer_cert_subject
// << endl
// << d->m_ssl_peer_cert_issuer
// << endl
// << d->m_ssl_cipher
// << endl
// << d->m_ssl_cipher_desc
// << endl
// << d->m_ssl_cipher_version
// << endl
// << d->m_ssl_good_from
// << endl
// << d->m_ssl_good_until
// << endl
// << d->m_ssl_cert_state
// << endl;
//### reenable with new signature
#if 0
KSslInfoDialog *kid = new KSslInfoDialog(d->m_ssl_in_use, widget(), "kssl_info_dlg", true );
const QStringList sl = d->m_ssl_peer_chain.split('\n', QString::SkipEmptyParts);
QList<QSslCertificate> certChain;
bool certChainOk = d->m_ssl_in_use;
if (certChainOk) {
foreach (const QString &s, sl) {
- certChain.append(QSslCertificate(s.toAscii())); //or is it toLocal8Bit or whatever?
+ certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
if (certChain.last().isNull()) {
certChainOk = false;
break;
}
}
}
if (certChainOk) {
kid->setup(certChain,
d->m_ssl_peer_ip,
url().toString(),
d->m_ssl_cipher,
d->m_ssl_cipher_desc,
d->m_ssl_cipher_version,
d->m_ssl_cipher_used_bits.toInt(),
d->m_ssl_cipher_bits.toInt(),
(KSSLCertificate::KSSLValidation) d->m_ssl_cert_state.toInt());
}
kid->exec();
//the dialog deletes itself on close
#endif
KSslInfoDialog *kid = new KSslInfoDialog(0);
//### This is boilerplate code and it's copied from SlaveInterface.
QStringList sl = d->m_ssl_peer_chain.split('\x01', QString::SkipEmptyParts);
QList<QSslCertificate> certChain;
bool decodedOk = true;
foreach (const QString &s, sl) {
- certChain.append(QSslCertificate(s.toAscii())); //or is it toLocal8Bit or whatever?
+ certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
if (certChain.last().isNull()) {
decodedOk = false;
break;
}
}
if (decodedOk || true /*H4X*/) {
kid->setSslInfo(certChain,
d->m_ssl_peer_ip,
url().host(),
d->m_ssl_protocol_version,
d->m_ssl_cipher,
d->m_ssl_cipher_used_bits.toInt(),
d->m_ssl_cipher_bits.toInt(),
KSslInfoDialog::errorsFromString(d->m_ssl_cert_errors));
kDebug(7024) << "Showing SSL Info dialog";
kid->exec();
kDebug(7024) << "SSL Info dialog closed";
} else {
KMessageBox::information(0, i18n("The peer SSL certificate chain "
"appears to be corrupt."),
i18n("SSL"));
}
}
void KHTMLPart::slotSaveFrame()
{
KParts::ReadOnlyPart *frame = currentFrame();
if ( !frame )
return;
KUrl srcURL( frame->url() );
if ( srcURL.fileName(KUrl::ObeyTrailingSlash).isEmpty() )
srcURL.setFileName( "index" + defaultExtension() );
KIO::MetaData metaData;
// Referrer unknown?
KHTMLPopupGUIClient::saveURL( d->m_view, i18n( "Save Frame As" ), srcURL, metaData, "text/html" );
}
void KHTMLPart::slotSetEncoding(const QString &enc)
{
d->m_autoDetectLanguage=KEncodingProber::None;
setEncoding( enc, true);
}
void KHTMLPart::slotAutomaticDetectionLanguage(KEncodingProber::ProberType scri)
{
d->m_autoDetectLanguage=scri;
setEncoding( QString(), false );
}
void KHTMLPart::slotUseStylesheet()
{
if (d->m_doc)
{
bool autoselect = (d->m_paUseStylesheet->currentItem() == 0);
d->m_sheetUsed = autoselect ? QString() : d->m_paUseStylesheet->currentText();
d->m_doc->updateStyleSelector();
}
}
void KHTMLPart::updateActions()
{
bool frames = false;
QList<khtml::ChildFrame*>::ConstIterator it = d->m_frames.constBegin();
const QList<khtml::ChildFrame*>::ConstIterator end = d->m_frames.constEnd();
for (; it != end; ++it )
if ( (*it)->m_type == khtml::ChildFrame::Frame )
{
frames = true;
break;
}
if (d->m_paViewFrame)
d->m_paViewFrame->setEnabled( frames );
if (d->m_paSaveFrame)
d->m_paSaveFrame->setEnabled( frames );
if ( frames )
d->m_paFind->setText( i18n( "&Find in Frame..." ) );
else
d->m_paFind->setText( i18n( "&Find..." ) );
KParts::Part *frame = 0;
if ( frames )
frame = currentFrame();
bool enableFindAndSelectAll = true;
if ( frame )
enableFindAndSelectAll = frame->inherits( "KHTMLPart" );
d->m_paFind->setEnabled( enableFindAndSelectAll );
d->m_paSelectAll->setEnabled( enableFindAndSelectAll );
bool enablePrintFrame = false;
if ( frame )
{
QObject *ext = KParts::BrowserExtension::childObject( frame );
if ( ext )
enablePrintFrame = ext->metaObject()->indexOfSlot( "print()" ) != -1;
}
d->m_paPrintFrame->setEnabled( enablePrintFrame );
QString bgURL;
// ### frames
if ( d->m_doc && d->m_doc->isHTMLDocument() && static_cast<HTMLDocumentImpl*>(d->m_doc)->body() && !d->m_bClearing )
bgURL = static_cast<HTMLDocumentImpl*>(d->m_doc)->body()->getAttribute( ATTR_BACKGROUND ).string();
if (d->m_paSaveBackground)
d->m_paSaveBackground->setEnabled( !bgURL.isEmpty() );
if ( d->m_paDebugScript )
d->m_paDebugScript->setEnabled( d->m_frame ? d->m_frame->m_jscript : 0L );
}
KParts::ScriptableExtension *KHTMLPart::scriptableExtension( const DOM::NodeImpl *frame) {
const ConstFrameIt end = d->m_objects.constEnd();
for(ConstFrameIt it = d->m_objects.constBegin(); it != end; ++it )
if ((*it)->m_partContainerElement.data() == frame)
return (*it)->m_scriptable.data();
return 0L;
}
void KHTMLPart::loadFrameElement( DOM::HTMLPartContainerElementImpl *frame, const QString &url,
const QString &frameName, const QStringList &params, bool isIFrame )
{
//kDebug( 6050 ) << this << " requestFrame( ..., " << url << ", " << frameName << " )";
khtml::ChildFrame* child;
FrameIt it = d->m_frames.find( frameName );
if ( it == d->m_frames.end() ) {
child = new khtml::ChildFrame;
//kDebug( 6050 ) << "inserting new frame into frame map " << frameName;
child->m_name = frameName;
d->m_frames.insert( d->m_frames.end(), child );
} else {
child = *it;
}
child->m_type = isIFrame ? khtml::ChildFrame::IFrame : khtml::ChildFrame::Frame;
child->m_partContainerElement = frame;
child->m_params = params;
// If we do not have a part, make sure we create one.
if (!child->m_part) {
QStringList dummy; // the list of servicetypes handled by the part is now unused.
QString khtml = QString::fromLatin1("khtml");
KParts::ReadOnlyPart* part = createPart(d->m_view->viewport(), this,
QString::fromLatin1("text/html"),
khtml, dummy, QStringList());
// We navigate it to about:blank to setup an empty one, but we do it
// before hooking up the signals and extensions, so that any sync emit
// of completed by the kid doesn't cause us to be marked as completed.
// (async ones are discovered by the presence of the KHTMLRun)
// ### load event on the kid?
navigateLocalProtocol(child, part, KUrl("about:blank"));
connectToChildPart(child, part, "text/html" /* mimetype of the part, not what's being loaded */);
}
KUrl u = url.isEmpty() ? KUrl() : completeURL( url );
// Since we don't specify args here a KHTMLRun will be used to determine the
// mimetype, which will then be passed down at the bottom of processObjectRequest
// inside URLArgs to the part. In our particular case, this means that we can
// use that inside KHTMLPart::openUrl to route things appropriately.
child->m_bCompleted = false;
if (!requestObject( child, u ) && !child->m_run) {
child->m_bCompleted = true;
}
}
QString KHTMLPart::requestFrameName()
{
return QString::fromLatin1("<!--frame %1-->").arg(d->m_frameNameId++);
}
bool KHTMLPart::loadObjectElement( DOM::HTMLPartContainerElementImpl *frame, const QString &url,
const QString &serviceType, const QStringList &params )
{
//kDebug( 6031 ) << this << "frame=" << frame;
khtml::ChildFrame *child = new khtml::ChildFrame;
FrameIt it = d->m_objects.insert( d->m_objects.end(), child );
(*it)->m_partContainerElement = frame;
(*it)->m_type = khtml::ChildFrame::Object;
(*it)->m_params = params;
KParts::OpenUrlArguments args;
args.setMimeType(serviceType);
if (!requestObject( *it, completeURL( url ), args ) && !(*it)->m_run) {
(*it)->m_bCompleted = true;
return false;
}
return true;
}
bool KHTMLPart::requestObject( khtml::ChildFrame *child, const KUrl &url, const KParts::OpenUrlArguments &_args,
const KParts::BrowserArguments& browserArgs )
{
// we always permit javascript: URLs here since they're basically just
// empty pages (and checkLinkSecurity/KAuthorized doesn't know what to do with them)
if (!d->isJavaScriptURL(url.toString()) && !checkLinkSecurity(url))
{
kDebug(6031) << this << "checkLinkSecurity refused";
return false;
}
if (d->m_bClearing)
{
return false;
}
if ( child->m_bPreloaded )
{
if ( child->m_partContainerElement && child->m_part )
child->m_partContainerElement.data()->setWidget( child->m_part.data()->widget() );
child->m_bPreloaded = false;
return true;
}
//kDebug(6031) << "child=" << child << "child->m_part=" << child->m_part;
KParts::OpenUrlArguments args( _args );
if ( child->m_run ) {
kDebug(6031) << "navigating ChildFrame while mimetype resolution was in progress...";
child->m_run.data()->abort();
}
// ### Dubious -- the whole dir/ vs. img thing
if ( child->m_part && !args.reload() && KUrl(child->m_part.data()->url()).equals( url,
KUrl::CompareWithoutTrailingSlash | KUrl::CompareWithoutFragment | KUrl::AllowEmptyPath ) )
args.setMimeType(child->m_serviceType);
child->m_browserArgs = browserArgs;
child->m_args = args;
// reload/soft-reload arguments are always inherited from parent
child->m_args.setReload( arguments().reload() );
child->m_browserArgs.softReload = d->m_extension->browserArguments().softReload;
child->m_serviceName.clear();
if (!d->m_referrer.isEmpty() && !child->m_args.metaData().contains( "referrer" ))
child->m_args.metaData()["referrer"] = d->m_referrer;
child->m_args.metaData().insert("PropagateHttpHeader", "true");
child->m_args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
child->m_args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
child->m_args.metaData().insert("main_frame_request",
parentPart() == 0 ? "TRUE":"FALSE");
child->m_args.metaData().insert("ssl_was_in_use",
d->m_ssl_in_use ? "TRUE":"FALSE");
child->m_args.metaData().insert("ssl_activate_warnings", "TRUE");
child->m_args.metaData().insert("cross-domain", toplevelURL().toString());
// We know the frame will be text/html if the HTML says <frame src=""> or <frame src="about:blank">,
// no need to KHTMLRun to figure out the mimetype"
// ### What if we're inside an XML document?
if ((url.isEmpty() || url.toString() == "about:blank" || url.scheme() == "javascript") && args.mimeType().isEmpty())
args.setMimeType(QLatin1String("text/html"));
if ( args.mimeType().isEmpty() ) {
kDebug(6031) << "Running new KHTMLRun for" << this << "and child=" << child;
child->m_run = new KHTMLRun( this, child, url, child->m_args, child->m_browserArgs, true );
d->m_bComplete = false; // ensures we stop it in checkCompleted...
return false;
} else {
return processObjectRequest( child, url, args.mimeType() );
}
}
void KHTMLPart::childLoadFailure( khtml::ChildFrame *child )
{
child->m_bCompleted = true;
if ( child->m_partContainerElement )
child->m_partContainerElement.data()->partLoadingErrorNotify();
checkCompleted();
}
bool KHTMLPart::processObjectRequest( khtml::ChildFrame *child, const KUrl &_url, const QString &mimetype )
{
kDebug( 6031 ) << "trying to create part for" << mimetype << _url;
// IMPORTANT: create a copy of the url here, because it is just a reference, which was likely to be given
// by an emitting frame part (emit openUrlRequest( blahurl, ... ) . A few lines below we delete the part
// though -> the reference becomes invalid -> crash is likely
KUrl url( _url );
// khtmlrun called us with empty url + mimetype to indicate a loading error,
// we obviosuly failed; but we can return true here since we don't want it
// doing anything more, while childLoadFailure is enough to notify our kid.
if ( d->m_onlyLocalReferences || ( url.isEmpty() && mimetype.isEmpty() ) ) {
childLoadFailure(child);
return true;
}
// we also want to ignore any spurious requests due to closing when parser is being cleared. These should be
// ignored entirely --- the tail end of ::clear will clean things up.
if (d->m_bClearing)
return false;
if (child->m_bNotify) {
child->m_bNotify = false;
if ( !child->m_browserArgs.lockHistory() )
emit d->m_extension->openUrlNotify();
}
QMimeDatabase db;
// Now, depending on mimetype and current state of the world, we may have
// to create a new part or ask the user to save things, etc.
//
// We need a new part if there isn't one at all (doh) or the one that's there
// is not for the mimetype we're loading.
//
// For these new types, we may have to ask the user to save it or not
// (we don't if it's navigating the same type).
// Further, we will want to ask if content-disposition suggests we ask for
// saving, even if we're re-navigating.
if ( !child->m_part || child->m_serviceType != mimetype ||
(child->m_run && child->m_run.data()->serverSuggestsSave())) {
// We often get here if we didn't know the mimetype in advance, and had to rely
// on KRun to figure it out. In this case, we let the element check if it wants to
// handle this mimetype itself, for e.g. objects containing images.
if ( child->m_partContainerElement &&
child->m_partContainerElement.data()->mimetypeHandledInternally(mimetype) ) {
child->m_bCompleted = true;
checkCompleted();
return true;
}
// Before attempting to load a part, check if the user wants that.
// Many don't like getting ZIP files embedded.
// However we don't want to ask for flash and other plugin things.
//
// Note: this is fine for frames, since we will merely effectively ignore
// the navigation if this happens
if ( child->m_type != khtml::ChildFrame::Object && child->m_type != khtml::ChildFrame::IFrame ) {
QString suggestedFileName;
int disposition = 0;
if ( KHTMLRun* run = child->m_run.data() ) {
suggestedFileName = run->suggestedFileName();
disposition = run->serverSuggestsSave() ?
KParts::BrowserRun::AttachmentDisposition :
KParts::BrowserRun::InlineDisposition;
}
KParts::BrowserOpenOrSaveQuestion dlg( widget(), url, mimetype );
dlg.setSuggestedFileName( suggestedFileName );
const KParts::BrowserOpenOrSaveQuestion::Result res = dlg.askEmbedOrSave( disposition );
switch( res ) {
case KParts::BrowserOpenOrSaveQuestion::Save:
KHTMLPopupGUIClient::saveURL( widget(), i18n( "Save As" ), url, child->m_args.metaData(), QString(), 0, suggestedFileName );
// fall-through
case KParts::BrowserOpenOrSaveQuestion::Cancel:
child->m_bCompleted = true;
checkCompleted();
return true; // done
default: // Embed
break;
}
}
// Now, for frames and iframes, we always create a KHTMLPart anyway,
// doing it in advance when registering the frame. So we want the
// actual creation only for objects here.
if ( child->m_type == khtml::ChildFrame::Object ) {
QMimeType mime = db.mimeTypeForName(mimetype);
if (mime.isValid()) {
// Even for objects, however, we want to force a KHTMLPart for
// html & xml, even if the normally preferred part is another one,
// so that we can script the target natively via contentDocument method.
if (mime.inherits("text/html")
|| mime.inherits("application/xml")) { // this includes xhtml and svg
child->m_serviceName = "khtml";
}
}
QStringList dummy; // the list of servicetypes handled by the part is now unused.
KParts::ReadOnlyPart *part = createPart( d->m_view->viewport(), this, mimetype, child->m_serviceName, dummy, child->m_params );
if ( !part ) {
childLoadFailure(child);
return false;
}
connectToChildPart( child, part, mimetype );
}
}
checkEmitLoadEvent();
// Some JS code in the load event may have destroyed the part
// In that case, abort
if ( !child->m_part )
return false;
if ( child->m_bPreloaded ) {
if ( child->m_partContainerElement && child->m_part )
child->m_partContainerElement.data()->setWidget( child->m_part.data()->widget() );
child->m_bPreloaded = false;
return true;
}
// reload/soft-reload arguments are always inherited from parent
child->m_args.setReload( arguments().reload() );
child->m_browserArgs.softReload = d->m_extension->browserArguments().softReload;
// make sure the part has a way to find out about the mimetype.
// we actually set it in child->m_args in requestObject already,
// but it's useless if we had to use a KHTMLRun instance, as the
// point the run object is to find out exactly the mimetype.
child->m_args.setMimeType(mimetype);
child->m_part.data()->setArguments( child->m_args );
// if not a frame set child as completed
// ### dubious.
child->m_bCompleted = child->m_type == khtml::ChildFrame::Object;
if ( child->m_extension )
child->m_extension.data()->setBrowserArguments( child->m_browserArgs );
return navigateChild( child, url );
}
bool KHTMLPart::navigateLocalProtocol( khtml::ChildFrame* /*child*/, KParts::ReadOnlyPart *inPart,
const KUrl& url )
{
if (!qobject_cast<KHTMLPart*>(inPart))
return false;
KHTMLPart* p = static_cast<KHTMLPart*>(static_cast<KParts::ReadOnlyPart *>(inPart));
p->begin();
// We may have to re-propagate the domain here if we go here due to navigation
d->propagateInitialDomainAndBaseTo(p);
// Support for javascript: sources
if (d->isJavaScriptURL(url.toString())) {
// See if we want to replace content with javascript: output..
QVariant res = p->executeScript( DOM::Node(),
d->codeForJavaScriptURL(url.toString()));
if (res.type() == QVariant::String && p->d->m_redirectURL.isEmpty()) {
p->begin();
p->setAlwaysHonourDoctype(); // Disable public API compat; it messes with doctype
// We recreated the document, so propagate domain again.
d->propagateInitialDomainAndBaseTo(p);
p->write( res.toString() );
p->end();
}
} else {
p->setUrl(url);
// we need a body element. testcase: <iframe id="a"></iframe><script>alert(a.document.body);</script>
p->write("<HTML><TITLE></TITLE><BODY></BODY></HTML>");
}
p->end();
// we don't need to worry about child completion explicitly for KHTMLPart...
// or do we?
return true;
}
bool KHTMLPart::navigateChild( khtml::ChildFrame *child, const KUrl& url )
{
if (url.scheme() == "javascript" || url.toString() == "about:blank") {
return navigateLocalProtocol(child, child->m_part.data(), url);
} else if ( !url.isEmpty() ) {
kDebug( 6031 ) << "opening" << url << "in frame" << child->m_part;
bool b = child->m_part.data()->openUrl( url );
if (child->m_bCompleted)
checkCompleted();
return b;
} else {
// empty URL -> no need to navigate
child->m_bCompleted = true;
checkCompleted();
return true;
}
}
void KHTMLPart::connectToChildPart( khtml::ChildFrame *child, KParts::ReadOnlyPart *part,
const QString& mimetype)
{
kDebug(6031) << "we:" << this << "kid:" << child << part << mimetype;
part->setObjectName( child->m_name );
// Cleanup any previous part for this childframe and its connections
if ( KParts::ReadOnlyPart* p = child->m_part.data() ) {
if (!qobject_cast<KHTMLPart*>(p) && child->m_jscript)
child->m_jscript->clear();
partManager()->removePart( p );
delete p;
child->m_scriptable.clear();
}
child->m_part = part;
child->m_serviceType = mimetype;
if ( child->m_partContainerElement && part->widget() )
child->m_partContainerElement.data()->setWidget( part->widget() );
if ( child->m_type != khtml::ChildFrame::Object )
partManager()->addPart( part, false );
// else
// kDebug(6031) << "AH! NO FRAME!!!!!";
if (qobject_cast<KHTMLPart*>(part)) {
static_cast<KHTMLPart*>(part)->d->m_frame = child;
} else if (child->m_partContainerElement) {
// See if this can be scripted..
KParts::ScriptableExtension* scriptExt = KParts::ScriptableExtension::childObject(part);
if (!scriptExt) {
// Try to fall back to LiveConnectExtension compat
KParts::LiveConnectExtension* lc = KParts::LiveConnectExtension::childObject(part);
if (lc)
scriptExt = KParts::ScriptableExtension::adapterFromLiveConnect(part, lc);
}
if (scriptExt)
scriptExt->setHost(d->m_scriptableExtension);
child->m_scriptable = scriptExt;
}
KParts::StatusBarExtension *sb = KParts::StatusBarExtension::childObject(part);
if (sb)
sb->setStatusBar( d->m_statusBarExtension->statusBar() );
connect( part, SIGNAL(started(KIO::Job*)),
this, SLOT(slotChildStarted(KIO::Job*)) );
connect( part, SIGNAL(completed()),
this, SLOT(slotChildCompleted()) );
connect( part, SIGNAL(completed(bool)),
this, SLOT(slotChildCompleted(bool)) );
connect( part, SIGNAL(setStatusBarText(QString)),
this, SIGNAL(setStatusBarText(QString)) );
if ( part->inherits( "KHTMLPart" ) )
{
connect( this, SIGNAL(completed()),
part, SLOT(slotParentCompleted()) );
connect( this, SIGNAL(completed(bool)),
part, SLOT(slotParentCompleted()) );
// As soon as the child's document is created, we need to set its domain
// (but we do so only once, so it can't be simply done in the child)
connect( part, SIGNAL(docCreated()),
this, SLOT(slotChildDocCreated()) );
}
child->m_extension = KParts::BrowserExtension::childObject( part );
if ( KParts::BrowserExtension* kidBrowserExt = child->m_extension.data() )
{
connect( kidBrowserExt, SIGNAL(openUrlNotify()),
d->m_extension, SIGNAL(openUrlNotify()) );
connect( kidBrowserExt, SIGNAL(openUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
this, SLOT(slotChildURLRequest(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)) );
connect( kidBrowserExt, SIGNAL(createNewWindow(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::WindowArgs,KParts::ReadOnlyPart**)),
d->m_extension, SIGNAL(createNewWindow(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::WindowArgs,KParts::ReadOnlyPart**)) );
connect( kidBrowserExt, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
d->m_extension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)) );
connect( kidBrowserExt, SIGNAL(popupMenu(QPoint,KUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
d->m_extension, SIGNAL(popupMenu(QPoint,KUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)) );
connect( kidBrowserExt, SIGNAL(infoMessage(QString)),
d->m_extension, SIGNAL(infoMessage(QString)) );
connect( kidBrowserExt, SIGNAL(requestFocus(KParts::ReadOnlyPart*)),
this, SLOT(slotRequestFocus(KParts::ReadOnlyPart*)) );
kidBrowserExt->setBrowserInterface( d->m_extension->browserInterface() );
}
}
KParts::ReadOnlyPart *KHTMLPart::createPart( QWidget *parentWidget,
QObject *parent, const QString &mimetype,
QString &serviceName, QStringList &serviceTypes,
const QStringList &params )
{
QString constr;
if ( !serviceName.isEmpty() )
constr.append( QString::fromLatin1( "DesktopEntryName == '%1'" ).arg( serviceName ) );
KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KParts/ReadOnlyPart", constr );
if ( offers.isEmpty() ) {
int pos = mimetype.indexOf( "-plugin" );
if (pos < 0)
return 0L;
QString stripped_mime = mimetype.left( pos );
offers = KMimeTypeTrader::self()->query( stripped_mime, "KParts/ReadOnlyPart", constr );
if ( offers.isEmpty() )
return 0L;
}
KService::List::ConstIterator it = offers.constBegin();
const KService::List::ConstIterator itEnd = offers.constEnd();
for ( ; it != itEnd; ++it )
{
KService::Ptr service = (*it);
KPluginLoader loader(*service);
KPluginFactory* const factory = loader.factory();
if ( factory ) {
// Turn params into a QVariantList as expected by KPluginFactory
QVariantList variantlist;
Q_FOREACH(const QString& str, params)
variantlist << QVariant(str);
if ( service->serviceTypes().contains( "Browser/View" ) )
variantlist << QString("Browser/View");
KParts::ReadOnlyPart* part = factory->create<KParts::ReadOnlyPart>(parentWidget, parent, QString(), variantlist);
if ( part ) {
serviceTypes = service->serviceTypes();
serviceName = service->name();
return part;
}
} else {
// TODO KMessageBox::error and i18n, like in KonqFactory::createView?
kWarning() << QString("There was an error loading the module %1.\nThe diagnostics is:\n%2")
.arg(service->name()).arg(loader.errorString());
}
}
return 0;
}
KParts::PartManager *KHTMLPart::partManager()
{
if ( !d->m_manager && d->m_view )
{
d->m_manager = new KParts::PartManager( d->m_view->topLevelWidget(), this );
d->m_manager->setObjectName( "khtml part manager" );
d->m_manager->setAllowNestedParts( true );
connect( d->m_manager, SIGNAL(activePartChanged(KParts::Part*)),
this, SLOT(slotActiveFrameChanged(KParts::Part*)) );
connect( d->m_manager, SIGNAL(partRemoved(KParts::Part*)),
this, SLOT(slotPartRemoved(KParts::Part*)) );
}
return d->m_manager;
}
void KHTMLPart::submitFormAgain()
{
disconnect(this, SIGNAL(completed()), this, SLOT(submitFormAgain()));
if( d->m_doc && !d->m_doc->parsing() && d->m_submitForm)
KHTMLPart::submitForm( d->m_submitForm->submitAction, d->m_submitForm->submitUrl, d->m_submitForm->submitFormData, d->m_submitForm->target, d->m_submitForm->submitContentType, d->m_submitForm->submitBoundary );
delete d->m_submitForm;
d->m_submitForm = 0;
}
void KHTMLPart::submitFormProxy( const char *action, const QString &url, const QByteArray &formData, const QString &_target, const QString& contentType, const QString& boundary )
{
submitForm(action, url, formData, _target, contentType, boundary);
}
void KHTMLPart::submitForm( const char *action, const QString &url, const QByteArray &formData, const QString &_target, const QString& contentType, const QString& boundary )
{
kDebug(6000) << this << "target=" << _target << "url=" << url;
if (d->m_formNotification == KHTMLPart::Only) {
emit formSubmitNotification(action, url, formData, _target, contentType, boundary);
return;
} else if (d->m_formNotification == KHTMLPart::Before) {
emit formSubmitNotification(action, url, formData, _target, contentType, boundary);
}
KUrl u = completeURL( url );
if ( !u.isValid() )
{
// ### ERROR HANDLING!
return;
}
// Form security checks
//
/*
* If these form security checks are still in this place in a month or two
* I'm going to simply delete them.
*/
/* This is separate for a reason. It has to be _before_ all script, etc,
* AND I don't want to break anything that uses checkLinkSecurity() in
* other places.
*/
if (!d->m_submitForm) {
if (u.scheme() != "https" && u.scheme() != "mailto") {
if (d->m_ssl_in_use) { // Going from SSL -> nonSSL
int rc = KMessageBox::warningContinueCancel(NULL, i18n("Warning: This is a secure form but it is attempting to send your data back unencrypted."
"\nA third party may be able to intercept and view this information."
"\nAre you sure you wish to continue?"),
i18n("Network Transmission"),KGuiItem(i18n("&Send Unencrypted")));
if (rc == KMessageBox::Cancel)
return;
} else { // Going from nonSSL -> nonSSL
KSSLSettings kss(true);
if (kss.warnOnUnencrypted()) {
int rc = KMessageBox::warningContinueCancel(NULL,
i18n("Warning: Your data is about to be transmitted across the network unencrypted."
"\nAre you sure you wish to continue?"),
i18n("Network Transmission"),
KGuiItem(i18n("&Send Unencrypted")),
KStandardGuiItem::cancel(),
"WarnOnUnencryptedForm");
// Move this setting into KSSL instead
QString grpNotifMsgs = QLatin1String("Notification Messages");
KConfigGroup cg( KSharedConfig::openConfig(), grpNotifMsgs );
if (!cg.readEntry("WarnOnUnencryptedForm", true)) {
cg.deleteEntry("WarnOnUnencryptedForm");
cg.sync();
kss.setWarnOnUnencrypted(false);
kss.save();
}
if (rc == KMessageBox::Cancel)
return;
}
}
}
if (u.scheme() == "mailto") {
int rc = KMessageBox::warningContinueCancel(NULL,
i18n("This site is attempting to submit form data via email.\n"
"Do you want to continue?"),
i18n("Network Transmission"),
KGuiItem(i18n("&Send Email")),
KStandardGuiItem::cancel(),
"WarnTriedEmailSubmit");
if (rc == KMessageBox::Cancel) {
return;
}
}
}
// End form security checks
//
QString urlstring = u.toString();
if ( d->isJavaScriptURL(urlstring) ) {
crossFrameExecuteScript( _target, d->codeForJavaScriptURL(urlstring) );
return;
}
if (!checkLinkSecurity(u,
ki18n( "<qt>The form will be submitted to <br /><b>%1</b><br />on your local filesystem.<br />Do you want to submit the form?</qt>" ),
i18n( "Submit" )))
return;
// OK. We're actually going to submit stuff. Clear any redirections,
// we should win over them
d->clearRedirection();
KParts::OpenUrlArguments args;
if (!d->m_referrer.isEmpty())
args.metaData()["referrer"] = d->m_referrer;
args.metaData().insert("PropagateHttpHeader", "true");
args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
args.metaData().insert("main_frame_request",
parentPart() == 0 ? "TRUE":"FALSE");
args.metaData().insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE":"FALSE");
args.metaData().insert("ssl_activate_warnings", "TRUE");
//WABA: When we post a form we should treat it as the main url
//the request should never be considered cross-domain
//args.metaData().insert("cross-domain", toplevelURL().toString());
KParts::BrowserArguments browserArgs;
browserArgs.frameName = _target.isEmpty() ? d->m_doc->baseTarget() : _target ;
// Handle mailto: forms
if (u.scheme() == "mailto") {
// 1) Check for attach= and strip it
QString q = u.query().mid(1);
QStringList nvps = q.split("&");
bool triedToAttach = false;
QStringList::Iterator nvp = nvps.begin();
const QStringList::Iterator nvpEnd = nvps.end();
// cannot be a for loop as if something is removed we don't want to do ++nvp, as
// remove returns an iterator pointing to the next item
while (nvp != nvpEnd) {
const QStringList pair = (*nvp).split("=");
if (pair.count() >= 2) {
if (pair.first().toLower() == "attach") {
nvp = nvps.erase(nvp);
triedToAttach = true;
} else {
++nvp;
}
} else {
++nvp;
}
}
if (triedToAttach)
KMessageBox::information(NULL, i18n("This site attempted to attach a file from your computer in the form submission. The attachment was removed for your protection."), i18n("KDE"), "WarnTriedAttach");
// 2) Append body=
QString bodyEnc;
if (contentType.toLower() == "multipart/form-data") {
// FIXME: is this correct? I suspect not
bodyEnc = QLatin1String( KUrl::toPercentEncoding(QString::fromLatin1(formData.data(),
formData.size())));
} else if (contentType.toLower() == "text/plain") {
// Convention seems to be to decode, and s/&/\n/
QString tmpbody = QString::fromLatin1(formData.data(),
formData.size());
tmpbody.replace(QRegExp("[&]"), "\n");
tmpbody.replace(QRegExp("[+]"), " ");
tmpbody = KUrl::fromPercentEncoding(tmpbody.toLatin1()); // Decode the rest of it
bodyEnc = QLatin1String( KUrl::toPercentEncoding(tmpbody) ); // Recode for the URL
} else {
bodyEnc = QLatin1String( KUrl::toPercentEncoding(QString::fromLatin1(formData.data(),
formData.size())) );
}
nvps.append(QString("body=%1").arg(bodyEnc));
q = nvps.join("&");
u.setQuery(q);
}
if ( strcmp( action, "get" ) == 0 ) {
if (u.scheme() != "mailto")
u.setQuery( QString::fromLatin1( formData.data(), formData.size() ) );
browserArgs.setDoPost( false );
}
else {
browserArgs.postData = formData;
browserArgs.setDoPost( true );
// construct some user headers if necessary
if (contentType.isNull() || contentType == "application/x-www-form-urlencoded")
browserArgs.setContentType( "Content-Type: application/x-www-form-urlencoded" );
else // contentType must be "multipart/form-data"
browserArgs.setContentType( "Content-Type: " + contentType + "; boundary=" + boundary );
}
if ( d->m_doc->parsing() || d->m_runningScripts > 0 ) {
if( d->m_submitForm ) {
kDebug(6000) << "ABORTING!";
return;
}
d->m_submitForm = new KHTMLPartPrivate::SubmitForm;
d->m_submitForm->submitAction = action;
d->m_submitForm->submitUrl = url;
d->m_submitForm->submitFormData = formData;
d->m_submitForm->target = _target;
d->m_submitForm->submitContentType = contentType;
d->m_submitForm->submitBoundary = boundary;
connect(this, SIGNAL(completed()), this, SLOT(submitFormAgain()));
}
else
{
emit d->m_extension->openUrlRequest( u, args, browserArgs );
}
}
void KHTMLPart::popupMenu( const QString &linkUrl )
{
KUrl popupURL;
KUrl linkKUrl;
KParts::OpenUrlArguments args;
KParts::BrowserArguments browserArgs;
QString referrer;
KParts::BrowserExtension::PopupFlags itemflags=KParts::BrowserExtension::ShowBookmark | KParts::BrowserExtension::ShowReload;
if ( linkUrl.isEmpty() ) { // click on background
KHTMLPart* khtmlPart = this;
while ( khtmlPart->parentPart() )
{
khtmlPart=khtmlPart->parentPart();
}
popupURL = khtmlPart->url();
referrer = khtmlPart->pageReferrer();
if (hasSelection())
itemflags = KParts::BrowserExtension::ShowTextSelectionItems;
else
itemflags |= KParts::BrowserExtension::ShowNavigationItems;
} else { // click on link
popupURL = completeURL( linkUrl );
linkKUrl = popupURL;
referrer = this->referrer();
itemflags |= KParts::BrowserExtension::IsLink;
if (!(d->m_strSelectedURLTarget).isEmpty() &&
(d->m_strSelectedURLTarget.toLower() != "_top") &&
(d->m_strSelectedURLTarget.toLower() != "_self") &&
(d->m_strSelectedURLTarget.toLower() != "_parent")) {
if (d->m_strSelectedURLTarget.toLower() == "_blank")
browserArgs.setForcesNewWindow(true);
else {
KHTMLPart *p = this;
while (p->parentPart())
p = p->parentPart();
if (!p->frameExists(d->m_strSelectedURLTarget))
browserArgs.setForcesNewWindow(true);
}
}
}
QMimeDatabase db;
// Danger, Will Robinson. The Popup might stay around for a much
// longer time than KHTMLPart. Deal with it.
KHTMLPopupGUIClient* client = new KHTMLPopupGUIClient( this, linkKUrl );
QPointer<QObject> guard( client );
QString mimetype = QLatin1String( "text/html" );
args.metaData()["referrer"] = referrer;
if (!linkUrl.isEmpty()) // over a link
{
if (popupURL.isLocalFile()) // safe to do this
{
mimetype = db.mimeTypeForUrl(popupURL).name();
}
else // look at "extension" of link
{
const QString fname(popupURL.fileName(KUrl::ObeyTrailingSlash));
if (!fname.isEmpty() && !popupURL.hasFragment() && popupURL.query().isEmpty())
{
QMimeType pmt = db.mimeTypeForFile(fname, QMimeDatabase::MatchExtension);
// Further check for mime types guessed from the extension which,
// on a web page, are more likely to be a script delivering content
// of undecidable type. If the mime type from the extension is one
// of these, don't use it. Retain the original type 'text/html'.
if (!pmt.isDefault() &&
!pmt.inherits("application/x-perl") &&
!pmt.inherits("application/x-perl-module") &&
!pmt.inherits("application/x-php") &&
!pmt.inherits("application/x-python-bytecode") &&
!pmt.inherits("application/x-python") &&
!pmt.inherits("application/x-shellscript"))
mimetype = pmt.name();
}
}
}
args.setMimeType(mimetype);
emit d->m_extension->popupMenu( QCursor::pos(), popupURL, S_IFREG /*always a file*/,
args, browserArgs, itemflags,
client->actionGroups() );
if ( !guard.isNull() ) {
delete client;
emit popupMenu(linkUrl, QCursor::pos());
d->m_strSelectedURL.clear();
d->m_strSelectedURLTarget.clear();
}
}
void KHTMLPart::slotParentCompleted()
{
//kDebug(6050) << this;
if ( !d->m_redirectURL.isEmpty() && !d->m_redirectionTimer.isActive() )
{
//kDebug(6050) << this << ": starting timer for child redirection -> " << d->m_redirectURL;
d->m_redirectionTimer.setSingleShot( true );
d->m_redirectionTimer.start( qMax(0, 1000 * d->m_delayRedirect) );
}
}
void KHTMLPart::slotChildStarted( KIO::Job *job )
{
khtml::ChildFrame *child = frame( sender() );
assert( child );
child->m_bCompleted = false;
if ( d->m_bComplete )
{
#if 0
// WABA: Looks like this belongs somewhere else
if ( !parentPart() ) // "toplevel" html document? if yes, then notify the hosting browser about the document (url) changes
{
emit d->m_extension->openURLNotify();
}
#endif
d->m_bComplete = false;
emit started( job );
}
}
void KHTMLPart::slotChildCompleted()
{
slotChildCompleted( false );
}
void KHTMLPart::slotChildCompleted( bool pendingAction )
{
khtml::ChildFrame *child = frame( sender() );
if ( child ) {
kDebug(6031) << this << "child=" << child << "m_partContainerElement=" << child->m_partContainerElement;
child->m_bCompleted = true;
child->m_bPendingRedirection = pendingAction;
child->m_args = KParts::OpenUrlArguments();
child->m_browserArgs = KParts::BrowserArguments();
// dispatch load event. We don't do that for KHTMLPart's since their internal
// load will be forwarded inside NodeImpl::dispatchWindowEvent
if (!qobject_cast<KHTMLPart*>(child->m_part))
QTimer::singleShot(0, child->m_partContainerElement.data(), SLOT(slotEmitLoadEvent()));
}
checkCompleted();
}
void KHTMLPart::slotChildDocCreated()
{
// Set domain to the frameset's domain
// This must only be done when loading the frameset initially (#22039),
// not when following a link in a frame (#44162).
if (KHTMLPart* htmlFrame = qobject_cast<KHTMLPart*>(sender()))
d->propagateInitialDomainAndBaseTo(htmlFrame);
// So it only happens once
disconnect( sender(), SIGNAL(docCreated()), this, SLOT(slotChildDocCreated()) );
}
void KHTMLPartPrivate::propagateInitialDomainAndBaseTo(KHTMLPart* kid)
{
// This method is used to propagate our domain and base information for
// child frames, to provide them for about: or JavaScript: URLs
if ( m_doc && kid->d->m_doc ) {
DocumentImpl* kidDoc = kid->d->m_doc;
if ( kidDoc->origin()->isEmpty() ) {
kidDoc->setOrigin ( m_doc->origin() );
kidDoc->setBaseURL( m_doc->baseURL() );
}
}
}
void KHTMLPart::slotChildURLRequest( const KUrl &url, const KParts::OpenUrlArguments& args, const KParts::BrowserArguments &browserArgs )
{
khtml::ChildFrame *child = frame( sender()->parent() );
KHTMLPart *callingHtmlPart = const_cast<KHTMLPart *>(dynamic_cast<const KHTMLPart *>(sender()->parent()));
// TODO: handle child target correctly! currently the script are always executed for the parent
QString urlStr = url.toString();
if ( d->isJavaScriptURL(urlStr) ) {
executeScript( DOM::Node(), d->codeForJavaScriptURL(urlStr) );
return;
}
QString frameName = browserArgs.frameName.toLower();
if ( !frameName.isEmpty() ) {
if ( frameName == QLatin1String( "_top" ) )
{
emit d->m_extension->openUrlRequest( url, args, browserArgs );
return;
}
else if ( frameName == QLatin1String( "_blank" ) )
{
emit d->m_extension->createNewWindow( url, args, browserArgs );
return;
}
else if ( frameName == QLatin1String( "_parent" ) )
{
KParts::BrowserArguments newBrowserArgs( browserArgs );
newBrowserArgs.frameName.clear();
emit d->m_extension->openUrlRequest( url, args, newBrowserArgs );
return;
}
else if ( frameName != QLatin1String( "_self" ) )
{
khtml::ChildFrame *_frame = recursiveFrameRequest( callingHtmlPart, url, args, browserArgs );
if ( !_frame )
{
emit d->m_extension->openUrlRequest( url, args, browserArgs );
return;
}
child = _frame;
}
}
if ( child && child->m_type != khtml::ChildFrame::Object ) {
// Inform someone that we are about to show something else.
child->m_bNotify = true;
requestObject( child, url, args, browserArgs );
} else if ( frameName== "_self" ) // this is for embedded objects (via <object>) which want to replace the current document
{
KParts::BrowserArguments newBrowserArgs( browserArgs );
newBrowserArgs.frameName.clear();
emit d->m_extension->openUrlRequest( url, args, newBrowserArgs );
}
}
void KHTMLPart::slotRequestFocus( KParts::ReadOnlyPart * )
{
emit d->m_extension->requestFocus(this);
}
khtml::ChildFrame *KHTMLPart::frame( const QObject *obj )
{
assert( obj->inherits( "KParts::ReadOnlyPart" ) );
const KParts::ReadOnlyPart* const part = static_cast<const KParts::ReadOnlyPart *>( obj );
FrameIt it = d->m_frames.begin();
const FrameIt end = d->m_frames.end();
for (; it != end; ++it ) {
if ((*it)->m_part.data() == part )
return *it;
}
FrameIt oi = d->m_objects.begin();
const FrameIt oiEnd = d->m_objects.end();
for (; oi != oiEnd; ++oi ) {
if ((*oi)->m_part.data() == part)
return *oi;
}
return 0L;
}
//#define DEBUG_FINDFRAME
bool KHTMLPart::checkFrameAccess(KHTMLPart *callingHtmlPart)
{
if (callingHtmlPart == this)
return true; // trivial
if (!xmlDocImpl()) {
#ifdef DEBUG_FINDFRAME
kDebug(6050) << "Empty part" << this << "URL = " << url();
#endif
return false; // we are empty?
}
// now compare the domains
if (callingHtmlPart && callingHtmlPart->xmlDocImpl() && xmlDocImpl()) {
khtml::SecurityOrigin* actDomain = callingHtmlPart->xmlDocImpl()->origin();
khtml::SecurityOrigin* destDomain = xmlDocImpl()->origin();
if (actDomain->canAccess(destDomain))
return true;
}
#ifdef DEBUG_FINDFRAME
else
{
kDebug(6050) << "Unknown part/domain" << callingHtmlPart << "tries to access part" << this;
}
#endif
return false;
}
KHTMLPart *
KHTMLPart::findFrameParent( KParts::ReadOnlyPart *callingPart, const QString &f, khtml::ChildFrame **childFrame )
{
return d->findFrameParent(callingPart, f, childFrame, false);
}
KHTMLPart* KHTMLPartPrivate::findFrameParent(KParts::ReadOnlyPart* callingPart,
const QString& f, khtml::ChildFrame **childFrame, bool checkForNavigation)
{
#ifdef DEBUG_FINDFRAME
kDebug(6050) << q << "URL =" << q->url() << "name =" << q->objectName() << "findFrameParent(" << f << ")";
#endif
// Check access
KHTMLPart* const callingHtmlPart = dynamic_cast<KHTMLPart *>(callingPart);
if (!checkForNavigation && !q->checkFrameAccess(callingHtmlPart))
return 0;
if (!childFrame && !q->parentPart() && (q->objectName() == f)) {
if (!checkForNavigation || callingHtmlPart->d->canNavigate(q))
return q;
}
FrameIt it = m_frames.find( f );
const FrameIt end = m_frames.end();
if ( it != end )
{
#ifdef DEBUG_FINDFRAME
kDebug(6050) << "FOUND!";
#endif
if (!checkForNavigation || callingHtmlPart->d->canNavigate((*it)->m_part.data())) {
if (childFrame)
*childFrame = *it;
return q;
}
}
it = m_frames.begin();
for (; it != end; ++it )
{
if ( KHTMLPart* p = qobject_cast<KHTMLPart*>((*it)->m_part.data()) )
{
KHTMLPart* const frameParent = p->d->findFrameParent(callingPart, f, childFrame, checkForNavigation);
if (frameParent)
return frameParent;
}
}
return 0;
}
KHTMLPart* KHTMLPartPrivate::top()
{
KHTMLPart* t = q;
while (t->parentPart())
t = t->parentPart();
return t;
}
bool KHTMLPartPrivate::canNavigate(KParts::ReadOnlyPart* bCand)
{
if (!bCand) // No part here (e.g. invalid url), reuse that frame
return true;
KHTMLPart* b = qobject_cast<KHTMLPart*>(bCand);
if (!b) // Another kind of part? Not sure what to do...
return false;
// HTML5 gives conditions for this (a) being able to navigate b
// 1) Same domain
if (q->checkFrameAccess(b))
return true;
// 2) A is nested, with B its top
if (q->parentPart() && top() == b)
return true;
// 3) B is 'auxilary' -- window.open with opener,
// and A can navigate B's opener
if (b->opener() && canNavigate(b->opener()))
return true;
// 4) B is not top-level, but an ancestor of it has same origin as A
for (KHTMLPart* anc = b->parentPart(); anc; anc = anc->parentPart()) {
if (anc->checkFrameAccess(q))
return true;
}
return false;
}
KHTMLPart *KHTMLPart::findFrame( const QString &f )
{
khtml::ChildFrame *childFrame;
KHTMLPart *parentFrame = findFrameParent(this, f, &childFrame);
if (parentFrame)
return qobject_cast<KHTMLPart*>(childFrame->m_part.data());
return 0;
}
KParts::ReadOnlyPart *KHTMLPart::findFramePart(const QString &f)
{
khtml::ChildFrame *childFrame;
return findFrameParent(this, f, &childFrame) ? childFrame->m_part.data() : 0L;
}
KParts::ReadOnlyPart *KHTMLPart::currentFrame() const
{
KParts::ReadOnlyPart* part = (KParts::ReadOnlyPart*)(this);
// Find active part in our frame manager, in case we are a frameset
// and keep doing that (in case of nested framesets).
// Just realized we could also do this recursively, calling part->currentFrame()...
while ( part && part->inherits("KHTMLPart") &&
static_cast<KHTMLPart *>(part)->d->m_frames.count() > 0 ) {
KHTMLPart* frameset = static_cast<KHTMLPart *>(part);
part = static_cast<KParts::ReadOnlyPart *>(frameset->partManager()->activePart());
if ( !part ) return frameset;
}
return part;
}
bool KHTMLPart::frameExists( const QString &frameName )
{
FrameIt it = d->m_frames.find( frameName );
if ( it == d->m_frames.end() )
return false;
// WABA: We only return true if the child actually has a frame
// set. Otherwise we might find our preloaded-selve.
// This happens when we restore the frameset.
return (!(*it)->m_partContainerElement.isNull());
}
void KHTMLPartPrivate::renameFrameForContainer(DOM::HTMLPartContainerElementImpl* cont,
const QString& newName)
{
for (int i = 0; i < m_frames.size(); ++i) {
khtml::ChildFrame* f = m_frames[i];
if (f->m_partContainerElement.data() == cont)
f->m_name = newName;
}
}
KJSProxy *KHTMLPart::framejScript(KParts::ReadOnlyPart *framePart)
{
KHTMLPart* const kp = qobject_cast<KHTMLPart*>(framePart);
if (kp)
return kp->jScript();
FrameIt it = d->m_frames.begin();
const FrameIt itEnd = d->m_frames.end();
for (; it != itEnd; ++it) {
khtml::ChildFrame* frame = *it;
if (framePart == frame->m_part.data()) {
if (!frame->m_jscript)
frame->m_jscript = new KJSProxy(frame);
return frame->m_jscript;
}
}
return 0L;
}
KHTMLPart *KHTMLPart::parentPart()
{
return qobject_cast<KHTMLPart*>( parent() );
}
khtml::ChildFrame *KHTMLPart::recursiveFrameRequest( KHTMLPart *callingHtmlPart, const KUrl &url,
const KParts::OpenUrlArguments &args,
const KParts::BrowserArguments &browserArgs, bool callParent )
{
#ifdef DEBUG_FINDFRAME
kDebug( 6050 ) << this << "frame = " << args.frameName << "url = " << url;
#endif
khtml::ChildFrame *childFrame;
KHTMLPart *childPart = findFrameParent(callingHtmlPart, browserArgs.frameName, &childFrame);
if (childPart)
{
if (childPart == this)
return childFrame;
childPart->requestObject( childFrame, url, args, browserArgs );
return 0;
}
if ( parentPart() && callParent )
{
khtml::ChildFrame *res = parentPart()->recursiveFrameRequest( callingHtmlPart, url, args, browserArgs, callParent );
if ( res )
parentPart()->requestObject( res, url, args, browserArgs );
}
return 0L;
}
#ifdef DEBUG_SAVESTATE
static int s_saveStateIndentLevel = 0;
#endif
void KHTMLPart::saveState( QDataStream &stream )
{
#ifdef DEBUG_SAVESTATE
QString indent= QString().leftJustified( s_saveStateIndentLevel * 4, ' ' );
const int indentLevel = s_saveStateIndentLevel++;
kDebug( 6050 ) << indent << "saveState this=" << this << " '" << objectName() << "' saving URL " << url();
#endif
stream << url() << (qint32)d->m_view->contentsX() << (qint32)d->m_view->contentsY()
<< (qint32) d->m_view->contentsWidth() << (qint32) d->m_view->contentsHeight() << (qint32) d->m_view->marginWidth() << (qint32) d->m_view->marginHeight();
// save link cursor position
int focusNodeNumber;
if (!d->m_focusNodeRestored)
focusNodeNumber = d->m_focusNodeNumber;
else if (d->m_doc && d->m_doc->focusNode())
focusNodeNumber = d->m_doc->nodeAbsIndex(d->m_doc->focusNode());
else
focusNodeNumber = -1;
stream << focusNodeNumber;
// Save the doc's cache id.
stream << d->m_cacheId;
// Save the state of the document (Most notably the state of any forms)
QStringList docState;
if (d->m_doc)
{
docState = d->m_doc->docState();
}
stream << d->m_encoding << d->m_sheetUsed << docState;
stream << d->m_zoomFactor;
stream << d->m_fontScaleFactor;
stream << d->m_httpHeaders;
stream << d->m_pageServices;
stream << d->m_pageReferrer;
// Save ssl data
stream << d->m_ssl_in_use
<< d->m_ssl_peer_chain
<< d->m_ssl_peer_ip
<< d->m_ssl_cipher
<< d->m_ssl_protocol_version
<< d->m_ssl_cipher_used_bits
<< d->m_ssl_cipher_bits
<< d->m_ssl_cert_errors
<< d->m_ssl_parent_ip
<< d->m_ssl_parent_cert;
QStringList frameNameLst, frameServiceTypeLst, frameServiceNameLst;
QList<QUrl> frameURLLst;
QList<QByteArray> frameStateBufferLst;
QList<int> frameTypeLst;
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it )
{
if ( !(*it)->m_part )
continue;
frameNameLst << (*it)->m_name;
frameServiceTypeLst << (*it)->m_serviceType;
frameServiceNameLst << (*it)->m_serviceName;
frameURLLst << (*it)->m_part.data()->url();
QByteArray state;
QDataStream frameStream( &state, QIODevice::WriteOnly );
if ( (*it)->m_extension )
(*it)->m_extension.data()->saveState( frameStream );
frameStateBufferLst << state;
frameTypeLst << int( (*it)->m_type );
}
// Save frame data
stream << (quint32) frameNameLst.count();
stream << frameNameLst << frameServiceTypeLst << frameServiceNameLst << frameURLLst << frameStateBufferLst << frameTypeLst;
#ifdef DEBUG_SAVESTATE
s_saveStateIndentLevel = indentLevel;
#endif
}
void KHTMLPart::restoreState( QDataStream &stream )
{
KUrl u;
qint32 xOffset, yOffset, wContents, hContents, mWidth, mHeight;
quint32 frameCount;
QStringList frameNames, frameServiceTypes, docState, frameServiceNames;
QList<int> frameTypes;
QList<QUrl> frameURLs;
QList<QByteArray> frameStateBuffers;
QList<int> fSizes;
QString encoding, sheetUsed;
long old_cacheId = d->m_cacheId;
stream >> u >> xOffset >> yOffset >> wContents >> hContents >> mWidth >> mHeight;
d->m_view->setMarginWidth( mWidth );
d->m_view->setMarginHeight( mHeight );
// restore link cursor position
// nth node is active. value is set in checkCompleted()
stream >> d->m_focusNodeNumber;
d->m_focusNodeRestored = false;
stream >> d->m_cacheId;
stream >> encoding >> sheetUsed >> docState;
d->m_encoding = encoding;
d->m_sheetUsed = sheetUsed;
int zoomFactor;
stream >> zoomFactor;
setZoomFactor(zoomFactor);
int fontScaleFactor;
stream >> fontScaleFactor;
setFontScaleFactor(fontScaleFactor);
stream >> d->m_httpHeaders;
stream >> d->m_pageServices;
stream >> d->m_pageReferrer;
// Restore ssl data
stream >> d->m_ssl_in_use
>> d->m_ssl_peer_chain
>> d->m_ssl_peer_ip
>> d->m_ssl_cipher
>> d->m_ssl_protocol_version
>> d->m_ssl_cipher_used_bits
>> d->m_ssl_cipher_bits
>> d->m_ssl_cert_errors
>> d->m_ssl_parent_ip
>> d->m_ssl_parent_cert;
setPageSecurity( d->m_ssl_in_use ? Encrypted : NotCrypted );
stream >> frameCount >> frameNames >> frameServiceTypes >> frameServiceNames
>> frameURLs >> frameStateBuffers >> frameTypes;
d->m_bComplete = false;
d->m_bLoadEventEmitted = false;
// kDebug( 6050 ) << "docState.count() = " << docState.count();
// kDebug( 6050 ) << "m_url " << url() << " <-> " << u;
// kDebug( 6050 ) << "m_frames.count() " << d->m_frames.count() << " <-> " << frameCount;
if (d->m_cacheId == old_cacheId && signed(frameCount) == d->m_frames.count())
{
// Partial restore
d->m_redirectionTimer.stop();
FrameIt fIt = d->m_frames.begin();
const FrameIt fEnd = d->m_frames.end();
for (; fIt != fEnd; ++fIt )
(*fIt)->m_bCompleted = false;
fIt = d->m_frames.begin();
QStringList::ConstIterator fNameIt = frameNames.constBegin();
QStringList::ConstIterator fServiceTypeIt = frameServiceTypes.constBegin();
QStringList::ConstIterator fServiceNameIt = frameServiceNames.constBegin();
QList<QUrl>::ConstIterator fURLIt = frameURLs.constBegin();
QList<QByteArray>::ConstIterator fBufferIt = frameStateBuffers.constBegin();
QList<int>::ConstIterator fFrameTypeIt = frameTypes.constBegin();
for (; fIt != fEnd; ++fIt, ++fNameIt, ++fServiceTypeIt, ++fServiceNameIt, ++fURLIt, ++fBufferIt, ++fFrameTypeIt )
{
khtml::ChildFrame* const child = *fIt;
// kDebug( 6050 ) << *fNameIt << " ---- " << *fServiceTypeIt;
if ( child->m_name != *fNameIt || child->m_serviceType != *fServiceTypeIt )
{
child->m_bPreloaded = true;
child->m_name = *fNameIt;
child->m_serviceName = *fServiceNameIt;
child->m_type = static_cast<khtml::ChildFrame::Type>(*fFrameTypeIt);
processObjectRequest( child, *fURLIt, *fServiceTypeIt );
}
if ( child->m_part )
{
child->m_bCompleted = false;
if ( child->m_extension && !(*fBufferIt).isEmpty() )
{
QDataStream frameStream( *fBufferIt );
child->m_extension.data()->restoreState( frameStream );
}
else
child->m_part.data()->openUrl( *fURLIt );
}
}
KParts::OpenUrlArguments args( arguments() );
args.setXOffset(xOffset);
args.setYOffset(yOffset);
setArguments(args);
KParts::BrowserArguments browserArgs( d->m_extension->browserArguments() );
browserArgs.docState = docState;
d->m_extension->setBrowserArguments(browserArgs);
d->m_view->resizeContents( wContents, hContents );
d->m_view->setContentsPos( xOffset, yOffset );
setUrl(u);
}
else
{
// Full restore.
closeUrl();
// We must force a clear because we want to be sure to delete all
// frames.
d->m_bCleared = false;
clear();
d->m_encoding = encoding;
d->m_sheetUsed = sheetUsed;
QStringList::ConstIterator fNameIt = frameNames.constBegin();
const QStringList::ConstIterator fNameEnd = frameNames.constEnd();
QStringList::ConstIterator fServiceTypeIt = frameServiceTypes.constBegin();
QStringList::ConstIterator fServiceNameIt = frameServiceNames.constBegin();
QList<QUrl>::ConstIterator fURLIt = frameURLs.constBegin();
QList<QByteArray>::ConstIterator fBufferIt = frameStateBuffers.constBegin();
QList<int>::ConstIterator fFrameTypeIt = frameTypes.constBegin();
for (; fNameIt != fNameEnd; ++fNameIt, ++fServiceTypeIt, ++fServiceNameIt, ++fURLIt, ++fBufferIt, ++fFrameTypeIt )
{
khtml::ChildFrame* const newChild = new khtml::ChildFrame;
newChild->m_bPreloaded = true;
newChild->m_name = *fNameIt;
newChild->m_serviceName = *fServiceNameIt;
newChild->m_type = static_cast<khtml::ChildFrame::Type>(*fFrameTypeIt);
// kDebug( 6050 ) << *fNameIt << " ---- " << *fServiceTypeIt;
const FrameIt childFrame = d->m_frames.insert( d->m_frames.end(), newChild );
processObjectRequest( *childFrame, *fURLIt, *fServiceTypeIt );
(*childFrame)->m_bPreloaded = true;
if ( (*childFrame)->m_part )
{
if ( (*childFrame)->m_extension && !(*fBufferIt).isEmpty() )
{
QDataStream frameStream( *fBufferIt );
(*childFrame)->m_extension.data()->restoreState( frameStream );
}
else
(*childFrame)->m_part.data()->openUrl( *fURLIt );
}
}
KParts::OpenUrlArguments args( arguments() );
args.setXOffset(xOffset);
args.setYOffset(yOffset);
setArguments(args);
KParts::BrowserArguments browserArgs( d->m_extension->browserArguments() );
browserArgs.docState = docState;
d->m_extension->setBrowserArguments(browserArgs);
if (!KHTMLPageCache::self()->isComplete(d->m_cacheId))
{
d->m_restored = true;
openUrl( u );
d->m_restored = false;
}
else
{
restoreURL( u );
}
}
}
void KHTMLPart::show()
{
if ( widget() )
widget()->show();
}
void KHTMLPart::hide()
{
if ( widget() )
widget()->hide();
}
DOM::Node KHTMLPart::nodeUnderMouse() const
{
return d->m_view->nodeUnderMouse();
}
DOM::Node KHTMLPart::nonSharedNodeUnderMouse() const
{
return d->m_view->nonSharedNodeUnderMouse();
}
void KHTMLPart::emitSelectionChanged()
{
// Don't emit signals about our selection if this is a frameset;
// the active frame has the selection (#187403)
if (!d->m_activeFrame)
{
emit d->m_extension->enableAction( "copy", hasSelection() );
emit d->m_extension->selectionInfo( selectedText() );
emit selectionChanged();
}
}
int KHTMLPart::zoomFactor() const
{
return d->m_zoomFactor;
}
// ### make the list configurable ?
static const int zoomSizes[] = { 20, 40, 60, 80, 90, 95, 100, 105, 110, 120, 140, 160, 180, 200, 250, 300 };
static const int zoomSizeCount = (sizeof(zoomSizes) / sizeof(int));
static const int minZoom = 20;
static const int maxZoom = 300;
// My idea of useful stepping ;-) (LS)
extern const int KHTML_NO_EXPORT fastZoomSizes[] = { 20, 50, 75, 90, 100, 120, 150, 200, 300 };
extern const int KHTML_NO_EXPORT fastZoomSizeCount = sizeof fastZoomSizes / sizeof fastZoomSizes[0];
void KHTMLPart::slotIncZoom()
{
zoomIn(zoomSizes, zoomSizeCount);
}
void KHTMLPart::slotDecZoom()
{
zoomOut(zoomSizes, zoomSizeCount);
}
void KHTMLPart::slotIncZoomFast()
{
zoomIn(fastZoomSizes, fastZoomSizeCount);
}
void KHTMLPart::slotDecZoomFast()
{
zoomOut(fastZoomSizes, fastZoomSizeCount);
}
void KHTMLPart::zoomIn(const int stepping[], int count)
{
int zoomFactor = d->m_zoomFactor;
if (zoomFactor < maxZoom) {
// find the entry nearest to the given zoomsizes
for (int i = 0; i < count; ++i)
if (stepping[i] > zoomFactor) {
zoomFactor = stepping[i];
break;
}
setZoomFactor(zoomFactor);
}
}
void KHTMLPart::zoomOut(const int stepping[], int count)
{
int zoomFactor = d->m_zoomFactor;
if (zoomFactor > minZoom) {
// find the entry nearest to the given zoomsizes
for (int i = count-1; i >= 0; --i)
if (stepping[i] < zoomFactor) {
zoomFactor = stepping[i];
break;
}
setZoomFactor(zoomFactor);
}
}
void KHTMLPart::setZoomFactor (int percent)
{
// ### zooming under 100% is majorly botched,
// so disable that for now.
if (percent < 100) percent = 100;
// ### if (percent < minZoom) percent = minZoom;
if (percent > maxZoom) percent = maxZoom;
if (d->m_zoomFactor == percent) return;
d->m_zoomFactor = percent;
updateZoomFactor();
}
void KHTMLPart::updateZoomFactor ()
{
if(d->m_view) {
QApplication::setOverrideCursor( Qt::WaitCursor );
d->m_view->setZoomLevel( d->m_zoomFactor );
QApplication::restoreOverrideCursor();
}
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it ) {
if ( KHTMLPart* p = qobject_cast<KHTMLPart*>((*it)->m_part.data()) )
p->setZoomFactor(d->m_zoomFactor);
}
if ( d->m_guiProfile == BrowserViewGUI ) {
d->m_paDecZoomFactor->setEnabled( d->m_zoomFactor > minZoom );
d->m_paIncZoomFactor->setEnabled( d->m_zoomFactor < maxZoom );
}
}
void KHTMLPart::slotIncFontSize()
{
incFontSize(zoomSizes, zoomSizeCount);
}
void KHTMLPart::slotDecFontSize()
{
decFontSize(zoomSizes, zoomSizeCount);
}
void KHTMLPart::slotIncFontSizeFast()
{
incFontSize(fastZoomSizes, fastZoomSizeCount);
}
void KHTMLPart::slotDecFontSizeFast()
{
decFontSize(fastZoomSizes, fastZoomSizeCount);
}
void KHTMLPart::incFontSize(const int stepping[], int count)
{
int zoomFactor = d->m_fontScaleFactor;
if (zoomFactor < maxZoom) {
// find the entry nearest to the given zoomsizes
for (int i = 0; i < count; ++i)
if (stepping[i] > zoomFactor) {
zoomFactor = stepping[i];
break;
}
setFontScaleFactor(zoomFactor);
}
}
void KHTMLPart::decFontSize(const int stepping[], int count)
{
int zoomFactor = d->m_fontScaleFactor;
if (zoomFactor > minZoom) {
// find the entry nearest to the given zoomsizes
for (int i = count-1; i >= 0; --i)
if (stepping[i] < zoomFactor) {
zoomFactor = stepping[i];
break;
}
setFontScaleFactor(zoomFactor);
}
}
void KHTMLPart::setFontScaleFactor(int percent)
{
if (percent < minZoom) percent = minZoom;
if (percent > maxZoom) percent = maxZoom;
if (d->m_fontScaleFactor == percent) return;
d->m_fontScaleFactor = percent;
if (d->m_view && d->m_doc) {
QApplication::setOverrideCursor( Qt::WaitCursor );
if (d->m_doc->styleSelector())
d->m_doc->styleSelector()->computeFontSizes(d->m_doc->logicalDpiY(), d->m_fontScaleFactor);
d->m_doc->recalcStyle( NodeImpl::Force );
QApplication::restoreOverrideCursor();
}
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it ) {
if ( KHTMLPart* p = qobject_cast<KHTMLPart*>((*it)->m_part.data()) )
p->setFontScaleFactor(d->m_fontScaleFactor);
}
}
int KHTMLPart::fontScaleFactor() const
{
return d->m_fontScaleFactor;
}
void KHTMLPart::slotZoomView( int delta )
{
if ( delta < 0 )
slotIncZoom();
else
slotDecZoom();
}
void KHTMLPart::setStatusBarText( const QString& text, StatusBarPriority p)
{
if (!d->m_statusMessagesEnabled)
return;
d->m_statusBarText[p] = text;
// shift handling ?
QString tobe = d->m_statusBarText[BarHoverText];
if (tobe.isEmpty())
tobe = d->m_statusBarText[BarOverrideText];
if (tobe.isEmpty()) {
tobe = d->m_statusBarText[BarDefaultText];
if (!tobe.isEmpty() && d->m_jobspeed)
tobe += " ";
if (d->m_jobspeed)
tobe += i18n( "(%1/s)" , KIO::convertSize( d->m_jobspeed ) );
}
tobe = "<qt>"+tobe;
emit ReadOnlyPart::setStatusBarText(tobe);
}
void KHTMLPart::setJSStatusBarText( const QString &text )
{
setStatusBarText(text, BarOverrideText);
}
void KHTMLPart::setJSDefaultStatusBarText( const QString &text )
{
setStatusBarText(text, BarDefaultText);
}
QString KHTMLPart::jsStatusBarText() const
{
return d->m_statusBarText[BarOverrideText];
}
QString KHTMLPart::jsDefaultStatusBarText() const
{
return d->m_statusBarText[BarDefaultText];
}
QString KHTMLPart::referrer() const
{
return d->m_referrer;
}
QString KHTMLPart::pageReferrer() const
{
KUrl referrerURL = KUrl( d->m_pageReferrer );
if (referrerURL.isValid())
{
QString protocol = referrerURL.scheme();
if ((protocol == "http") ||
((protocol == "https") && (url().scheme() == "https")))
{
referrerURL.setRef(QString());
referrerURL.setUser(QString());
referrerURL.setPass(QString());
return referrerURL.toString();
}
}
return QString();
}
QString KHTMLPart::lastModified() const
{
if ( d->m_lastModified.isEmpty() && url().isLocalFile() ) {
// Local file: set last-modified from the file's mtime.
// Done on demand to save time when this isn't needed - but can lead
// to slightly wrong results if updating the file on disk w/o reloading.
QDateTime lastModif = QFileInfo( url().toLocalFile() ).lastModified();
d->m_lastModified = lastModif.toString( Qt::LocalDate );
}
//kDebug(6050) << d->m_lastModified;
return d->m_lastModified;
}
void KHTMLPart::slotLoadImages()
{
if (d->m_doc )
d->m_doc->docLoader()->setAutoloadImages( !d->m_doc->docLoader()->autoloadImages() );
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it ) {
if ( KHTMLPart* p = qobject_cast<KHTMLPart*>((*it)->m_part.data()) )
p->slotLoadImages();
}
}
void KHTMLPart::reparseConfiguration()
{
KHTMLSettings *settings = KHTMLGlobal::defaultHTMLSettings();
settings->init();
setAutoloadImages( settings->autoLoadImages() );
if (d->m_doc)
d->m_doc->docLoader()->setShowAnimations( settings->showAnimations() );
d->m_bOpenMiddleClick = settings->isOpenMiddleClickEnabled();
d->m_bJScriptEnabled = settings->isJavaScriptEnabled(url().host());
setDebugScript( settings->isJavaScriptDebugEnabled() );
d->m_bJavaEnabled = settings->isJavaEnabled(url().host());
d->m_bPluginsEnabled = settings->isPluginsEnabled(url().host());
d->m_metaRefreshEnabled = settings->isAutoDelayedActionsEnabled ();
delete d->m_settings;
d->m_settings = new KHTMLSettings(*KHTMLGlobal::defaultHTMLSettings());
QApplication::setOverrideCursor( Qt::WaitCursor );
khtml::CSSStyleSelector::reparseConfiguration();
if(d->m_doc) d->m_doc->updateStyleSelector();
QApplication::restoreOverrideCursor();
if (d->m_view) {
KHTMLSettings::KSmoothScrollingMode ssm = d->m_settings->smoothScrolling();
if (ssm == KHTMLSettings::KSmoothScrollingDisabled)
d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMDisabled);
else if (ssm == KHTMLSettings::KSmoothScrollingWhenEfficient)
d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMWhenEfficient);
else
d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMEnabled);
}
if (KHTMLGlobal::defaultHTMLSettings()->isAdFilterEnabled())
runAdFilter();
}
QStringList KHTMLPart::frameNames() const
{
QStringList res;
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it )
if (!(*it)->m_bPreloaded && (*it)->m_part)
res += (*it)->m_name;
return res;
}
QList<KParts::ReadOnlyPart*> KHTMLPart::frames() const
{
QList<KParts::ReadOnlyPart*> res;
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it )
if (!(*it)->m_bPreloaded && (*it)->m_part) // ### TODO: make sure that we always create an empty
// KHTMLPart for frames so this never happens.
res.append( (*it)->m_part.data() );
return res;
}
bool KHTMLPart::openUrlInFrame( const KUrl &url, const KParts::OpenUrlArguments& args, const KParts::BrowserArguments &browserArgs)
{
kDebug( 6031 ) << this << url;
FrameIt it = d->m_frames.find( browserArgs.frameName );
if ( it == d->m_frames.end() )
return false;
// Inform someone that we are about to show something else.
if ( !browserArgs.lockHistory() )
emit d->m_extension->openUrlNotify();
requestObject( *it, url, args, browserArgs );
return true;
}
void KHTMLPart::setDNDEnabled( bool b )
{
d->m_bDnd = b;
}
bool KHTMLPart::dndEnabled() const
{
return d->m_bDnd;
}
void KHTMLPart::customEvent( QEvent *event )
{
if ( khtml::MousePressEvent::test( event ) )
{
khtmlMousePressEvent( static_cast<khtml::MousePressEvent *>( event ) );
return;
}
if ( khtml::MouseDoubleClickEvent::test( event ) )
{
khtmlMouseDoubleClickEvent( static_cast<khtml::MouseDoubleClickEvent *>( event ) );
return;
}
if ( khtml::MouseMoveEvent::test( event ) )
{
khtmlMouseMoveEvent( static_cast<khtml::MouseMoveEvent *>( event ) );
return;
}
if ( khtml::MouseReleaseEvent::test( event ) )
{
khtmlMouseReleaseEvent( static_cast<khtml::MouseReleaseEvent *>( event ) );
return;
}
if ( khtml::DrawContentsEvent::test( event ) )
{
khtmlDrawContentsEvent( static_cast<khtml::DrawContentsEvent *>( event ) );
return;
}
KParts::ReadOnlyPart::customEvent( event );
}
bool KHTMLPart::isPointInsideSelection(int x, int y)
{
// Treat a collapsed selection like no selection.
if (d->editor_context.m_selection.state() == Selection::CARET)
return false;
if (!xmlDocImpl()->renderer())
return false;
khtml::RenderObject::NodeInfo nodeInfo(true, true);
xmlDocImpl()->renderer()->layer()->nodeAtPoint(nodeInfo, x, y);
NodeImpl *innerNode = nodeInfo.innerNode();
if (!innerNode || !innerNode->renderer())
return false;
return innerNode->isPointInsideSelection(x, y, d->editor_context.m_selection);
}
/** returns the position of the first inline text box of the line at
* coordinate y in renderNode
*
* This is a helper function for line-by-line text selection.
*/
static bool firstRunAt(khtml::RenderObject *renderNode, int y, NodeImpl *&startNode, long &startOffset)
{
for (khtml::RenderObject *n = renderNode; n; n = n->nextSibling()) {
if (n->isText()) {
khtml::RenderText* const textRenderer = static_cast<khtml::RenderText *>(n);
for (khtml::InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (box->m_y == y && textRenderer->element()) {
startNode = textRenderer->element();
startOffset = box->m_start;
return true;
}
}
}
if (firstRunAt(n->firstChild(), y, startNode, startOffset)) {
return true;
}
}
return false;
}
/** returns the position of the last inline text box of the line at
* coordinate y in renderNode
*
* This is a helper function for line-by-line text selection.
*/
static bool lastRunAt(khtml::RenderObject *renderNode, int y, NodeImpl *&endNode, long &endOffset)
{
khtml::RenderObject *n = renderNode;
if (!n) {
return false;
}
khtml::RenderObject *next;
while ((next = n->nextSibling())) {
n = next;
}
while (1) {
if (lastRunAt(n->firstChild(), y, endNode, endOffset)) {
return true;
}
if (n->isText()) {
khtml::RenderText* const textRenderer = static_cast<khtml::RenderText *>(n);
for (khtml::InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (box->m_y == y && textRenderer->element()) {
endNode = textRenderer->element();
endOffset = box->m_start + box->m_len;
return true;
}
}
}
if (n == renderNode) {
return false;
}
n = n->previousSibling();
}
}
void KHTMLPart::handleMousePressEventDoubleClick(khtml::MouseDoubleClickEvent *event)
{
QMouseEvent *mouse = event->qmouseEvent();
DOM::Node innerNode = event->innerNode();
Selection selection;
if (mouse->button() == Qt::LeftButton && !innerNode.isNull() && innerNode.handle()->renderer() &&
innerNode.handle()->renderer()->shouldSelect()) {
Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()).position());
if (pos.node() && (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE)) {
selection.moveTo(pos);
selection.expandUsingGranularity(Selection::WORD);
}
}
if (selection.state() != Selection::CARET) {
d->editor_context.beginSelectingText(Selection::WORD);
}
setCaret(selection);
startAutoScroll();
}
void KHTMLPart::handleMousePressEventTripleClick(khtml::MouseDoubleClickEvent *event)
{
QMouseEvent *mouse = event->qmouseEvent();
DOM::Node innerNode = event->innerNode();
Selection selection;
if (mouse->button() == Qt::LeftButton && !innerNode.isNull() && innerNode.handle()->renderer() &&
innerNode.handle()->renderer()->shouldSelect()) {
Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()).position());
if (pos.node() && (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE)) {
selection.moveTo(pos);
selection.expandUsingGranularity(Selection::LINE);
}
}
if (selection.state() != Selection::CARET) {
d->editor_context.beginSelectingText(Selection::LINE);
}
setCaret(selection);
startAutoScroll();
}
void KHTMLPart::handleMousePressEventSingleClick(khtml::MousePressEvent *event)
{
QMouseEvent *mouse = event->qmouseEvent();
DOM::Node innerNode = event->innerNode();
if (mouse->button() == Qt::LeftButton) {
Selection sel;
if (!innerNode.isNull() && innerNode.handle()->renderer() &&
innerNode.handle()->renderer()->shouldSelect()) {
bool extendSelection = mouse->modifiers() & Qt::ShiftModifier;
// Don't restart the selection when the mouse is pressed on an
// existing selection so we can allow for text dragging.
if (!extendSelection && isPointInsideSelection(event->x(), event->y())) {
return;
}
Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()).position());
if (pos.isEmpty())
pos = Position(innerNode.handle(), innerNode.handle()->caretMinOffset());
kDebug(6050) << event->x() << event->y() << pos << endl;
sel = caret();
if (extendSelection && sel.notEmpty()) {
sel.clearModifyBias();
sel.setExtent(pos);
if (d->editor_context.m_selectionGranularity != Selection::CHARACTER) {
sel.expandUsingGranularity(d->editor_context.m_selectionGranularity);
}
d->editor_context.m_beganSelectingText = true;
} else {
sel = pos;
d->editor_context.m_selectionGranularity = Selection::CHARACTER;
}
}
setCaret(sel);
startAutoScroll();
}
}
void KHTMLPart::khtmlMousePressEvent( khtml::MousePressEvent *event )
{
DOM::DOMString url = event->url();
QMouseEvent *_mouse = event->qmouseEvent();
DOM::Node innerNode = event->innerNode();
d->m_mousePressNode = innerNode;
d->m_dragStartPos = QPoint(event->x(), event->y());
if ( !event->url().isNull() ) {
d->m_strSelectedURL = event->url().string();
d->m_strSelectedURLTarget = event->target().string();
}
else {
d->m_strSelectedURL.clear();
d->m_strSelectedURLTarget.clear();
}
if ( _mouse->button() == Qt::LeftButton ||
_mouse->button() == Qt::MidButton )
{
d->m_bMousePressed = true;
#ifdef KHTML_NO_SELECTION
d->m_dragLastPos = _mouse->globalPos();
#else
if ( _mouse->button() == Qt::LeftButton )
{
if ( (!d->m_strSelectedURL.isNull() && !isEditable())
|| (!d->m_mousePressNode.isNull() && d->m_mousePressNode.elementId() == ID_IMG) )
return;
d->editor_context.m_beganSelectingText = false;
handleMousePressEventSingleClick(event);
}
#endif
}
if ( _mouse->button() == Qt::RightButton )
{
popupMenu( d->m_strSelectedURL );
// might be deleted, don't touch "this"
}
}
void KHTMLPart::khtmlMouseDoubleClickEvent( khtml::MouseDoubleClickEvent *event )
{
QMouseEvent *_mouse = event->qmouseEvent();
if ( _mouse->button() == Qt::LeftButton )
{
d->m_bMousePressed = true;
d->editor_context.m_beganSelectingText = false;
if (event->clickCount() == 2) {
handleMousePressEventDoubleClick(event);
return;
}
if (event->clickCount() >= 3) {
handleMousePressEventTripleClick(event);
return;
}
}
}
#ifndef KHTML_NO_SELECTION
bool KHTMLPart::isExtendingSelection() const
{
// This is it, the whole detection. khtmlMousePressEvent only sets this
// on LMB or MMB, but never on RMB. As text selection doesn't work for MMB,
// it's sufficient to only rely on this flag to detect selection extension.
return d->editor_context.m_beganSelectingText;
}
void KHTMLPart::extendSelectionTo(int x, int y, const DOM::Node &innerNode)
{
// handle making selection
Position pos(innerNode.handle()->positionForCoordinates(x, y).position());
// Don't modify the selection if we're not on a node.
if (pos.isEmpty())
return;
// Restart the selection if this is the first mouse move. This work is usually
// done in khtmlMousePressEvent, but not if the mouse press was on an existing selection.
Selection sel = caret();
sel.clearModifyBias();
if (!d->editor_context.m_beganSelectingText) {
// We are beginning a selection during press-drag, when the original click
// wasn't appropriate for one. Make sure to set the granularity.
d->editor_context.beginSelectingText(Selection::CHARACTER);
sel.moveTo(pos);
}
sel.setExtent(pos);
if (d->editor_context.m_selectionGranularity != Selection::CHARACTER) {
sel.expandUsingGranularity(d->editor_context.m_selectionGranularity);
}
setCaret(sel);
}
#endif // KHTML_NO_SELECTION
bool KHTMLPart::handleMouseMoveEventDrag(khtml::MouseMoveEvent *event)
{
#ifdef QT_NO_DRAGANDDROP
return false;
#else
if (!dndEnabled())
return false;
DOM::Node innerNode = event->innerNode();
if( (d->m_bMousePressed &&
( (!d->m_strSelectedURL.isEmpty() && !isEditable())
|| (!d->m_mousePressNode.isNull() && d->m_mousePressNode.elementId() == ID_IMG) ) )
&& ( d->m_dragStartPos - QPoint(event->x(), event->y()) ).manhattanLength() > QApplication::startDragDistance() ) {
DOM::DOMString url = event->url();
QPixmap pix;
HTMLImageElementImpl *img = 0L;
KUrl u;
// qDebug("****************** Event URL: %s", url.string().toLatin1().constData());
// qDebug("****************** Event Target: %s", target.string().toLatin1().constData());
// Normal image...
if ( url.length() == 0 && innerNode.handle() && innerNode.handle()->id() == ID_IMG )
{
img = static_cast<HTMLImageElementImpl *>(innerNode.handle());
u = KUrl( completeURL( khtml::parseURL(img->getAttribute(ATTR_SRC)).string() ) );
pix = KIconLoader::global()->loadIcon("image-x-generic", KIconLoader::Desktop);
}
else
{
// Text or image link...
u = completeURL( d->m_strSelectedURL );
pix = KIO::pixmapForUrl(u, 0, KIconLoader::Desktop, KIconLoader::SizeMedium);
}
u.setPass(QString());
QDrag *drag = new QDrag( d->m_view->viewport() );
QMap<QString, QString> metaDataMap;
if ( !d->m_referrer.isEmpty() )
metaDataMap.insert( "referrer", d->m_referrer );
QMimeData* mimeData = new QMimeData();
mimeData->setUrls( QList<QUrl>() << u );
KUrlMimeData::setMetaData( metaDataMap, mimeData );
drag->setMimeData( mimeData );
if( img && img->complete() )
drag->mimeData()->setImageData( img->currentImage() );
if ( !pix.isNull() )
drag->setPixmap( pix );
stopAutoScroll();
drag->start();
// when we finish our drag, we need to undo our mouse press
d->m_bMousePressed = false;
d->m_strSelectedURL.clear();
d->m_strSelectedURLTarget.clear();
return true;
}
return false;
#endif // QT_NO_DRAGANDDROP
}
bool KHTMLPart::handleMouseMoveEventOver(khtml::MouseMoveEvent *event)
{
// Mouse clicked -> do nothing
if ( d->m_bMousePressed ) return false;
DOM::DOMString url = event->url();
// The mouse is over something
if ( url.length() )
{
DOM::DOMString target = event->target();
QMouseEvent *_mouse = event->qmouseEvent();
DOM::Node innerNode = event->innerNode();
bool shiftPressed = ( _mouse->modifiers() & Qt::ShiftModifier );
// Image map
if ( !innerNode.isNull() && innerNode.elementId() == ID_IMG )
{
HTMLImageElementImpl *i = static_cast<HTMLImageElementImpl *>(innerNode.handle());
if ( i && i->isServerMap() )
{
khtml::RenderObject *r = i->renderer();
if(r)
{
int absx, absy;
r->absolutePosition(absx, absy);
int x(event->x() - absx), y(event->y() - absy);
d->m_overURL = url.string() + QString("?%1,%2").arg(x).arg(y);
d->m_overURLTarget = target.string();
overURL( d->m_overURL, target.string(), shiftPressed );
return true;
}
}
}
// normal link
if ( d->m_overURL.isEmpty() || d->m_overURL != url || d->m_overURLTarget != target )
{
d->m_overURL = url.string();
d->m_overURLTarget = target.string();
overURL( d->m_overURL, target.string(), shiftPressed );
}
}
else // Not over a link...
{
if( !d->m_overURL.isEmpty() ) // and we were over a link -> reset to "default statusbar text"
{
// reset to "default statusbar text"
resetHoverText();
}
}
return true;
}
void KHTMLPart::handleMouseMoveEventSelection(khtml::MouseMoveEvent *event)
{
// Mouse not pressed. Do nothing.
if (!d->m_bMousePressed)
return;
#ifdef KHTML_NO_SELECTION
if (d->m_doc && d->m_view) {
QPoint diff( mouse->globalPos() - d->m_dragLastPos );
if (abs(diff.x()) > 64 || abs(diff.y()) > 64) {
d->m_view->scrollBy(-diff.x(), -diff.y());
d->m_dragLastPos = mouse->globalPos();
}
}
#else
QMouseEvent *mouse = event->qmouseEvent();
DOM::Node innerNode = event->innerNode();
if ( (mouse->buttons() & Qt::LeftButton) == 0 || !innerNode.handle() || !innerNode.handle()->renderer() ||
!innerNode.handle()->renderer()->shouldSelect())
return;
// handle making selection
extendSelectionTo(event->x(), event->y(), innerNode);
#endif // KHTML_NO_SELECTION
}
void KHTMLPart::khtmlMouseMoveEvent( khtml::MouseMoveEvent *event )
{
if (handleMouseMoveEventDrag(event))
return;
if (handleMouseMoveEventOver(event))
return;
handleMouseMoveEventSelection(event);
}
void KHTMLPart::khtmlMouseReleaseEvent( khtml::MouseReleaseEvent *event )
{
DOM::Node innerNode = event->innerNode();
d->m_mousePressNode = DOM::Node();
if ( d->m_bMousePressed ) {
setStatusBarText(QString(), BarHoverText);
stopAutoScroll();
}
// Used to prevent mouseMoveEvent from initiating a drag before
// the mouse is pressed again.
d->m_bMousePressed = false;
#ifndef QT_NO_CLIPBOARD
QMouseEvent *_mouse = event->qmouseEvent();
if ((d->m_guiProfile == BrowserViewGUI) && (_mouse->button() == Qt::MidButton) && (event->url().isNull())) {
kDebug( 6050 ) << "MMB shouldOpen=" << d->m_bOpenMiddleClick;
if (d->m_bOpenMiddleClick) {
KHTMLPart *p = this;
while (p->parentPart()) p = p->parentPart();
p->d->m_extension->pasteRequest();
}
}
#endif
#ifndef KHTML_NO_SELECTION
{
// Clear the selection if the mouse didn't move after the last mouse press.
// We do this so when clicking on the selection, the selection goes away.
// However, if we are editing, place the caret.
if (!d->editor_context.m_beganSelectingText
&& d->m_dragStartPos.x() == event->x()
&& d->m_dragStartPos.y() == event->y()
&& d->editor_context.m_selection.state() == Selection::RANGE) {
Selection selection;
#ifdef APPLE_CHANGES
if (d->editor_context.m_selection.base().node()->isContentEditable())
#endif
selection.moveTo(d->editor_context.m_selection.base().node()->positionForCoordinates(event->x(), event->y()).position());
setCaret(selection);
}
// get selected text and paste to the clipboard
#ifndef QT_NO_CLIPBOARD
QString text = selectedText();
text.replace(QChar(0xa0), ' ');
if (!text.isEmpty()) {
disconnect( qApp->clipboard(), SIGNAL(selectionChanged()), this, SLOT(slotClearSelection()));
qApp->clipboard()->setText(text,QClipboard::Selection);
connect( qApp->clipboard(), SIGNAL(selectionChanged()), SLOT(slotClearSelection()));
}
#endif
//kDebug( 6000 ) << "selectedText = " << text;
emitSelectionChanged();
//kDebug(6000) << "rel2: startBefEnd " << d->m_startBeforeEnd << " extAtEnd " << d->m_extendAtEnd << " (" << d->m_startOffset << ") - (" << d->m_endOffset << "), caretOfs " << d->caretOffset();
}
#endif
}
void KHTMLPart::khtmlDrawContentsEvent( khtml::DrawContentsEvent * )
{
}
void KHTMLPart::guiActivateEvent( KParts::GUIActivateEvent *event )
{
if ( event->activated() )
{
emitSelectionChanged();
emit d->m_extension->enableAction( "print", d->m_doc != 0 );
if ( !d->m_settings->autoLoadImages() && d->m_paLoadImages )
{
QList<QAction*> lst;
lst.append( d->m_paLoadImages );
plugActionList( "loadImages", lst );
}
}
}
void KHTMLPart::slotPrintFrame()
{
if ( d->m_frames.count() == 0 )
return;
KParts::ReadOnlyPart *frame = currentFrame();
if (!frame)
return;
KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject( frame );
if ( !ext )
return;
const QMetaObject *mo = ext->metaObject();
if (mo->indexOfSlot( "print()") != -1)
QMetaObject::invokeMethod(ext, "print()", Qt::DirectConnection);
}
void KHTMLPart::slotSelectAll()
{
KParts::ReadOnlyPart *part = currentFrame();
if (part && part->inherits("KHTMLPart"))
static_cast<KHTMLPart *>(part)->selectAll();
}
void KHTMLPart::startAutoScroll()
{
connect(&d->m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
d->m_scrollTimer.setSingleShot(false);
d->m_scrollTimer.start(100);
}
void KHTMLPart::stopAutoScroll()
{
disconnect(&d->m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
if (d->m_scrollTimer.isActive())
d->m_scrollTimer.stop();
}
void KHTMLPart::slotAutoScroll()
{
if (d->m_view)
d->m_view->doAutoScroll();
else
stopAutoScroll(); // Safety
}
void KHTMLPart::runAdFilter()
{
if ( parentPart() )
parentPart()->runAdFilter();
if ( !d->m_doc )
return;
QSetIterator<khtml::CachedObject*> it( d->m_doc->docLoader()->m_docObjects );
while (it.hasNext())
{
khtml::CachedObject* obj = it.next();
if ( obj->type() == khtml::CachedObject::Image ) {
khtml::CachedImage *image = static_cast<khtml::CachedImage *>(obj);
bool wasBlocked = image->m_wasBlocked;
image->m_wasBlocked = KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( d->m_doc->completeURL( image->url().string() ) );
if ( image->m_wasBlocked != wasBlocked )
image->do_notify(QRect(QPoint(0,0), image->pixmap_size()));
}
}
if ( KHTMLGlobal::defaultHTMLSettings()->isHideAdsEnabled() ) {
for ( NodeImpl *nextNode, *node = d->m_doc; node; node = nextNode ) {
// We might be deleting 'node' shortly.
nextNode = node->traverseNextNode();
if ( node->id() == ID_IMG ||
node->id() == ID_IFRAME ||
(node->id() == ID_INPUT && static_cast<HTMLInputElementImpl *>(node)->inputType() == HTMLInputElementImpl::IMAGE ))
{
if ( KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( d->m_doc->completeURL( static_cast<ElementImpl *>(node)->getAttribute(ATTR_SRC).string() ) ) )
{
// Since any kids of node will be deleted, too, fastforward nextNode
// until we get outside of node.
while (nextNode && nextNode->isAncestor(node))
nextNode = nextNode->traverseNextNode();
node->ref();
NodeImpl *parent = node->parent();
if( parent )
{
int exception = 0;
parent->removeChild(node, exception);
}
node->deref();
}
}
}
}
}
void KHTMLPart::selectAll()
{
if (!d->m_doc) return;
NodeImpl *first;
if (d->m_doc->isHTMLDocument())
first = static_cast<HTMLDocumentImpl*>(d->m_doc)->body();
else
first = d->m_doc;
NodeImpl *next;
// Look for first text/cdata node that has a renderer,
// or first childless replaced element
while ( first && !(first->renderer()
&& ((first->nodeType() == Node::TEXT_NODE || first->nodeType() == Node::CDATA_SECTION_NODE)
|| (first->renderer()->isReplaced() && !first->renderer()->firstChild()))))
{
next = first->firstChild();
if ( !next ) next = first->nextSibling();
while( first && !next )
{
first = first->parentNode();
if ( first )
next = first->nextSibling();
}
first = next;
}
NodeImpl *last;
if (d->m_doc->isHTMLDocument())
last = static_cast<HTMLDocumentImpl*>(d->m_doc)->body();
else
last = d->m_doc;
// Look for last text/cdata node that has a renderer,
// or last childless replaced element
// ### Instead of changing this loop, use findLastSelectableNode
// in render_table.cpp (LS)
while ( last && !(last->renderer()
&& ((last->nodeType() == Node::TEXT_NODE || last->nodeType() == Node::CDATA_SECTION_NODE)
|| (last->renderer()->isReplaced() && !last->renderer()->lastChild()))))
{
next = last->lastChild();
if ( !next ) next = last->previousSibling();
while ( last && !next )
{
last = last->parentNode();
if ( last )
next = last->previousSibling();
}
last = next;
}
if ( !first || !last )
return;
Q_ASSERT(first->renderer());
Q_ASSERT(last->renderer());
d->editor_context.m_selection.moveTo(Position(first, 0), Position(last, last->nodeValue().length()));
d->m_doc->updateSelection();
emitSelectionChanged();
}
bool KHTMLPart::checkLinkSecurity(const KUrl &linkURL,const KLocalizedString &message, const QString &button)
{
bool linkAllowed = true;
if ( d->m_doc )
linkAllowed = KAuthorized::authorizeUrlAction("redirect", url(), linkURL);
if ( !linkAllowed ) {
khtml::Tokenizer *tokenizer = d->m_doc->tokenizer();
if (tokenizer)
tokenizer->setOnHold(true);
int response = KMessageBox::Cancel;
if (!message.isEmpty())
{
// Dangerous flag makes the Cancel button the default
response = KMessageBox::warningContinueCancel( 0,
message.subs(Qt::escape(linkURL.toDisplayString())).toString(),
i18n( "Security Warning" ),
KGuiItem(button),
KStandardGuiItem::cancel(),
QString(), // no don't ask again info
KMessageBox::Notify | KMessageBox::Dangerous );
}
else
{
KMessageBox::error( 0,
i18n( "<qt>Access by untrusted page to<br /><b>%1</b><br /> denied.</qt>", Qt::escape(linkURL.toDisplayString())),
i18n( "Security Alert" ));
}
if (tokenizer)
tokenizer->setOnHold(false);
return (response==KMessageBox::Continue);
}
return true;
}
void KHTMLPart::slotPartRemoved( KParts::Part *part )
{
// kDebug(6050) << part;
if ( part == d->m_activeFrame )
{
d->m_activeFrame = 0L;
if ( !part->inherits( "KHTMLPart" ) )
{
if (factory()) {
factory()->removeClient( part );
}
if (childClients().contains(part)) {
removeChildClient( part );
}
}
}
}
void KHTMLPart::slotActiveFrameChanged( KParts::Part *part )
{
// kDebug(6050) << this << "part=" << part;
if ( part == this )
{
kError(6050) << "strange error! we activated ourselves";
assert( false );
return;
}
// kDebug(6050) << "d->m_activeFrame=" << d->m_activeFrame;
if ( d->m_activeFrame && d->m_activeFrame->widget() && d->m_activeFrame->widget()->inherits( "QFrame" ) )
{
QFrame *frame = static_cast<QFrame *>( d->m_activeFrame->widget() );
if (frame->frameStyle() != QFrame::NoFrame)
{
frame->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken);
frame->repaint();
}
}
if( d->m_activeFrame && !d->m_activeFrame->inherits( "KHTMLPart" ) )
{
if (factory()) {
factory()->removeClient( d->m_activeFrame );
}
removeChildClient( d->m_activeFrame );
}
if( part && !part->inherits( "KHTMLPart" ) )
{
if (factory()) {
factory()->addClient( part );
}
insertChildClient( part );
}
d->m_activeFrame = part;
if ( d->m_activeFrame && d->m_activeFrame->widget()->inherits( "QFrame" ) )
{
QFrame *frame = static_cast<QFrame *>( d->m_activeFrame->widget() );
if (frame->frameStyle() != QFrame::NoFrame)
{
frame->setFrameStyle( QFrame::StyledPanel | QFrame::Plain);
frame->repaint();
}
kDebug(6050) << "new active frame " << d->m_activeFrame;
}
updateActions();
// (note: childObject returns 0 if the argument is 0)
d->m_extension->setExtensionProxy( KParts::BrowserExtension::childObject( d->m_activeFrame ) );
}
void KHTMLPart::setActiveNode(const DOM::Node &node)
{
if (!d->m_doc || !d->m_view)
return;
// Set the document's active node
d->m_doc->setFocusNode(node.handle());
// Scroll the view if necessary to ensure that the new focus node is visible
QRect rect = node.handle()->getRect();
d->m_view->ensureVisible(rect.right(), rect.bottom());
d->m_view->ensureVisible(rect.left(), rect.top());
}
DOM::Node KHTMLPart::activeNode() const
{
return DOM::Node(d->m_doc?d->m_doc->focusNode():0);
}
DOM::EventListener *KHTMLPart::createHTMLEventListener( QString code, QString name, NodeImpl* node, bool svg )
{
KJSProxy *proxy = jScript();
if (!proxy)
return 0;
return proxy->createHTMLEventHandler( url().toString(), name, code, node, svg );
}
KHTMLPart *KHTMLPart::opener()
{
return d->m_opener;
}
void KHTMLPart::setOpener(KHTMLPart *_opener)
{
d->m_opener = _opener;
}
bool KHTMLPart::openedByJS()
{
return d->m_openedByJS;
}
void KHTMLPart::setOpenedByJS(bool _openedByJS)
{
d->m_openedByJS = _openedByJS;
}
void KHTMLPart::preloadStyleSheet(const QString &url, const QString &stylesheet)
{
khtml::Cache::preloadStyleSheet(url, stylesheet);
}
void KHTMLPart::preloadScript(const QString &url, const QString &script)
{
khtml::Cache::preloadScript(url, script);
}
long KHTMLPart::cacheId() const
{
return d->m_cacheId;
}
bool KHTMLPart::restored() const
{
return d->m_restored;
}
bool KHTMLPart::pluginPageQuestionAsked(const QString& mimetype) const
{
// parentPart() should be const!
KHTMLPart* parent = const_cast<KHTMLPart *>(this)->parentPart();
if ( parent )
return parent->pluginPageQuestionAsked(mimetype);
return d->m_pluginPageQuestionAsked.contains(mimetype);
}
void KHTMLPart::setPluginPageQuestionAsked(const QString& mimetype)
{
if ( parentPart() )
parentPart()->setPluginPageQuestionAsked(mimetype);
d->m_pluginPageQuestionAsked.append(mimetype);
}
KEncodingDetector *KHTMLPart::createDecoder()
{
KEncodingDetector *dec = new KEncodingDetector();
if( !d->m_encoding.isNull() )
dec->setEncoding( d->m_encoding.toLatin1().constData(),
d->m_haveEncoding ? KEncodingDetector::UserChosenEncoding : KEncodingDetector::EncodingFromHTTPHeader);
else {
// Inherit the default encoding from the parent frame if there is one.
QByteArray defaultEncoding = (parentPart() && parentPart()->d->m_decoder)
? QByteArray( parentPart()->d->m_decoder->encoding() ) : settings()->encoding().toLatin1();
dec->setEncoding(defaultEncoding.constData(), KEncodingDetector::DefaultEncoding);
}
if (d->m_doc)
d->m_doc->setDecoder(dec);
// convert from KEncodingProber::ProberType to KEncodingDetector::AutoDetectScript
KEncodingDetector::AutoDetectScript scri;
switch (d->m_autoDetectLanguage) {
case KEncodingProber::None: scri = KEncodingDetector::None; break;
case KEncodingProber::Universal: scri = KEncodingDetector::SemiautomaticDetection; break;
case KEncodingProber::Arabic: scri = KEncodingDetector::Arabic; break;
case KEncodingProber::Baltic: scri = KEncodingDetector::Baltic; break;
case KEncodingProber::CentralEuropean: scri = KEncodingDetector::CentralEuropean; break;
case KEncodingProber::ChineseSimplified: scri = KEncodingDetector::ChineseSimplified; break;
case KEncodingProber::ChineseTraditional: scri = KEncodingDetector::ChineseTraditional; break;
case KEncodingProber::Cyrillic: scri = KEncodingDetector::Cyrillic; break;
case KEncodingProber::Greek: scri = KEncodingDetector::Greek; break;
case KEncodingProber::Hebrew: scri = KEncodingDetector::Hebrew; break;
case KEncodingProber::Japanese: scri = KEncodingDetector::Japanese; break;
case KEncodingProber::Korean: scri = KEncodingDetector::Korean; break;
case KEncodingProber::NorthernSaami: scri = KEncodingDetector::NorthernSaami; break;
case KEncodingProber::Other: scri = KEncodingDetector::SemiautomaticDetection; break;
case KEncodingProber::SouthEasternEurope: scri = KEncodingDetector::SouthEasternEurope; break;
case KEncodingProber::Thai: scri = KEncodingDetector::Thai; break;
case KEncodingProber::Turkish: scri = KEncodingDetector::Turkish; break;
case KEncodingProber::Unicode: scri = KEncodingDetector::Unicode; break;
case KEncodingProber::WesternEuropean: scri = KEncodingDetector::WesternEuropean; break;
default: scri = KEncodingDetector::SemiautomaticDetection; break;
}
dec->setAutoDetectLanguage( scri );
return dec;
}
void KHTMLPart::emitCaretPositionChanged(const DOM::Position &pos) {
// pos must not be already converted to range-compliant coordinates
Position rng_pos = pos.equivalentRangeCompliantPosition();
Node node = rng_pos.node();
emit caretPositionChanged(node, rng_pos.offset());
}
void KHTMLPart::restoreScrollPosition()
{
const KParts::OpenUrlArguments args( arguments() );
if ( url().hasFragment() && !d->m_restoreScrollPosition && !args.reload()) {
if ( !d->m_doc || !d->m_doc->parsing() )
disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
if ( !gotoAnchor(KUrl(url()).ref()) )
gotoAnchor(url().fragment());
return;
}
// Check whether the viewport has become large enough to encompass the stored
// offsets. If the document has been fully loaded, force the new coordinates,
// even if the canvas is too short (can happen when user resizes the window
// during loading).
if (d->m_view->contentsHeight() - d->m_view->visibleHeight() >= args.yOffset()
|| d->m_bComplete) {
d->m_view->setContentsPos(args.xOffset(), args.yOffset());
disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
}
}
void KHTMLPart::openWallet(DOM::HTMLFormElementImpl *form)
{
#ifndef KHTML_NO_WALLET
KHTMLPart *p;
for (p = parentPart(); p && p->parentPart(); p = p->parentPart()) {
}
if (p) {
p->openWallet(form);
return;
}
if (onlyLocalReferences()) { // avoid triggering on local apps, thumbnails
return;
}
if (d->m_wallet) {
if (d->m_bWalletOpened) {
if (d->m_wallet->isOpen()) {
form->walletOpened(d->m_wallet);
return;
}
d->m_wallet->deleteLater();
d->m_wallet = 0L;
d->m_bWalletOpened = false;
}
}
if (!d->m_wq) {
KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), widget() ? widget()->topLevelWidget()->winId() : 0, KWallet::Wallet::Asynchronous);
d->m_wq = new KHTMLWalletQueue(this);
d->m_wq->wallet = wallet;
connect(wallet, SIGNAL(walletOpened(bool)), d->m_wq, SLOT(walletOpened(bool)));
connect(d->m_wq, SIGNAL(walletOpened(KWallet::Wallet*)), this, SLOT(walletOpened(KWallet::Wallet*)));
}
assert(form);
d->m_wq->callers.append(KHTMLWalletQueue::Caller(form, form->document()));
#endif // KHTML_NO_WALLET
}
void KHTMLPart::saveToWallet(const QString& key, const QMap<QString,QString>& data)
{
#ifndef KHTML_NO_WALLET
KHTMLPart *p;
for (p = parentPart(); p && p->parentPart(); p = p->parentPart()) {
}
if (p) {
p->saveToWallet(key, data);
return;
}
if (d->m_wallet) {
if (d->m_bWalletOpened) {
if (d->m_wallet->isOpen()) {
if (!d->m_wallet->hasFolder(KWallet::Wallet::FormDataFolder())) {
d->m_wallet->createFolder(KWallet::Wallet::FormDataFolder());
}
d->m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
d->m_wallet->writeMap(key, data);
return;
}
d->m_wallet->deleteLater();
d->m_wallet = 0L;
d->m_bWalletOpened = false;
}
}
if (!d->m_wq) {
KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), widget() ? widget()->topLevelWidget()->winId() : 0, KWallet::Wallet::Asynchronous);
d->m_wq = new KHTMLWalletQueue(this);
d->m_wq->wallet = wallet;
connect(wallet, SIGNAL(walletOpened(bool)), d->m_wq, SLOT(walletOpened(bool)));
connect(d->m_wq, SIGNAL(walletOpened(KWallet::Wallet*)), this, SLOT(walletOpened(KWallet::Wallet*)));
}
d->m_wq->savers.append(qMakePair(key, data));
#endif // KHTML_NO_WALLET
}
void KHTMLPart::dequeueWallet(DOM::HTMLFormElementImpl *form) {
#ifndef KHTML_NO_WALLET
KHTMLPart *p;
for (p = parentPart(); p && p->parentPart(); p = p->parentPart()) {
}
if (p) {
p->dequeueWallet(form);
return;
}
if (d->m_wq) {
d->m_wq->callers.removeAll(KHTMLWalletQueue::Caller(form, form->document()));
}
#endif // KHTML_NO_WALLET
}
void KHTMLPart::walletOpened(KWallet::Wallet *wallet) {
#ifndef KHTML_NO_WALLET
assert(!d->m_wallet);
assert(d->m_wq);
d->m_wq->deleteLater(); // safe?
d->m_wq = 0L;
if (!wallet) {
d->m_bWalletOpened = false;
return;
}
d->m_wallet = wallet;
d->m_bWalletOpened = true;
connect(d->m_wallet, SIGNAL(walletClosed()), SLOT(slotWalletClosed()));
d->m_walletForms.clear();
if (!d->m_statusBarWalletLabel) {
d->m_statusBarWalletLabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
d->m_statusBarWalletLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
d->m_statusBarWalletLabel->setUseCursor(false);
d->m_statusBarExtension->addStatusBarItem(d->m_statusBarWalletLabel, 0, false);
d->m_statusBarWalletLabel->setPixmap(SmallIcon("wallet-open"));
connect(d->m_statusBarWalletLabel, SIGNAL(leftClickedUrl()), SLOT(launchWalletManager()));
connect(d->m_statusBarWalletLabel, SIGNAL(rightClickedUrl()), SLOT(walletMenu()));
}
d->m_statusBarWalletLabel->setToolTip(i18n("The wallet '%1' is open and being used for form data and passwords.", KWallet::Wallet::NetworkWallet()));
#endif // KHTML_NO_WALLET
}
KWallet::Wallet *KHTMLPart::wallet()
{
#ifndef KHTML_NO_WALLET
KHTMLPart *p;
for (p = parentPart(); p && p->parentPart(); p = p->parentPart())
;
if (p)
return p->wallet();
return d->m_wallet;
#else
return 0;
#endif // !KHTML_NO_WALLET
}
void KHTMLPart::slotWalletClosed()
{
#ifndef KHTML_NO_WALLET
if (d->m_wallet) {
d->m_wallet->deleteLater();
d->m_wallet = 0L;
}
d->m_bWalletOpened = false;
if (d->m_statusBarWalletLabel) {
d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarWalletLabel);
delete d->m_statusBarWalletLabel;
d->m_statusBarWalletLabel = 0L;
}
#endif // KHTML_NO_WALLET
}
void KHTMLPart::launchWalletManager()
{
#ifndef KHTML_NO_WALLET
QDBusInterface r("org.kde.kwalletmanager", "/kwalletmanager/MainWindow_1",
"org.kde.KMainWindow");
if (!r.isValid()) {
KToolInvocation::startServiceByDesktopName("kwalletmanager_show");
} else {
r.call(QDBus::NoBlock, "show");
r.call(QDBus::NoBlock, "raise");
}
#endif // KHTML_NO_WALLET
}
void KHTMLPart::walletMenu()
{
#ifndef KHTML_NO_WALLET
KMenu *menu = new KMenu(0L);
QActionGroup *menuActionGroup = new QActionGroup(menu);
connect( menuActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(removeStoredPasswordForm(QAction*)) );
menu->addAction(i18n("&Close Wallet"), this, SLOT(slotWalletClosed()));
if (d->m_view && d->m_view->nonPasswordStorableSite(toplevelURL().host())) {
menu->addAction(i18n("&Allow storing passwords for this site"), this, SLOT(delNonPasswordStorableSite()));
}
// List currently removable form passwords
for ( QStringList::ConstIterator it = d->m_walletForms.constBegin(); it != d->m_walletForms.constEnd(); ++it ) {
QAction* action = menu->addAction( i18n("Remove password for form %1", *it) );
action->setActionGroup(menuActionGroup);
QVariant var(*it);
action->setData(var);
}
KAcceleratorManager::manage(menu);
menu->popup(QCursor::pos());
#endif // KHTML_NO_WALLET
}
void KHTMLPart::removeStoredPasswordForm(QAction* action)
{
#ifndef KHTML_NO_WALLET
assert(action);
assert(d->m_wallet);
QVariant var(action->data());
if(var.isNull() || !var.isValid() || var.type() != QVariant::String)
return;
QString key = var.toString();
if (KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
KWallet::Wallet::FormDataFolder(),
key))
return; // failed
if (!d->m_wallet->hasFolder(KWallet::Wallet::FormDataFolder()))
return; // failed
d->m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
if (d->m_wallet->removeEntry(key))
return; // failed
d->m_walletForms.removeAll(key);
#endif // KHTML_NO_WALLET
}
void KHTMLPart::addWalletFormKey(const QString& walletFormKey)
{
#ifndef KHTML_NO_WALLET
if (parentPart()) {
parentPart()->addWalletFormKey(walletFormKey);
return;
}
if(!d->m_walletForms.contains(walletFormKey))
d->m_walletForms.append(walletFormKey);
#endif // KHTML_NO_WALLET
}
void KHTMLPart::delNonPasswordStorableSite()
{
#ifndef KHTML_NO_WALLET
if (d->m_view)
d->m_view->delNonPasswordStorableSite(toplevelURL().host());
#endif // KHTML_NO_WALLET
}
void KHTMLPart::saveLoginInformation(const QString& host, const QString& key, const QMap<QString, QString>& walletMap)
{
#ifndef KHTML_NO_WALLET
d->m_storePass.saveLoginInformation(host, key, walletMap);
#endif // KHTML_NO_WALLET
}
void KHTMLPart::slotToggleCaretMode()
{
setCaretMode(d->m_paToggleCaretMode->isChecked());
}
void KHTMLPart::setFormNotification(KHTMLPart::FormNotification fn) {
d->m_formNotification = fn;
}
KHTMLPart::FormNotification KHTMLPart::formNotification() const {
return d->m_formNotification;
}
KUrl KHTMLPart::toplevelURL()
{
KHTMLPart* part = this;
while (part->parentPart())
part = part->parentPart();
if (!part)
return KUrl();
return part->url();
}
bool KHTMLPart::isModified() const
{
if ( !d->m_doc )
return false;
return d->m_doc->unsubmittedFormChanges();
}
void KHTMLPart::setDebugScript( bool enable )
{
unplugActionList( "debugScriptList" );
if ( enable ) {
if (!d->m_paDebugScript) {
d->m_paDebugScript = new KAction( i18n( "JavaScript &Debugger" ), this );
actionCollection()->addAction( "debugScript", d->m_paDebugScript );
connect( d->m_paDebugScript, SIGNAL(triggered(bool)), this, SLOT(slotDebugScript()) );
}
d->m_paDebugScript->setEnabled( d->m_frame ? d->m_frame->m_jscript : 0L );
QList<QAction*> lst;
lst.append( d->m_paDebugScript );
plugActionList( "debugScriptList", lst );
}
d->m_bJScriptDebugEnabled = enable;
}
void KHTMLPart::setSuppressedPopupIndicator( bool enable, KHTMLPart *originPart )
{
if ( parentPart() ) {
parentPart()->setSuppressedPopupIndicator( enable, originPart );
return;
}
if ( enable && originPart ) {
d->m_openableSuppressedPopups++;
if ( d->m_suppressedPopupOriginParts.indexOf( originPart ) == -1 )
d->m_suppressedPopupOriginParts.append( originPart );
}
if ( enable && !d->m_statusBarPopupLabel ) {
d->m_statusBarPopupLabel = new KUrlLabel( d->m_statusBarExtension->statusBar() );
d->m_statusBarPopupLabel->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ));
d->m_statusBarPopupLabel->setUseCursor( false );
d->m_statusBarExtension->addStatusBarItem( d->m_statusBarPopupLabel, 0, false );
d->m_statusBarPopupLabel->setPixmap( SmallIcon( "window-suppressed") );
d->m_statusBarPopupLabel->setToolTip(i18n("This page was prevented from opening a new window via JavaScript." ) );
connect(d->m_statusBarPopupLabel, SIGNAL(leftClickedUrl()), SLOT(suppressedPopupMenu()));
if (d->m_settings->jsPopupBlockerPassivePopup()) {
QPixmap px;
px = MainBarIcon( "window-suppressed" );
KPassivePopup::message(i18n("Popup Window Blocked"),i18n("This page has attempted to open a popup window but was blocked.\nYou can click on this icon in the status bar to control this behavior\nor to open the popup."),px,d->m_statusBarPopupLabel);
}
} else if ( !enable && d->m_statusBarPopupLabel ) {
d->m_statusBarPopupLabel->setToolTip("" );
d->m_statusBarExtension->removeStatusBarItem( d->m_statusBarPopupLabel );
delete d->m_statusBarPopupLabel;
d->m_statusBarPopupLabel = 0L;
}
}
void KHTMLPart::suppressedPopupMenu() {
KMenu *m = new KMenu(0L);
if ( d->m_openableSuppressedPopups )
m->addAction(i18np("&Show Blocked Popup Window","&Show %1 Blocked Popup Windows", d->m_openableSuppressedPopups), this, SLOT(showSuppressedPopups()));
QAction *a = m->addAction(i18n("Show Blocked Window Passive Popup &Notification"), this, SLOT(togglePopupPassivePopup()));
a->setChecked(d->m_settings->jsPopupBlockerPassivePopup());
m->addAction(i18n("&Configure JavaScript New Window Policies..."), this, SLOT(launchJSConfigDialog()));
m->popup(QCursor::pos());
}
void KHTMLPart::togglePopupPassivePopup() {
// Same hack as in disableJSErrorExtension()
d->m_settings->setJSPopupBlockerPassivePopup( !d->m_settings->jsPopupBlockerPassivePopup() );
emit configurationChanged();
}
void KHTMLPart::showSuppressedPopups() {
foreach ( KHTMLPart* part, d->m_suppressedPopupOriginParts ) {
if (part) {
KJS::Window *w = KJS::Window::retrieveWindow( part );
if (w) {
w->showSuppressedWindows();
w->forgetSuppressedWindows();
}
}
}
setSuppressedPopupIndicator( false );
d->m_openableSuppressedPopups = 0;
d->m_suppressedPopupOriginParts.clear();
}
// Extension to use for "view document source", "save as" etc.
// Using the right extension can help the viewer get into the right mode (#40496)
QString KHTMLPart::defaultExtension() const
{
if ( !d->m_doc )
return ".html";
if ( !d->m_doc->isHTMLDocument() )
return ".xml";
return d->m_doc->htmlMode() == DOM::DocumentImpl::XHtml ? ".xhtml" : ".html";
}
bool KHTMLPart::inProgress() const
{
if (!d->m_bComplete || d->m_runningScripts || (d->m_doc && d->m_doc->parsing()))
return true;
// Any frame that hasn't completed yet ?
ConstFrameIt it = d->m_frames.constBegin();
const ConstFrameIt end = d->m_frames.constEnd();
for (; it != end; ++it ) {
if ((*it)->m_run || !(*it)->m_bCompleted)
return true;
}
return d->m_submitForm || !d->m_redirectURL.isEmpty() || d->m_redirectionTimer.isActive() || d->m_job;
}
using namespace KParts;
#include "moc_khtmlpart_p.cpp"
#ifndef KHTML_NO_WALLET
#include "moc_khtml_wallet_p.cpp"
#endif
// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;
diff --git a/khtml/misc/loader.cpp b/khtml/misc/loader.cpp
index 972abb1357..a7c6b9dcfd 100644
--- a/khtml/misc/loader.cpp
+++ b/khtml/misc/loader.cpp
@@ -1,1708 +1,1708 @@
/*
This file is part of the KDE libraries
Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
(C) 2001-2003 Dirk Mueller (mueller@kde.org)
(C) 2002 Waldo Bastian (bastian@kde.org)
(C) 2003 Apple Computer, Inc.
(C) 2006-2010 Germain Garand (germain@ebooksfrance.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.
This class provides all functionality needed for loading images, style sheets and html
pages from the web. It has a memory cache for these objects.
// regarding the LRU:
// http://www.is.kyusan-u.ac.jp/~chengk/pub/papers/compsac00_A07-07.pdf
*/
#undef CACHE_DEBUG
//#define CACHE_DEBUG
#ifdef CACHE_DEBUG
#define CDEBUG kDebug(6060)
#else
#define CDEBUG kDebugDevNull()
#endif
#undef LOADER_DEBUG
//#define LOADER_DEBUG
//#define PRELOAD_DEBUG
#include "loader.h"
#include "seed.h"
#include "woff.h"
#include <imload/image.h>
#include <imload/imagepainter.h>
#include <kfilterdev.h>
#include <assert.h>
// default cache size
#define DEFCACHESIZE 2096*1024
//#include <qasyncio.h>
//#include <qasyncimageio.h>
#include <QApplication>
#include <QDesktopWidget>
#include <QPainter>
#include <QBitmap>
#include <QMovie>
#include <QWidget>
#include <QtCore/QDebug>
#include <kauthorized.h>
#include <kio/job.h>
#include <kio/jobuidelegate.h>
#include <kio/jobclasses.h>
#include <kcharsets.h>
#include <kiconloader.h>
#include <scheduler.h>
#include <kdebug.h>
#include <khtml_global.h>
#include <khtml_part.h>
#ifdef IMAGE_TITLES
#include <qfile.h>
#include <kfilemetainfo.h>
#include <qtemporaryfile.h>
#endif
#include "html/html_documentimpl.h"
#include "css/css_stylesheetimpl.h"
#include "xml/dom_docimpl.h"
#include "blocked_icon.cpp"
#include <QPaintEngine>
using namespace khtml;
using namespace DOM;
using namespace khtmlImLoad;
#define MAX_LRU_LISTS 20
struct LRUList {
CachedObject* m_head;
CachedObject* m_tail;
LRUList() : m_head(0), m_tail(0) {}
};
static LRUList m_LRULists[MAX_LRU_LISTS];
static LRUList* getLRUListFor(CachedObject* o);
CachedObjectClient::~CachedObjectClient()
{
}
CachedObject::~CachedObject()
{
Cache::removeFromLRUList(this);
}
void CachedObject::finish()
{
m_status = Cached;
}
bool CachedObject::isExpired() const
{
if (!m_expireDate) return false;
time_t now = time(0);
return (difftime(now, m_expireDate) >= 0);
}
void CachedObject::setRequest(Request *_request)
{
if ( _request && !m_request )
m_status = Pending;
if ( allowInLRUList() )
Cache::removeFromLRUList( this );
m_request = _request;
if ( allowInLRUList() )
Cache::insertInLRUList( this );
}
void CachedObject::ref(CachedObjectClient *c)
{
if (m_preloadResult == PreloadNotReferenced) {
if (isLoaded())
m_preloadResult = PreloadReferencedWhileComplete;
else if (m_prospectiveRequest)
m_preloadResult = PreloadReferencedWhileLoading;
else
m_preloadResult = PreloadReferenced;
}
// unfortunately we can be ref'ed multiple times from the
// same object, because it uses e.g. the same foreground
// and the same background picture. so deal with it.
// Hence the use of a QHash rather than of a QSet.
m_clients.insertMulti(c,c);
Cache::removeFromLRUList(this);
m_accessCount++;
}
void CachedObject::deref(CachedObjectClient *c)
{
assert( c );
assert( m_clients.count() );
assert( !canDelete() );
assert( m_clients.contains( c ) );
Cache::flush();
m_clients.take(c);
if (allowInLRUList())
Cache::insertInLRUList(this);
}
void CachedObject::setSize(int size)
{
bool sizeChanged;
if ( !m_next && !m_prev && getLRUListFor(this)->m_head != this )
sizeChanged = false;
else
sizeChanged = ( size - m_size ) != 0;
// The object must now be moved to a different queue,
// since its size has been changed.
if ( sizeChanged && allowInLRUList())
Cache::removeFromLRUList(this);
m_size = size;
if ( sizeChanged && allowInLRUList())
Cache::insertInLRUList(this);
}
QTextCodec* CachedObject::codecForBuffer( const QString& charset, const QByteArray& buffer ) const
{
// we don't use heuristicContentMatch here since it is a) far too slow and
// b) having too much functionality for our case.
uchar* d = ( uchar* ) buffer.data();
int s = buffer.size();
// BOM
if ( s >= 3 &&
d[0] == 0xef && d[1] == 0xbb && d[2] == 0xbf)
return QTextCodec::codecForMib( 106 ); // UTF-8
if ( s >= 2 && ((d[0] == 0xff && d[1] == 0xfe) ||
(d[0] == 0xfe && d[1] == 0xff)))
return QTextCodec::codecForMib( 1000 ); // UCS-2
// Link or @charset
if(!charset.isEmpty())
{
QTextCodec* c = KCharsets::charsets()->codecForName(charset);
if(c->mibEnum() == 11) {
// iso8859-8 (visually ordered)
c = QTextCodec::codecForName("iso8859-8-i");
}
return c;
}
// Default
return QTextCodec::codecForMib( 4 ); // latin 1
}
// -------------------------------------------------------------------------------------------
CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy,
const char *accept)
: CachedObject(url, CSSStyleSheet, _cachePolicy, 0)
{
// Set the type we want (probably css or xml)
QString ah = QLatin1String( accept );
if ( !ah.isEmpty() )
ah += ',';
ah += "*/*;q=0.1";
setAccept( ah );
m_hadError = false;
m_wasBlocked = false;
m_err = 0;
// load the file.
// Style sheets block rendering, they are therefore the higher priority item.
// Do |not| touch the priority value unless you conducted thorough tests and
// can back your choice with meaningful data, testing page load time and
// time to first paint.
Cache::loader()->load(dl, this, false, -8);
m_loading = true;
}
CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString &url, const QString &stylesheet_data)
: CachedObject(url, CSSStyleSheet, KIO::CC_Verify, stylesheet_data.length())
{
m_loading = false;
m_status = Persistent;
m_sheet = DOMString(stylesheet_data);
}
bool khtml::isAcceptableCSSMimetype( const DOM::DOMString& mimetype )
{
// matches Mozilla's check (cf. nsCSSLoader.cpp)
return mimetype.isEmpty() || mimetype == "text/css" || mimetype == "application/x-unknown-content-type";
}
void CachedCSSStyleSheet::ref(CachedObjectClient *c)
{
CachedObject::ref(c);
if (!m_loading) {
if (m_hadError)
c->error( m_err, m_errText );
else
c->setStyleSheet( m_url, m_sheet, m_charset, m_mimetype );
}
}
void CachedCSSStyleSheet::data( QBuffer &buffer, bool eof )
{
if(!eof) return;
buffer.close();
setSize(buffer.buffer().size());
m_charset = checkCharset( buffer.buffer() );
QTextCodec* c = 0;
if (!m_charset.isEmpty()) {
c = KCharsets::charsets()->codecForName(m_charset);
if(c->mibEnum() == 11) c = QTextCodec::codecForName("iso8859-8-i");
}
else {
c = codecForBuffer( m_charsetHint, buffer.buffer() );
m_charset = c->name();
}
QString data = c->toUnicode( buffer.buffer().data(), m_size );
// workaround Qt bugs
m_sheet = static_cast<QChar>(data[0]) == QChar::ByteOrderMark ? DOMString(data.mid( 1 ) ) : DOMString(data);
m_loading = false;
checkNotify();
}
void CachedCSSStyleSheet::checkNotify()
{
if(m_loading || m_hadError) return;
CDEBUG << "CachedCSSStyleSheet:: finishedLoading " << m_url.string() << endl;
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
it.next().value()->setStyleSheet( m_url, m_sheet, m_charset, m_mimetype );
}
void CachedCSSStyleSheet::error( int err, const char* text )
{
m_hadError = true;
m_err = err;
m_errText = text;
m_loading = false;
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
it.next().value()->error( m_err, m_errText );
}
QString CachedCSSStyleSheet::checkCharset(const QByteArray& buffer ) const
{
int s = buffer.size();
if (s <= 12) return m_charset;
// @charset has to be first or directly after BOM.
// CSS 2.1 says @charset should win over BOM, but since more browsers support BOM
// than @charset, we default to that.
const char* d = buffer.data();
if (strncmp(d, "@charset \"",10) == 0)
{
// the string until "; is the charset name
const char *p = strchr(d+10, '"');
if (p == 0) return m_charset;
- QString charset = QString::fromAscii(d+10, p-(d+10));
+ QString charset = QString::fromLatin1(d+10, p-(d+10));
return charset;
}
return m_charset;
}
// -------------------------------------------------------------------------------------------
CachedScript::CachedScript(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, const char*)
: CachedObject(url, Script, _cachePolicy, 0)
{
// It's javascript we want.
// But some websites think their scripts are <some wrong mimetype here>
// and refuse to serve them if we only accept application/x-javascript.
setAccept( QLatin1String("*/*") );
// load the file.
// Scripts block document parsing. They are therefore second in our list of most
// desired resources.
Cache::loader()->load(dl, this, false/*incremental*/, -6);
m_loading = true;
m_hadError = false;
}
CachedScript::CachedScript(const DOMString &url, const QString &script_data)
: CachedObject(url, Script, KIO::CC_Verify, script_data.length())
{
m_hadError = false;
m_loading = false;
m_status = Persistent;
m_script = DOMString(script_data);
}
void CachedScript::ref(CachedObjectClient *c)
{
CachedObject::ref(c);
if(!m_loading) c->notifyFinished(this);
}
void CachedScript::data( QBuffer &buffer, bool eof )
{
if(!eof) return;
buffer.close();
setSize(buffer.buffer().size());
QTextCodec* c = codecForBuffer( m_charset, buffer.buffer() );
QString data = c->toUnicode( buffer.buffer().data(), m_size );
m_script = static_cast<QChar>(data[0]) == QChar::ByteOrderMark ? DOMString(data.mid( 1 ) ) : DOMString(data);
m_loading = false;
checkNotify();
}
void CachedScript::checkNotify()
{
if(m_loading) return;
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
it.next().value()->notifyFinished(this);
}
void CachedScript::error( int /*err*/, const char* /*text*/ )
{
m_hadError = true;
m_loading = false;
checkNotify();
}
// ------------------------------------------------------------------------------------------
static QString buildAcceptHeader()
{
return "image/png, image/jpeg, video/x-mng, image/jp2, image/gif;q=0.5,*/*;q=0.1";
}
// -------------------------------------------------------------------------------------
CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, const char*)
: QObject(), CachedObject(url, Image, _cachePolicy, 0)
{
i = new khtmlImLoad::Image(this);
//p = 0;
//pixPart = 0;
bg = 0;
scaled = 0;
bgColor = qRgba( 0, 0, 0, 0 );
m_status = Unknown;
setAccept( buildAcceptHeader() );
i->setShowAnimations(dl->showAnimations());
m_loading = true;
if ( KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( url.string() ) ) {
m_wasBlocked = true;
CachedObject::finish();
}
}
CachedImage::~CachedImage()
{
clear();
delete i;
}
void CachedImage::ref( CachedObjectClient *c )
{
CachedObject::ref(c);
#ifdef LOADER_DEBUG
kDebug(6060) << " image "<<this<<" ref'd by client " << c << "\n";
#endif
// for mouseovers, dynamic changes
//### having both makes no sense
if ( m_status >= Persistent && !pixmap_size().isNull() ) {
#ifdef LOADER_DEBUG
kDebug(6060) << "Notifying finished size:" <<
i->size().width() << ", " << i->size().height() << endl;
#endif
c->updatePixmap( QRect(QPoint(0, 0), pixmap_size()),
this );
c->notifyFinished( this );
}
}
void CachedImage::deref( CachedObjectClient *c )
{
CachedObject::deref(c);
/* if(m && m_clients.isEmpty() && m->running())
m->pause();*/
}
#define BGMINWIDTH 32
#define BGMINHEIGHT 32
QPixmap CachedImage::tiled_pixmap(const QColor& newc, int xWidth, int xHeight)
{
// no error indication for background images
if(m_hadError||m_wasBlocked) return *Cache::nullPixmap;
// If we don't have size yet, nothing to draw yet
if (i->size().width() == 0 || i->size().height() == 0)
return *Cache::nullPixmap;
#ifdef __GNUC__
#warning "Needs some additional performance work"
#endif
static QRgb bgTransparent = qRgba( 0, 0, 0, 0 );
QSize s(pixmap_size());
int w = xWidth;
int h = xHeight;
if (w == -1) xWidth = w = s.width();
if (h == -1) xHeight = h = s.height();
if ( ( (bgColor != bgTransparent) && (bgColor != newc.rgba()) ) ||
( bgSize != QSize(xWidth, xHeight)) )
{
delete bg; bg = 0;
}
if (bg)
return *bg;
const QPixmap* src; //source for pretiling, if any
const QPixmap &r = pixmap(); //this is expensive
if (r.isNull()) return r;
//See whether we should scale
if (xWidth != s.width() || xHeight != s.height()) {
src = scaled_pixmap(xWidth, xHeight);
} else {
src = &r;
}
bgSize = QSize(xWidth, xHeight);
//See whether we can - and should - pre-blend
// ### this needs serious investigations. Not likely to help with transparent bgColor,
// won't work with CSS3 multiple backgrounds. Does it help at all in Qt4? (ref: #114938)
if (newc.isValid() && (r.hasAlpha() || r.hasAlphaChannel())) {
bg = new QPixmap(xWidth, xHeight);
bg->fill(newc);
QPainter p(bg);
p.drawPixmap(0, 0, *src);
bgColor = newc.rgba();
src = bg;
} else {
bgColor = bgTransparent;
}
//See whether to pre-tile.
if ( w*h < 8192 )
{
if ( r.width() < BGMINWIDTH )
w = ((BGMINWIDTH-1) / xWidth + 1) * xWidth;
if ( r.height() < BGMINHEIGHT )
h = ((BGMINHEIGHT-1) / xHeight + 1) * xHeight;
}
if ( w != xWidth || h != xHeight )
{
// kDebug() << "pre-tiling " << s.width() << "," << s.height() << " to " << w << "," << h;
QPixmap* oldbg = bg;
bg = new QPixmap(w, h);
if (src->hasAlpha() || src->hasAlphaChannel()) {
if (newc.isValid() && (bgColor != bgTransparent))
bg->fill( bgColor );
else
bg->fill( Qt::transparent );
}
QPainter p(bg);
p.drawTiledPixmap(0, 0, w, h, *src);
p.end();
if ( src == oldbg )
delete oldbg;
} else if (src && !bg) {
// we were asked for the entire pixmap. Cache it.
// ### goes against imload stuff, but it's far too expensive
// to recreate the full pixmap each time it's requested as
// we don't know what portion of it will be used eventually
// (by e.g. paintBackgroundExtended). It could be a few pixels of
// a huge image. See #140248/#1 for an obvious example.
// Imload probably needs to handle all painting in paintBackgroundExtended.
bg = new QPixmap(*src);
}
if (bg)
return *bg;
return *src;
}
QPixmap* CachedImage::scaled_pixmap( int xWidth, int xHeight )
{
// no error indication for background images
if(m_hadError||m_wasBlocked) return Cache::nullPixmap;
// If we don't have size yet, nothing to draw yet
if (i->size().width() == 0 || i->size().height() == 0)
return Cache::nullPixmap;
if (scaled) {
if (scaled->width() == xWidth && scaled->height() == xHeight)
return scaled;
delete scaled;
}
//### this is quite awful performance-wise. It should avoid
// alpha if not needed, and go to pixmap, etc.
QImage im(xWidth, xHeight, QImage::Format_ARGB32_Premultiplied);
QPainter paint(&im);
paint.setCompositionMode(QPainter::CompositionMode_Source);
ImagePainter pi(i, QSize(xWidth, xHeight));
pi.paint(0, 0, &paint);
paint.end();
scaled = new QPixmap(QPixmap::fromImage(im));
return scaled;
}
QPixmap CachedImage::pixmap( ) const
{
if (m_hadError)
return *Cache::brokenPixmap;
if(m_wasBlocked)
return *Cache::blockedPixmap;
int w = i->size().width();
int h = i->size().height();
if (i->hasAlpha() && QApplication::desktop()->paintEngine() &&
!QApplication::desktop()->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) {
QImage im(w, h, QImage::Format_ARGB32_Premultiplied);
QPainter paint(&im);
paint.setCompositionMode(QPainter::CompositionMode_Source);
ImagePainter pi(i);
pi.paint(0, 0, &paint);
paint.end();
return QPixmap::fromImage( im, Qt::NoOpaqueDetection );
} else {
QPixmap pm(w, h);
if (i->hasAlpha())
pm.fill(Qt::transparent);
QPainter paint(&pm);
paint.setCompositionMode(QPainter::CompositionMode_Source);
ImagePainter pi(i);
pi.paint(0, 0, &paint);
paint.end();
return pm;
}
}
QSize CachedImage::pixmap_size() const
{
if (m_wasBlocked) return Cache::blockedPixmap->size();
if (m_hadError) return Cache::brokenPixmap->size();
if (i) return i->size();
return QSize();
}
void CachedImage::imageHasGeometry(khtmlImLoad::Image* /*img*/, int width, int height)
{
#ifdef LOADER_DEBUG
kDebug(6060) << this << " got geometry "<< width << "x" << height;
#endif
do_notify(QRect(0, 0, width, height));
}
void CachedImage::imageChange (khtmlImLoad::Image* /*img*/, QRect region)
{
#ifdef LOADER_DEBUG
kDebug(6060) << "Image " << this << " change " <<
region.x() << "," << region.y() << ":" << region.width() << "x" << region.height() << endl;
#endif
//### this is overly conservative -- I guess we need to also specify reason,
//e.g. repaint vs. changed !!!
delete bg;
bg = 0;
do_notify(region);
}
void CachedImage::doNotifyFinished()
{
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
{
it.next().value()->notifyFinished(this);
}
}
void CachedImage::imageError(khtmlImLoad::Image* /*img*/)
{
error(0, 0);
}
void CachedImage::imageDone(khtmlImLoad::Image* /*img*/)
{
#ifdef LOADER_DEBUG
kDebug(6060)<<"Image is done:" << this;
#endif
m_status = Persistent;
m_loading = false;
doNotifyFinished();
}
// QRect CachedImage::valid_rect() const
// {
// if (m_wasBlocked) return Cache::blockedPixmap->rect();
// return (m_hadError ? Cache::brokenPixmap->rect() : m ? m->frameRect() : ( p ? p->rect() : QRect()) );
// }
void CachedImage::do_notify(const QRect& r)
{
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
{
#ifdef LOADER_DEBUG
kDebug(6060) << " image "<<this<<" notify of geom client " << it.peekNext() << "\n";
#endif
it.next().value()->updatePixmap( r, this);
}
}
void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
{
if (i)
i->setShowAnimations(showAnimations);
}
void CachedImage::clear()
{
delete i; i = new khtmlImLoad::Image(this);
delete scaled; scaled = 0;
bgColor = qRgba( 0, 0, 0, 0xff );
delete bg; bg = 0;
bgSize = QSize(-1,-1);
setSize(0);
}
void CachedImage::data ( QBuffer &_buffer, bool eof )
{
#ifdef LOADER_DEBUG
kDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << " pos:" << _buffer.pos();
#endif
i->processData((uchar*)_buffer.data().data(), _buffer.pos());
_buffer.close();
if (eof)
i->processEOF();
}
void CachedImage::finish()
{
CachedObject::finish();
m_loading = false;
QSize s = pixmap_size();
setSize( s.width() * s.height() * 2);
}
void CachedImage::error( int /*err*/, const char* /*text*/ )
{
clear();
m_hadError = true;
m_loading = false;
do_notify(QRect(0, 0, 16, 16));
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
it.next().value()->notifyFinished(this);
}
// -------------------------------------------------------------------------------------------
CachedSound::CachedSound(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, const char*)
: CachedObject(url, Sound, _cachePolicy, 0)
{
setAccept( QLatin1String("*/*") ); // should be whatever phonon would accept...
Cache::loader()->load(dl, this, false/*incremental*/, 2);
m_loading = true;
}
void CachedSound::ref(CachedObjectClient *c)
{
CachedObject::ref(c);
if(!m_loading) c->notifyFinished(this);
}
void CachedSound::data( QBuffer &buffer, bool eof )
{
if(!eof) return;
buffer.close();
setSize(buffer.buffer().size());
m_sound = buffer.buffer();
m_loading = false;
checkNotify();
}
void CachedSound::checkNotify()
{
if(m_loading) return;
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
it.next().value()->notifyFinished(this);
}
void CachedSound::error( int /*err*/, const char* /*text*/ )
{
m_loading = false;
checkNotify();
}
// -------------------------------------------------------------------------------------------
CachedFont::CachedFont(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, const char*)
: CachedObject(url, Font, _cachePolicy, 0)
{
setAccept( QLatin1String("*/*") );
// Fonts are desired early because their absence will lead to a page being rendered
// with a default replacement, then the text being re-rendered with the new font when it arrives.
// This can be fairly disturbing for the reader - more than missing images for instance.
Cache::loader()->load(dl, this, false /*incremental*/, -4);
m_loading = true;
}
void CachedFont::ref(CachedObjectClient *c)
{
CachedObject::ref(c);
if(!m_loading) c->notifyFinished(this);
}
void CachedFont::data( QBuffer &buffer, bool eof )
{
if(!eof) return;
buffer.close();
m_font = buffer.buffer();
// some fonts are compressed.
QIODevice* dev = KFilterDev::device(&buffer, mimetype(), false /*autoDeleteInDevice*/);
if (dev && dev->open( QIODevice::ReadOnly )) {
m_font = dev->readAll();
delete dev;
}
// handle decoding of WOFF fonts
int woffStatus = eWOFF_ok;
if (int need = WOFF::getDecodedSize( m_font.constData(), m_font.size(), &woffStatus)) {
kDebug(6040) << "***************************** Got WOFF FoNT";
m_hadError = true;
do {
if (WOFF_FAILURE(woffStatus))
break;
QByteArray wbuffer;
wbuffer.resize( need );
int len;
woffStatus = eWOFF_ok;
WOFF::decodeToBuffer(m_font.constData(), m_font.size(), wbuffer.data(), wbuffer.size(), &len, &woffStatus);
if (WOFF_FAILURE(woffStatus))
break;
wbuffer.resize(len);
m_font = wbuffer;
m_hadError = false;
} while (false);
} else if (m_font.isEmpty()) {
m_hadError = true;
}
else kDebug(6040) << "******** #################### ********************* NON WOFF font";
setSize(m_font.size());
m_loading = false;
checkNotify();
}
void CachedFont::checkNotify()
{
if(m_loading) return;
for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
it.next().value()->notifyFinished(this);
}
void CachedFont::error( int /*err*/, const char* /*text*/ )
{
m_loading = false;
m_hadError = true;
checkNotify();
}
// ------------------------------------------------------------------------------------------
Request::Request(DocLoader* dl, CachedObject *_object, bool _incremental, int _priority)
{
object = _object;
object->setRequest(this);
incremental = _incremental;
priority = _priority;
m_docLoader = dl;
}
Request::~Request()
{
object->setRequest(0);
}
// ------------------------------------------------------------------------------------------
DocLoader::DocLoader(KHTMLPart* part, DocumentImpl* doc)
{
m_cachePolicy = KIO::CC_Verify;
m_expireDate = 0;
m_creationDate = time(0);
m_bautoloadImages = true;
m_showAnimations = KHTMLSettings::KAnimationEnabled;
m_part = part;
m_doc = doc;
Cache::docloader->append( this );
}
DocLoader::~DocLoader()
{
clearPreloads();
Cache::loader()->cancelRequests( this );
Cache::docloader->removeAll( this );
}
void DocLoader::setCacheCreationDate(time_t _creationDate)
{
if (_creationDate)
m_creationDate = _creationDate;
else
m_creationDate = time(0); // Now
}
void DocLoader::setExpireDate(time_t _expireDate, bool relative)
{
if (relative)
m_expireDate = _expireDate + m_creationDate; // Relative date
else
m_expireDate = _expireDate; // Absolute date
#ifdef CACHE_DEBUG
kDebug(6061) << "docLoader: " << m_expireDate - time(0) << " seconds left until reload required.\n";
#endif
}
void DocLoader::insertCachedObject( CachedObject* o ) const
{
m_docObjects.insert( o );
}
bool DocLoader::needReload(CachedObject *existing, const QString& fullURL)
{
bool reload = false;
if (m_cachePolicy == KIO::CC_Verify)
{
if (!m_reloadedURLs.contains(fullURL))
{
if (existing && existing->isExpired() && !existing->isPreloaded())
{
Cache::removeCacheEntry(existing);
m_reloadedURLs.append(fullURL);
reload = true;
}
}
}
else if ((m_cachePolicy == KIO::CC_Reload) || (m_cachePolicy == KIO::CC_Refresh))
{
if (!m_reloadedURLs.contains(fullURL))
{
if (existing && !existing->isPreloaded())
{
Cache::removeCacheEntry(existing);
}
if (!existing || !existing->isPreloaded()) {
m_reloadedURLs.append(fullURL);
reload = true;
}
}
}
return reload;
}
void DocLoader::registerPreload(CachedObject* resource)
{
if (!resource || resource->isLoaded() || m_preloads.contains(resource))
return;
resource->increasePreloadCount();
m_preloads.insert(resource);
resource->setProspectiveRequest();
#ifdef PRELOAD_DEBUG
fprintf(stderr, "PRELOADING %s\n", resource->url().string().toLatin1().data());
#endif
}
void DocLoader::clearPreloads()
{
printPreloadStats();
QSet<CachedObject*>::iterator end = m_preloads.end();
for (QSet<CachedObject*>::iterator it = m_preloads.begin(); it != end; ++it) {
CachedObject* res = *it;
res->decreasePreloadCount();
if (res->preloadResult() == CachedObject::PreloadNotReferenced || res->hadError())
Cache::removeCacheEntry(res);
}
m_preloads.clear();
}
void DocLoader::printPreloadStats()
{
#ifdef PRELOAD_DEBUG
unsigned scripts = 0;
unsigned scriptMisses = 0;
unsigned stylesheets = 0;
unsigned stylesheetMisses = 0;
unsigned images = 0;
unsigned imageMisses = 0;
QSet<CachedObject*>::iterator end = m_preloads.end();
for (QSet<CachedObject*>::iterator it = m_preloads.begin(); it != end; ++it) {
CachedObject* res = *it;
if (res->preloadResult() == CachedObject::PreloadNotReferenced)
fprintf(stderr,"!! UNREFERENCED PRELOAD %s\n", res->url().string().toLatin1().data());
else if (res->preloadResult() == CachedObject::PreloadReferencedWhileComplete)
fprintf(stderr,"HIT COMPLETE PRELOAD %s\n", res->url().string().toLatin1().data());
else if (res->preloadResult() == CachedObject::PreloadReferencedWhileLoading)
fprintf(stderr,"HIT LOADING PRELOAD %s\n", res->url().string().toLatin1().data());
if (res->type() == CachedObject::Script) {
scripts++;
if (res->preloadResult() < CachedObject::PreloadReferencedWhileLoading)
scriptMisses++;
} else if (res->type() == CachedObject::CSSStyleSheet) {
stylesheets++;
if (res->preloadResult() < CachedObject::PreloadReferencedWhileLoading)
stylesheetMisses++;
} else {
images++;
if (res->preloadResult() < CachedObject::PreloadReferencedWhileLoading)
imageMisses++;
}
}
if (scripts)
fprintf(stderr, "SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
if (stylesheets)
fprintf(stderr, "STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
if (images)
fprintf(stderr, "IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
#endif
}
static inline bool securityCheckUrl(const KUrl& fullURL, KHTMLPart* part, DOM::DocumentImpl* doc,
bool doRedirectCheck, bool isImg)
{
if (!fullURL.isValid())
return false;
if (part && part->onlyLocalReferences() && fullURL.scheme() != "file" && fullURL.scheme() != "data")
return false;
if (doRedirectCheck && doc) {
if (isImg && part && part->forcePermitLocalImages() && fullURL.scheme() == "file")
return true;
else
return KAuthorized::authorizeUrlAction("redirect", doc->URL(), fullURL);
}
return true;
}
#define DOCLOADER_SECCHECK_IMP(doRedirectCheck,isImg) \
KUrl fullURL(m_doc->completeURL(url.string())); \
if (!securityCheckUrl(fullURL, m_part, m_doc, doRedirectCheck, isImg)) \
return 0L;
#define DOCLOADER_SECCHECK(doRedirectCheck) DOCLOADER_SECCHECK_IMP(doRedirectCheck, false)
#define DOCLOADER_SECCHECK_IMG(doRedirectCheck) DOCLOADER_SECCHECK_IMP(doRedirectCheck, true)
bool DocLoader::willLoadMediaElement( const DOM::DOMString &url)
{
DOCLOADER_SECCHECK(true);
return true;
}
CachedImage *DocLoader::requestImage( const DOM::DOMString &url)
{
DOCLOADER_SECCHECK_IMG(true);
CachedImage* i = Cache::requestObject<CachedImage, CachedObject::Image>( this, fullURL, 0);
if (i && i->status() == CachedObject::Unknown && autoloadImages())
Cache::loader()->load(this, i, true /*incremental*/);
return i;
}
CachedCSSStyleSheet *DocLoader::requestStyleSheet( const DOM::DOMString &url, const QString& charset,
const char *accept, bool userSheet )
{
DOCLOADER_SECCHECK(!userSheet);
CachedCSSStyleSheet* s = Cache::requestObject<CachedCSSStyleSheet, CachedObject::CSSStyleSheet>( this, fullURL, accept );
if ( s && !charset.isEmpty() ) {
s->setCharsetHint( charset );
}
return s;
}
CachedScript *DocLoader::requestScript( const DOM::DOMString &url, const QString& charset)
{
DOCLOADER_SECCHECK(true);
if ( ! KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(fullURL.host()) ||
KHTMLGlobal::defaultHTMLSettings()->isAdFiltered(fullURL.url()))
return 0L;
CachedScript* s = Cache::requestObject<CachedScript, CachedObject::Script>( this, fullURL, 0 );
if ( s && !charset.isEmpty() )
s->setCharset( charset );
return s;
}
CachedSound *DocLoader::requestSound( const DOM::DOMString &url )
{
DOCLOADER_SECCHECK(true);
CachedSound* s = Cache::requestObject<CachedSound, CachedObject::Sound>( this, fullURL, 0 );
return s;
}
CachedFont *DocLoader::requestFont( const DOM::DOMString &url )
{
DOCLOADER_SECCHECK(true);
CachedFont* s = Cache::requestObject<CachedFont, CachedObject::Font>( this, fullURL, 0 );
return s;
}
#undef DOCLOADER_SECCHECK
void DocLoader::setAutoloadImages( bool enable )
{
if ( enable == m_bautoloadImages )
return;
m_bautoloadImages = enable;
if ( !m_bautoloadImages ) return;
for ( QSetIterator<CachedObject*> it( m_docObjects ); it.hasNext(); )
{
CachedObject* cur = it.next();
if ( cur->type() == CachedObject::Image )
{
CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>(cur) );
CachedObject::Status status = img->status();
if ( status != CachedObject::Unknown )
continue;
Cache::loader()->load(this, img, true /*incremental*/);
}
}
}
void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
{
if ( showAnimations == m_showAnimations ) return;
m_showAnimations = showAnimations;
for ( QSetIterator<CachedObject*> it( m_docObjects ); it.hasNext(); )
{
CachedObject* cur = it.next();
if ( cur->type() == CachedObject::Image )
{
CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( cur ) );
img->setShowAnimations( m_showAnimations );
}
}
}
// ------------------------------------------------------------------------------------------
Loader::Loader() : QObject()
{
}
Loader::~Loader()
{
qDeleteAll(m_requestsLoading);
}
void Loader::load(DocLoader* dl, CachedObject *object, bool incremental, int priority)
{
Request *req = new Request(dl, object, incremental, priority);
scheduleRequest(req);
emit requestStarted( req->m_docLoader, req->object );
}
void Loader::scheduleRequest(Request* req)
{
#ifdef LOADER_DEBUG
kDebug( 6060 ) << "starting Loader url=" << req->object->url().string();
#endif
KUrl u(req->object->url().string());
KIO::TransferJob* job = KIO::get( u, KIO::NoReload, KIO::HideProgressInfo /*no GUI*/);
job->addMetaData("cache", KIO::getCacheControlString(req->object->cachePolicy()));
if (!req->object->accept().isEmpty())
job->addMetaData("accept", req->object->accept());
if ( req->m_docLoader )
{
job->addMetaData( "referrer", req->m_docLoader->doc()->URL().url() );
KHTMLPart *part = req->m_docLoader->part();
if (part )
{
job->addMetaData( "cross-domain", part->toplevelURL().url() );
if (part->widget())
job->ui()->setWindow (part->widget()->topLevelWidget());
}
}
connect( job, SIGNAL(result(KJob*)), this, SLOT(slotFinished(KJob*)) );
connect( job, SIGNAL(mimetype(KIO::Job*,QString)), this, SLOT(slotMimetype(KIO::Job*,QString)) );
connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
SLOT(slotData(KIO::Job*,QByteArray)));
KIO::Scheduler::setJobPriority( job, req->priority );
m_requestsLoading.insertMulti(job, req);
}
void Loader::slotMimetype( KIO::Job *j, const QString& s )
{
Request *r = m_requestsLoading.value( j );
if (!r)
return;
CachedObject *o = r->object;
// Mozilla plain ignores any mimetype that doesn't have / in it, and handles it as "",
// including when being picky about mimetypes. Match that for better compatibility with broken servers.
if (s.contains('/'))
o->m_mimetype = s;
else
o->m_mimetype = "";
}
void Loader::slotFinished( KJob* job )
{
KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
Request *r = m_requestsLoading.take( j );
if ( !r )
return;
if (j->error() || j->isErrorPage())
{
#ifdef LOADER_DEBUG
kDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage();
#endif
- r->object->error( job->error(), job->errorText().toAscii().constData() );
+ r->object->error( job->error(), job->errorText().toLatin1().constData() );
emit requestFailed( r->m_docLoader, r->object );
}
else
{
QString cs = j->queryMetaData("charset");
if (!cs.isEmpty()) r->object->setCharset(cs);
r->object->data(r->m_buffer, true);
emit requestDone( r->m_docLoader, r->object );
time_t expireDate = j->queryMetaData("expire-date").toLong();
#ifdef LOADER_DEBUG
kDebug(6060) << "Loader::slotFinished, url = " << j->url().url();
#endif
r->object->setExpireDate( expireDate );
if ( r->object->type() == CachedObject::Image ) {
QString fn = j->queryMetaData("content-disposition-filename");
static_cast<CachedImage*>( r->object )->setSuggestedFilename(fn);
#ifdef IMAGE_TITLES
static_cast<CachedImage*>( r->object )->setSuggestedTitle(fn);
QTemporaryFile tf;
tf.open();
tf.write((const char*)r->m_buffer.buffer().data(), r->m_buffer.size());
tf.flush();
KFileMetaInfo kfmi(tf.fileName());
if (!kfmi.isEmpty()) {
KFileMetaInfoItem i = kfmi.item("Name");
if (i.isValid()) {
static_cast<CachedImage*>(r->object)->setSuggestedTitle(i.string());
} else {
i = kfmi.item("Title");
if (i.isValid()) {
static_cast<CachedImage*>(r->object)->setSuggestedTitle(i.string());
}
}
}
#endif
}
}
r->object->finish();
#ifdef LOADER_DEBUG
kDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string();
#endif
delete r;
}
void Loader::slotData( KIO::Job*job, const QByteArray &data )
{
Request *r = m_requestsLoading[job];
if(!r) {
kDebug( 6060 ) << "got data for unknown request!";
return;
}
if ( !r->m_buffer.isOpen() )
r->m_buffer.open( QIODevice::WriteOnly );
r->m_buffer.write( data.data(), data.size() );
if(r->incremental)
r->object->data( r->m_buffer, false );
}
int Loader::numRequests( DocLoader* dl ) const
{
int res = 0;
foreach( Request* req, m_requestsLoading)
if ( req->m_docLoader == dl )
res++;
return res;
}
void Loader::cancelRequests( DocLoader* dl )
{
QMutableHashIterator<KIO::Job*,Request*> lIt( m_requestsLoading );
while ( lIt.hasNext() )
{
lIt.next();
if ( lIt.value()->m_docLoader == dl )
{
//kDebug( 6060 ) << "canceling loading request for " << lIt.current()->object->url().string();
KIO::Job *job = static_cast<KIO::Job *>( lIt.key() );
Cache::removeCacheEntry( lIt.value()->object );
delete lIt.value();
lIt.remove();
job->kill();
}
}
}
KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
{
QHashIterator<KIO::Job*,Request*> it( m_requestsLoading );
while (it.hasNext())
{
it.next();
if ( it.value()->object && it.value()->object->url() == url )
return static_cast<KIO::Job *>( it.key() );
}
return 0;
}
// ----------------------------------------------------------------------------
QHash<QString,CachedObject*> *Cache::cache;
QLinkedList<DocLoader*> *Cache::docloader;
QLinkedList<CachedObject*> *Cache::freeList;
Loader *Cache::m_loader;
int Cache::maxSize = DEFCACHESIZE;
int Cache::totalSizeOfLRU;
QPixmap *Cache::nullPixmap;
QPixmap *Cache::brokenPixmap;
QPixmap *Cache::blockedPixmap;
void Cache::init()
{
if ( !cache )
cache = new QHash<QString,CachedObject*>();
if ( !docloader )
docloader = new QLinkedList<DocLoader*>;
if ( !nullPixmap )
nullPixmap = new QPixmap;
if ( !brokenPixmap )
brokenPixmap = new QPixmap(KHTMLGlobal::iconLoader()->loadIcon("image-missing", KIconLoader::Desktop, 16, KIconLoader::DisabledState));
if ( !blockedPixmap ) {
blockedPixmap = new QPixmap();
blockedPixmap->loadFromData(blocked_icon_data, blocked_icon_len);
}
if ( !m_loader )
m_loader = new Loader();
if ( !freeList )
freeList = new QLinkedList<CachedObject*>;
}
void Cache::clear()
{
if ( !cache ) return;
#ifdef CACHE_DEBUG
kDebug( 6060 ) << "Cache: CLEAR!";
statistics();
#endif
#ifndef NDEBUG
bool crash = false;
foreach (CachedObject* co, *cache) {
if (!co->canDelete()) {
kDebug( 6060 ) << " Object in cache still linked to";
kDebug( 6060 ) << " -> URL: " << co->url();
kDebug( 6060 ) << " -> #clients: " << co->count();
crash = true;
// assert(co->canDelete());
}
}
foreach (CachedObject* co, *freeList) {
if (!co->canDelete()) {
kDebug( 6060 ) << " Object in freelist still linked to";
kDebug( 6060 ) << " -> URL: " << co->url();
kDebug( 6060 ) << " -> #clients: " << co->count();
crash = true;
/*
foreach (CachedObjectClient* cur, (*co->m_clients)))
{
if (dynamic_cast<RenderObject*>(cur)) {
kDebug( 6060 ) << " --> RenderObject";
} else
kDebug( 6060 ) << " --> Something else";
}*/
}
// assert(freeList->current()->canDelete());
}
assert(!crash);
#endif
qDeleteAll(*cache);
delete cache; cache = 0;
delete nullPixmap; nullPixmap = 0;
delete brokenPixmap; brokenPixmap = 0;
delete blockedPixmap; blockedPixmap = 0;
delete m_loader; m_loader = 0;
delete docloader; docloader = 0;
qDeleteAll(*freeList);
delete freeList; freeList = 0;
}
template<typename CachedObjectType, enum CachedObject::Type CachedType>
CachedObjectType* Cache::requestObject( DocLoader* dl, const KUrl& kurl, const char* accept )
{
KIO::CacheControl cachePolicy = dl->cachePolicy();
QString url = kurl.url();
CachedObject* o = cache->value(url);
if ( o && o->type() != CachedType ) {
removeCacheEntry( o );
o = 0;
}
if ( o && dl->needReload( o, url ) ) {
o = 0;
assert( !cache->contains( url ) );
}
if(!o)
{
#ifdef CACHE_DEBUG
kDebug( 6060 ) << "Cache: new: " << kurl.url();
#endif
CachedObjectType* cot = new CachedObjectType(dl, url, cachePolicy, accept);
cache->insert( url, cot );
if ( cot->allowInLRUList() )
insertInLRUList( cot );
o = cot;
}
#ifdef CACHE_DEBUG
else {
kDebug( 6060 ) << "Cache: using pending/cached: " << kurl.url();
}
#endif
dl->insertCachedObject( o );
return static_cast<CachedObjectType *>(o);
}
void Cache::preloadStyleSheet( const QString &url, const QString &stylesheet_data)
{
if (cache->contains(url))
removeCacheEntry(cache->value(url));
CachedCSSStyleSheet *stylesheet = new CachedCSSStyleSheet(url, stylesheet_data);
cache->insert( url, stylesheet );
}
void Cache::preloadScript( const QString &url, const QString &script_data)
{
if (cache->contains(url))
removeCacheEntry(cache->value(url));
CachedScript *script = new CachedScript(url, script_data);
cache->insert( url, script );
}
void Cache::flush(bool force)
{
init();
if ( force || totalSizeOfLRU > maxSize + maxSize/4) {
for ( int i = MAX_LRU_LISTS-1; i >= 0 && totalSizeOfLRU > maxSize; --i )
while ( totalSizeOfLRU > maxSize && m_LRULists[i].m_tail )
removeCacheEntry( m_LRULists[i].m_tail );
#ifdef CACHE_DEBUG
statistics();
#endif
}
QMutableLinkedListIterator<CachedObject*> it(*freeList);
while ( it.hasNext() ) {
CachedObject* p = it.next();
if ( p->canDelete() ) {
it.remove();
delete p;
}
}
}
void Cache::setSize( int bytes )
{
maxSize = bytes;
flush(true /* force */);
}
void Cache::statistics()
{
// this function is for debugging purposes only
init();
int size = 0;
int msize = 0;
int movie = 0;
int images = 0;
int scripts = 0;
int stylesheets = 0;
int sound = 0;
int fonts = 0;
foreach (CachedObject* o, *cache)
{
switch(o->type()) {
case CachedObject::Image:
{
//CachedImage *im = static_cast<CachedImage *>(o);
images++;
/*if(im->m != 0)
{
movie++;
msize += im->size();
}*/
break;
}
case CachedObject::CSSStyleSheet:
stylesheets++;
break;
case CachedObject::Script:
scripts++;
break;
case CachedObject::Sound:
sound++;
break;
case CachedObject::Font:
fonts++;
break;
}
size += o->size();
}
size /= 1024;
kDebug( 6060 ) << "------------------------- image cache statistics -------------------";
kDebug( 6060 ) << "Number of items in cache: " << cache->count();
kDebug( 6060 ) << "Number of cached images: " << images;
kDebug( 6060 ) << "Number of cached movies: " << movie;
kDebug( 6060 ) << "Number of cached scripts: " << scripts;
kDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets;
kDebug( 6060 ) << "Number of cached sounds: " << sound;
kDebug( 6060 ) << "Number of cached fonts: " << fonts;
kDebug( 6060 ) << "pixmaps: allocated space approx. " << size << " kB";
kDebug( 6060 ) << "movies : allocated space approx. " << msize/1024 << " kB";
kDebug( 6060 ) << "--------------------------------------------------------------------";
}
void Cache::removeCacheEntry( CachedObject *object )
{
QString key = object->url().string();
cache->remove( key );
removeFromLRUList( object );
foreach( DocLoader* dl, *docloader )
dl->removeCachedObject( object );
if ( !object->free() ) {
Cache::freeList->append( object );
object->m_free = true;
}
}
static inline int FastLog2(unsigned int j)
{
unsigned int log2;
log2 = 0;
if (j & (j-1))
log2 += 1;
if (j >> 16)
log2 += 16, j >>= 16;
if (j >> 8)
log2 += 8, j >>= 8;
if (j >> 4)
log2 += 4, j >>= 4;
if (j >> 2)
log2 += 2, j >>= 2;
if (j >> 1)
log2 += 1;
return log2;
}
static LRUList* getLRUListFor(CachedObject* o)
{
int accessCount = o->accessCount();
int queueIndex;
if (accessCount == 0) {
queueIndex = 0;
} else {
int sizeLog = FastLog2(o->size());
queueIndex = sizeLog/o->accessCount() - 1;
if (queueIndex < 0)
queueIndex = 0;
if (queueIndex >= MAX_LRU_LISTS)
queueIndex = MAX_LRU_LISTS-1;
}
return &m_LRULists[queueIndex];
}
void Cache::removeFromLRUList(CachedObject *object)
{
CachedObject *next = object->m_next;
CachedObject *prev = object->m_prev;
LRUList* list = getLRUListFor(object);
CachedObject *&head = getLRUListFor(object)->m_head;
if (next == 0 && prev == 0 && head != object) {
return;
}
object->m_next = 0;
object->m_prev = 0;
if (next)
next->m_prev = prev;
else if (list->m_tail == object)
list->m_tail = prev;
if (prev)
prev->m_next = next;
else if (head == object)
head = next;
totalSizeOfLRU -= object->size();
}
void Cache::insertInLRUList(CachedObject *object)
{
removeFromLRUList(object);
assert( object );
assert( !object->free() );
assert( object->canDelete() );
assert( object->allowInLRUList() );
LRUList* list = getLRUListFor(object);
CachedObject *&head = list->m_head;
object->m_next = head;
if (head)
head->m_prev = object;
head = object;
if (object->m_next == 0)
list->m_tail = object;
totalSizeOfLRU += object->size();
}
// --------------------------------------
void CachedObjectClient::updatePixmap(const QRect&, CachedImage *) {}
void CachedObjectClient::setStyleSheet(const DOM::DOMString &/*url*/, const DOM::DOMString &/*sheet*/, const DOM::DOMString &/*charset*/, const DOM::DOMString &/*mimetype*/) {}
void CachedObjectClient::notifyFinished(CachedObject * /*finishedObj*/) {}
void CachedObjectClient::error(int /*err*/, const QString &/*text*/) {}
#undef CDEBUG
diff --git a/khtml/test_regression_gui_window.cpp b/khtml/test_regression_gui_window.cpp
index 6171e33bbc..6db60e773e 100644
--- a/khtml/test_regression_gui_window.cpp
+++ b/khtml/test_regression_gui_window.cpp
@@ -1,1213 +1,1213 @@
/**
* This file is part of the KDE project
*
* Copyright (C) 2006 Nikolas Zimmermann <zimmermann@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.
*
*/
#include <assert.h>
#include <signal.h>
#include <QtCore/QFile>
#include <QtCore/QTimer>
#include <QtCore/QProcess>
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
#include <QMainWindow>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <kconfig.h>
#include <kfiledialog.h>
// Taken from QUrl
#define Q_HAS_FLAG(a, b) ( ((a) & (b)) == (b) )
#define Q_SET_FLAG(a, b) { (a) |= (b); }
#define Q_UNSET_FLAG(a, b) { (a) &= ~(b); }
TestRegressionWindow::TestRegressionWindow(QWidget *parent)
: QMainWindow(parent), m_flags(None), m_runCounter(0), m_testCounter(0), m_totalTests(0),
m_totalTestsJS(0), m_totalTestsDOMTS(0), m_lastResult(Unknown),
m_browserPart(0), m_activeProcess(0), m_activeTreeItem(0),
m_suspended(false), m_justProcessingQueue(false)
{
m_ui.setupUi(this);
// Setup actions/connections
connect(m_ui.actionOnly_run_JS_tests, SIGNAL(toggled(bool)), SLOT(toggleJSTests(bool)));
connect(m_ui.actionOnly_run_HTML_tests, SIGNAL(toggled(bool)), SLOT(toggleHTMLTests(bool)));
connect(m_ui.actionDo_not_suppress_debug_output, SIGNAL(toggled(bool)), SLOT(toggleDebugOutput(bool)));
connect(m_ui.actionDo_not_use_Xvfb, SIGNAL(toggled(bool)), SLOT(toggleNoXvfbUse(bool)));
connect(m_ui.actionSpecify_tests_directory, SIGNAL(triggered(bool)), SLOT(setTestsDirectory()));
connect(m_ui.actionSpecify_khtml_directory, SIGNAL(triggered(bool)), SLOT(setKHTMLDirectory()));
connect(m_ui.actionSpecify_output_directory, SIGNAL(triggered(bool)), SLOT(setOutputDirectory()));
connect(m_ui.actionRun_tests, SIGNAL(triggered(bool)), SLOT(runTests()));
connect(m_ui.pauseContinueButton, SIGNAL(clicked(bool)), SLOT(pauseContinueButtonClicked()));
connect(m_ui.saveLogButton, SIGNAL(clicked(bool)), SLOT(saveLogButtonClicked()));
connect(m_ui.treeWidget, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(treeWidgetContextMenuRequested(QPoint)));
// Setup actions' default state
m_ui.progressBar->setValue(0);
m_ui.textEdit->setReadOnly(true);
m_ui.actionRun_tests->setEnabled(false);
m_ui.pauseContinueButton->setEnabled(false);
m_ui.treeWidget->headerItem()->setTextAlignment(0, Qt::AlignLeft);
m_ui.treeWidget->headerItem()->setText(0, i18n("Available Tests: 0"));
// Load default values for tests directory/khtml directory...
KConfig config("testregressiongui", KConfig::SimpleConfig);
KConfigGroup grp = config.group("<default>");
m_testsUrl = KUrl::fromPath(grp.readPathEntry("TestsDirectory", QString()));
m_khtmlUrl = KUrl::fromPath(grp.readPathEntry("KHTMLDirectory", QString()));
initTestsDirectory();
// Init early visible items in the text edit...
initLegend();
initOutputBrowser();
}
TestRegressionWindow::~TestRegressionWindow()
{
if(m_activeProcess)
{
m_activeProcess->kill();
/* This leads to:
* QProcess object destroyed while process is still running.
* Any idea why??
delete m_activeProcess;
*/
m_activeProcess = 0;
}
}
void TestRegressionWindow::toggleJSTests(bool checked)
{
if(checked)
{
Q_SET_FLAG(m_flags, JSTests)
Q_UNSET_FLAG(m_flags, HTMLTests)
m_ui.actionOnly_run_HTML_tests->setChecked(false);
}
else
Q_UNSET_FLAG(m_flags, JSTests)
// Eventually update progress bar range...
updateProgressBarRange();
}
void TestRegressionWindow::toggleHTMLTests(bool checked)
{
if(checked)
{
Q_SET_FLAG(m_flags, HTMLTests)
Q_UNSET_FLAG(m_flags, JSTests)
m_ui.actionOnly_run_JS_tests->setChecked(false);
}
else
Q_UNSET_FLAG(m_flags, HTMLTests)
// Eventually update progress bar range...
updateProgressBarRange();
}
void TestRegressionWindow::toggleDebugOutput(bool checked)
{
if(checked)
Q_SET_FLAG(m_flags, DebugOutput)
else
Q_UNSET_FLAG(m_flags, DebugOutput)
}
void TestRegressionWindow::toggleNoXvfbUse(bool checked)
{
if(checked)
Q_SET_FLAG(m_flags, NoXvfbUse)
else
Q_UNSET_FLAG(m_flags, NoXvfbUse)
}
void TestRegressionWindow::setTestsDirectory()
{
m_testsUrl = KFileDialog::getExistingDirectory();
initTestsDirectory();
loadOutputHTML();
}
void TestRegressionWindow::setOutputDirectory()
{
m_outputUrl = KFileDialog::getExistingDirectory();
loadOutputHTML();
}
void TestRegressionWindow::initTestsDirectory()
{
bool okay = !m_testsUrl.isEmpty();
if(okay)
{
const char *subdirs[] = { "tests", "baseline", "output", "resources" };
for(int i = 0; i <= 3; i++)
{
QFileInfo sourceDir(m_testsUrl.path() + "/" + subdirs[i]); //krazy:exclude=duoblequote_chars DOM demands chars
if(!sourceDir.exists() || !sourceDir.isDir())
{
KMessageBox::error(0, i18n("Please choose a valid 'khtmltests/regression/' directory."));
okay = false;
m_testsUrl = KUrl();
break;
}
}
}
if(okay)
{
// Clean up...
m_itemMap.clear();
m_ignoreMap.clear();
m_failureMap.clear();
m_directoryMap.clear();
m_ui.treeWidget->clear();
if(!m_khtmlUrl.isEmpty())
m_ui.actionRun_tests->setEnabled(true);
// Initialize map (to prevent assert below)...
m_directoryMap.insert(QString(), QStringList());
// Setup root tree widget item...
(void) new QTreeWidgetItem(m_ui.treeWidget, QStringList(m_testsUrl.path() + "/tests"));
// Check for ignore & failure file in root directory...
QString ignoreFile = m_testsUrl.path() + "/tests/ignore";
QString failureFile = m_testsUrl.path() + "/tests/KNOWN_FAILURES";
QStringList ignoreFileList = readListFile(ignoreFile);
QStringList failureFileList = readListFile(failureFile);
if(!ignoreFileList.isEmpty())
m_ignoreMap.insert(QString(), ignoreFileList);
if(!failureFileList.isEmpty())
m_failureMap.insert(QString(), failureFileList);
// Remember directory...
KConfig config("testregressiongui", KConfig::SimpleConfig);
KConfigGroup grp = config.group("<default>");
grp.writePathEntry("TestsDirectory", m_testsUrl.path());
// Start listing directory...
KUrl listUrl = m_testsUrl; listUrl.addPath("tests");
KIO::ListJob *job = KIO::listRecursive(listUrl, KIO::HideProgressInfo, false /* no hidden files */);
connect(job, SIGNAL(result(KJob*)), SLOT(directoryListingFinished(KJob*)));
connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
this, SLOT(directoryListingResult(KIO::Job*,KIO::UDSEntryList)));
}
}
void TestRegressionWindow::setKHTMLDirectory()
{
m_khtmlUrl = KFileDialog::getExistingDirectory();
if(!m_khtmlUrl.isEmpty())
{
const char *subdirs[] = { "css", "dom", "xml", "html" }; // That's enough ;-)
for(int i = 0; i <= 3; i++)
{
QFileInfo sourceDir(m_khtmlUrl.path() + "/" + subdirs[i]); //krazy:exclude=duoblequote_chars DOM demands chars
if(!sourceDir.exists() || !sourceDir.isDir())
{
KMessageBox::error(0, i18n("Please choose a valid 'khtml/' build directory."));
m_khtmlUrl = KUrl();
break;
}
}
// Remember directory...
KConfig config("testregressiongui", KConfig::SimpleConfig);
KConfigGroup grp = config.group("<default>");
grp.writePathEntry("KHTMLDirectory", m_khtmlUrl.path());
if(!m_testsUrl.isEmpty() && !m_khtmlUrl.isEmpty())
m_ui.actionRun_tests->setEnabled(true);
}
}
void TestRegressionWindow::directoryListingResult(KIO::Job *, const KIO::UDSEntryList &list)
{
KIO::UDSEntryList::ConstIterator it = list.constBegin();
const KIO::UDSEntryList::ConstIterator end = list.constEnd();
for(; it != end; ++it)
{
const KIO::UDSEntry &entry = *it;
QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
if(entry.isDir()) // Create new map entry...
{
assert(m_directoryMap.constFind(name) == m_directoryMap.constEnd());
m_directoryMap.insert(name, QStringList());
QString ignoreFile = m_testsUrl.path() + "/tests/" + name + "/ignore";
QString failureFile = m_testsUrl.path() + "/tests/" + name + "/KNOWN_FAILURES";
QStringList ignoreFileList = readListFile(ignoreFile);
QStringList failureFileList = readListFile(failureFile);
if(!ignoreFileList.isEmpty())
m_ignoreMap.insert(name, ignoreFileList);
if(!failureFileList.isEmpty())
m_failureMap.insert(name, failureFileList);
}
else if(name.endsWith(".html") || name.endsWith(".htm") ||
name.endsWith(".xhtml") || name.endsWith(".xml") || name.endsWith(".js"))
{
int lastSlashPos = name.lastIndexOf('/');
QString cachedDirectory = (lastSlashPos > 0 ? name.mid(0, lastSlashPos) : QString());
QString cachedFilename = name.mid(lastSlashPos + 1);
assert(m_directoryMap.constFind(cachedDirectory) != m_directoryMap.constEnd());
m_directoryMap[cachedDirectory].append(cachedFilename);
}
}
}
void TestRegressionWindow::directoryListingFinished(KJob *)
{
QTreeWidgetItem *topLevelItem = m_ui.treeWidget->topLevelItem(0);
// Gather a lot of statistics...
unsigned long availableDomFiles = 0;
unsigned long availableDumpFiles = 0;
unsigned long availableRenderFiles = 0;
unsigned long ignoredJSTests = 0;
unsigned long availableJSTests = 0;
unsigned long ignoredXMLTests = 0;
unsigned long availableXMLTests = 0;
unsigned long ignoredHTMLTests = 0;
unsigned long availableHTMLTests = 0;
unsigned long ignoredDOMTSTests = 0;
unsigned long availableDOMTSTests = 0;
// Start the actual data processing...
QMap<QString, QStringList>::const_iterator it = m_directoryMap.constBegin();
const QMap<QString, QStringList>::const_iterator end = m_directoryMap.constEnd();
for(; it != end; ++it)
{
QString directory = it.key();
QStringList filenames = it.value();
if(filenames.isEmpty()) // Do not add empty directories at all...
continue;
bool hasIgnores = (m_ignoreMap.constFind(directory) != m_directoryMap.constEnd());
bool hasFailures = (m_failureMap.constFind(directory) != m_failureMap.constEnd());
// Extract parent directory...
int position = directory.lastIndexOf('/');
QString parentDirectory = directory.mid(0, (position == -1 ? 0 : position));
QString parentDirectoryItem = directory.mid(position + 1);
bool hasParentIgnores = (m_ignoreMap.constFind(parentDirectory) != m_directoryMap.constEnd());
bool hasParentFailures = (m_failureMap.constFind(parentDirectory) != m_failureMap.constEnd());
// Sort in ascending order...
filenames.sort();
QStringList::const_iterator it2 = filenames.constBegin();
const QStringList::const_iterator end2 = filenames.constEnd();
// Create new tree widget item for the active directory...
QTreeWidgetItem *parent = topLevelItem;
if(!directory.isEmpty())
{
parent = new QTreeWidgetItem(topLevelItem, QStringList(directory));
// Directory is completely ignored, mark it 'yellow'...
if(hasParentIgnores && m_ignoreMap[parentDirectory].contains(parentDirectoryItem))
parent->setIcon(0, m_ignorePixmap);
// Directory is completely known to fail, mark it 'red'...
if(hasParentFailures && m_failureMap[parentDirectory].contains(parentDirectoryItem))
parent->setIcon(0, m_failKnownPixmap);
}
// Add all contained files as new items below 'parent'...
for(; it2 != end2; ++it2)
{
QString test = (*it2);
QString cacheName = directory + "/" + test; //krazy:exclude=duoblequote_chars DOM demands chars
QTreeWidgetItem *testItem = new QTreeWidgetItem(parent, QStringList(KUrl(test).path()));
// Remember name <-> item pair...
assert(m_itemMap.contains(cacheName));
m_itemMap.insert(cacheName, testItem);
bool ignore = (hasIgnores && m_ignoreMap[directory].contains(test));
bool ignoreParent = (hasParentIgnores && m_ignoreMap[parentDirectory].contains(parentDirectoryItem));
bool failure = (hasFailures && m_failureMap[directory].contains(test));
// Check baseline directory for this test...
QString baseLinePath = m_testsUrl.path() + "/baseline/" + cacheName;
bool dom[9], render[9];
for(unsigned int i = 0; i < 9; ++i)
{
if(i == 0)
{
dom[i] = (QFileInfo(baseLinePath + "-dom").exists());
render[i] = (QFileInfo(baseLinePath + "-render").exists());
}
else
{
dom[i] = (QFileInfo(baseLinePath + "-" + QString::number(i) + "-dom").exists()); //krazy:exclude=duoblequote_chars DOM demands chars
render[i] = (QFileInfo(baseLinePath + "-" + QString::number(i) + "-render").exists()); //krazy:exclude=duoblequote_chars DOM demands chars
}
}
bool dump = (QFileInfo(baseLinePath + "-dump.png").exists());
// Ignored tests are marked 'yellow'...
if(ignore)
testItem->setIcon(0, m_ignorePixmap);
// Tests, known to fail, are marked 'red'...
if(failure)
testItem->setIcon(0, m_failKnownPixmap);
// Detect whether the tests has no corresponding baseline items...
if(!ignore && !failure)
{
if(!dom[0] && !dump && !render && !cacheName.endsWith(".js") && !cacheName.startsWith("domts"))
{
// See if parent directory is completely ignored...
if(!ignoreParent)
testItem->setIcon(0, m_noBaselinePixmap);
}
}
// Update statistics...
if(dump)
availableDumpFiles++;
for(unsigned i = 0; i < 9; ++i)
{
if(dom[i])
availableDomFiles++;
if(render[i])
availableRenderFiles++;
}
// Count DOM Testsuite files separated... (these have no baseline items!)
if(cacheName.startsWith("domts"))
{
// See if parent directory is completely ignored...
if(ignore || ignoreParent)
ignoredDOMTSTests++;
else
availableDOMTSTests++;
}
if(cacheName.endsWith(".html") || cacheName.endsWith(".htm") || cacheName.endsWith(".xhtml"))
{
if(ignore || ignoreParent)
ignoredHTMLTests++;
else
availableHTMLTests++;
}
else if(cacheName.endsWith(".xml"))
{
if(ignore || ignoreParent)
ignoredXMLTests++;
else
availableXMLTests++;
}
else if(cacheName.endsWith(".js"))
{
unsigned long containedTests = 0;
// Try hard to _ESTIMATE_ the number of tests...
// I really meant estimate, no way to calculate it perfectly.
QString jsFilePath = m_testsUrl.path() + "/tests/" + cacheName;
assert(QFileInfo(jsFilePath).exists() == true);
QStringList fileList = readListFile(jsFilePath);
QString fileContent = fileList.join("");
// #1 -> Check js file for the 'reportResult' calls...
containedTests = fileContent.count("reportResult");
// #2 -> Check js file for 'openPage' calls...
containedTests += fileContent.count("openPage");
// #3 -> Check js file for 'checkOutput' calls...
containedTests += fileContent.count("checkOutput");
// #4 -> Fallback for ie. mozilla/ecma files...
if(containedTests == 0) // Doesn't use 'reportResult' scheme...
containedTests++;
if(ignore || ignoreParent)
ignoredJSTests += containedTests;
else
availableJSTests += containedTests;
}
}
}
// Now we can calculate all ignored/available tests...
unsigned long ignoredTests = ignoredJSTests + ignoredXMLTests + ignoredHTMLTests;
unsigned long availableTests = availableJSTests + availableXMLTests + availableHTMLTests;
// This estimates the number of total tests, depending on the mode...
m_totalTests = availableDomFiles + availableDumpFiles + availableRenderFiles +
availableDOMTSTests + availableJSTests;
m_totalTestsJS = availableJSTests;
m_totalTestsDOMTS = availableDOMTSTests;
// Update progress bar range...
updateProgressBarRange();
QString statistics = QString("<body><table border='0' align='center' cellspacing='15'>") +
QString("<tr valign='top'><td colspan='3'><center><b>Statistics</b></center></td></tr>") +
QString("<tr valign='middle'><td>JS Tests</td><td>" + QString::number(availableJSTests) + "</td><td>(" + QString::number(ignoredJSTests) + " ignored)</td></tr>") +
QString("<tr valign='middle'><td>XML Tests</td><td>" + QString::number(availableXMLTests) + "</td><td>(" + QString::number(ignoredXMLTests) + " ignored)</td></tr>") +
QString("<tr valign='middle'><td>HTML Tests</td><td>" + QString::number(availableHTMLTests) + "</td><td>(" + QString::number(ignoredHTMLTests) + " ignored)</td></tr>") +
QString("</table></body>");
// Go to end...
QTextCursor cursor = m_ui.textEdit->textCursor();
cursor.movePosition(QTextCursor::End);
m_ui.textEdit->setTextCursor(cursor);
// Insert statistics...
m_ui.textEdit->insertHtml(statistics);
// Update treeview...
m_ui.treeWidget->headerItem()->setText(0, i18n("Available Tests: %1 (ignored: %2)", availableTests, ignoredTests));
}
void TestRegressionWindow::updateProgressBarRange() const
{
if(m_totalTests != 0 && m_totalTestsJS != 0)
{
unsigned long totalTests = m_totalTests;
if(Q_HAS_FLAG(m_flags, JSTests))
totalTests = m_totalTestsJS;
else if(Q_HAS_FLAG(m_flags, HTMLTests))
{
totalTests -= m_totalTestsJS;
totalTests -= m_totalTestsDOMTS;
}
m_ui.progressBar->setRange(0, totalTests);
}
}
void TestRegressionWindow::pauseContinueButtonClicked()
{
assert(m_activeProcess != 0);
if(!m_suspended)
{
// Suspend process
kill(m_activeProcess->pid(), SIGSTOP);
m_suspended = true;
m_ui.pauseContinueButton->setText(i18n("Continue"));
}
else
{
// Continue process
kill(m_activeProcess->pid(), SIGCONT);
m_suspended = false;
m_ui.pauseContinueButton->setText(i18n("Pause"));
}
}
void TestRegressionWindow::saveLogButtonClicked()
{
assert(m_activeProcess == 0);
m_saveLogUrl = KFileDialog::getExistingDirectory();
QString fileName = m_saveLogUrl.path() + "/logOutput.html";
if(QFileInfo(fileName).exists())
{
// Remove file if already existent...
QFile file(fileName);
if(!file.remove())
{
kError() << " Can't remove " << fileName << endl;
exit(1);
}
}
}
void TestRegressionWindow::runTests()
{
// Run in all-in-one mode...
m_runCounter = 0;
m_testCounter = 0;
initRegressionTesting(QString());
}
void TestRegressionWindow::runSingleTest()
{
assert(m_activeTreeItem != 0);
QString testFileName = pathFromItem(m_activeTreeItem);
// Run in single-test mode...
m_runCounter = 0;
m_testCounter = -1;
initRegressionTesting(testFileName);
}
void TestRegressionWindow::initRegressionTesting(const QString &testFileName)
{
assert(m_activeProcess == 0);
m_activeProcess = new QProcess();
m_activeProcess->setReadChannelMode(QProcess::MergedChannels);
QStringList environment = QProcess::systemEnvironment();
environment << "KDE_DEBUG=false"; // No Dr. Konqi please!
QString program = m_khtmlUrl.path() + "/.libs/testregression";
QString program2 = m_khtmlUrl.path() + "/testregression"; // with CMake, it's in $buildir/bin
if(!QFileInfo(program).exists())
{
if(!QFileInfo(program2).exists())
{
KMessageBox::error(0, i18n("Cannot find testregression executable."));
return;
}
else
{
program = program2;
}
}
QStringList arguments;
arguments << "--base" << m_testsUrl.path();
if(!m_outputUrl.isEmpty())
arguments << "--output" << m_outputUrl.path();
if(!testFileName.isEmpty())
arguments << "--test" << testFileName;
if(Q_HAS_FLAG(m_flags, JSTests))
arguments << "--js";
if(Q_HAS_FLAG(m_flags, HTMLTests))
arguments << "--html";
if(Q_HAS_FLAG(m_flags, DebugOutput))
arguments << "--debug";
if(Q_HAS_FLAG(m_flags, NoXvfbUse))
arguments << "--noxvfb";
connect(m_activeProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(testerExited(int,QProcess::ExitStatus)));
connect(m_activeProcess, SIGNAL(readyReadStandardOutput()), SLOT(testerReceivedData()));
// Clear processing queue before starting...
m_processingQueue.clear();
// Clean up gui...
m_ui.textEdit->clear();
m_ui.progressBar->reset();
m_ui.saveLogButton->setEnabled(false);
m_ui.actionRun_tests->setEnabled(false);
m_ui.pauseContinueButton->setEnabled(true);
// Start regression testing process...
m_activeProcess->setEnvironment(environment);
m_activeProcess->start(program, arguments, QIODevice::ReadOnly);
}
void TestRegressionWindow::initOutputBrowser()
{
assert(m_browserPart == 0);
m_browserPart = new KHTMLPart(m_ui.secondTab, m_ui.secondTab, KHTMLPart::BrowserViewGUI);
// Setup vertical layout for the browser widget...
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(m_browserPart->widget());
m_ui.secondTab->setLayout(layout);
m_browserPart->setJavaEnabled(true);
m_browserPart->setJScriptEnabled(true);
m_browserPart->setPluginsEnabled(true);
m_browserPart->setURLCursor(QCursor(Qt::PointingHandCursor));
m_browserPart->widget()->show();
// Check if there is already an output/index.html present...
loadOutputHTML();
}
void TestRegressionWindow::loadOutputHTML() const
{
if(m_testsUrl.isEmpty())
return;
QString fileName = m_testsUrl.path() + "/output/index.html";
if(!m_outputUrl.isEmpty())
fileName = m_outputUrl.path() + "/index.html";
QFileInfo indexHtml(fileName);
if(indexHtml.exists())
{
m_browserPart->openUrl(KUrl::fromPath(fileName));
m_ui.tabWidget->setTabEnabled(1, true);
}
else
m_ui.tabWidget->setTabEnabled(1, false);
}
void TestRegressionWindow::updateItemStatus(TestResult result, QTreeWidgetItem *item, const QString &testFileName)
{
if(!item)
return;
// Ensure item is visible...
QTreeWidgetItem *parent = item;
while(parent != 0)
{
m_ui.treeWidget->setItemExpanded(parent, true);
parent = parent->parent();
}
m_ui.treeWidget->scrollToItem(item);
bool updateIcon = true;
if(m_lastName == testFileName && !m_lastName.isEmpty())
{
if(m_lastResult == result)
updateIcon = false;
else if((m_lastResult == Pass || m_lastResult == PassUnexpected) &&
(result == Fail || result == FailKnown || result == Crash))
{
// If one part of the test (render/dom/paint) passed,
// and the current part fails, update to 'failed' icon...
updateIcon = true;
}
else if((m_lastResult == Fail || m_lastResult == FailKnown || m_lastResult == Crash) &&
(result == Pass || result == PassUnexpected))
{
// If one part of the test (render/dom/paint) failed,
// and the current part passes, don't update to 'passed' icon...
updateIcon = false;
}
}
// Update icon, if necessary...
if(updateIcon)
{
if(result == Crash)
item->setIcon(0, m_crashPixmap);
else if(result == Fail)
item->setIcon(0, m_failPixmap);
else if(result == FailKnown)
item->setIcon(0, m_failKnownPixmap);
else if(result == Pass)
item->setIcon(0, m_passPixmap);
else if(result == PassUnexpected)
item->setIcon(0, m_passUnexpectedPixmap);
else // Unhandled state...
assert(false);
}
// Remember test & result...
m_lastResult = result;
m_lastName = testFileName;
m_activeTreeItem = item;
}
void TestRegressionWindow::initLegend()
{
// Init pixmaps...
m_failPixmap = QPixmap(":/test/pics/fail.xpm");
m_failKnownPixmap = QPixmap(":/test/pics/failKnown.xpm");
m_passPixmap = QPixmap(":/test/pics/pass.xpm");
m_passUnexpectedPixmap = QPixmap(":/test/pics/passUnexpected.xpm");
m_ignorePixmap = QPixmap(":/test/pics/ignore.xpm");
m_crashPixmap = QPixmap(":/test/pics/crash.xpm");
m_noBaselinePixmap = QPixmap(":/test/pics/noBaseline.xpm");
QString legend = QLatin1String("<body><center><font size='8'>Welcome to the khtml<br/>") +
QLatin1String("regression testing tool!</font></center><br/><br/>") +
QLatin1String("<table border='0' align='center' cellspacing='15'>") +
QLatin1String("<tr valign='top'><td colspan='2'><center><b>Legend</b></center></td></tr>") +
QLatin1String("<tr valign='middle'><td>Pass</td><td><img src=':/test/pics/pass.xpm'></td></tr>") +
QLatin1String("<tr valign='middle'><td>Pass unexpected</td><td><img src=':/test/pics/passUnexpected.xpm'></td></tr>") +
QLatin1String("<tr valign='middle'><td>Fail</td><td><img src=':/test/pics/fail.xpm'></td></tr>") +
QLatin1String("<tr valign='middle'><td>Fail known</td><td><img src=':/test/pics/failKnown.xpm'></td></tr>") +
QLatin1String("<tr valign='middle'><td>Ignore</td><td><img src=':/test/pics/ignore.xpm'></td></tr>") +
QLatin1String("<tr valign='middle'><td>Baseline missing</td><td><img src=':/test/pics/noBaseline.xpm'></td></tr>") +
QLatin1String("<tr valign='middle'><td>Crash</td><td><img src=':/test/pics/crash.xpm'></td></tr>") +
QLatin1String("</table></body>");
m_ui.textEdit->setHtml(legend);
}
void TestRegressionWindow::testerExited(int /* exitCode */, QProcess::ExitStatus exitStatus)
{
assert(m_activeProcess != 0);
assert(m_activeTreeItem != 0);
if(exitStatus == QProcess::CrashExit) // Special case: crash!
{
QTreeWidgetItem *useItem = m_activeTreeItem;
if(m_testCounter >= 0 || m_runCounter > 0) // Single-tests mode invoked on a directory OR All-test-mode
{
QTreeWidgetItem *parent = useItem->parent();
assert(parent != 0);
useItem = parent->child(parent->indexOfChild(useItem) + 1);
assert(useItem != 0);
}
// Reflect crashed test...
updateItemStatus(Crash, useItem, QString());
}
if(m_testCounter >= 0) // All-tests mode
m_ui.progressBar->setValue(m_ui.progressBar->maximum());
// Eventually save log output...
if(!m_saveLogUrl.isEmpty())
{
// We should close our written log with </body></html>.
m_processingQueue.enqueue(QString::fromLatin1("\n</body>\n</html>"));
if(!m_justProcessingQueue)
{
m_justProcessingQueue = true;
QTimer::singleShot(50, this, SLOT(processQueue()));
}
}
// Cleanup gui...
m_ui.saveLogButton->setEnabled(true);
m_ui.actionRun_tests->setEnabled(true);
m_ui.pauseContinueButton->setEnabled(false);
// Check if there is already an output/index.html present...
loadOutputHTML();
// Cleanup data..
delete m_activeProcess;
m_activeProcess = 0;
m_runCounter = 0;
m_testCounter = 0;
m_activeTreeItem = 0;
}
void TestRegressionWindow::testerReceivedData()
{
assert(m_activeProcess != 0);
QString data(m_activeProcess->readAllStandardOutput());
QStringList list = data.split('\n');
QStringList::const_iterator it = list.constBegin();
const QStringList::const_iterator end = list.constEnd();
for(; it != end; ++it)
{
QString temp = *it;
if(!temp.isEmpty())
m_processingQueue.enqueue(temp);
}
if(!m_justProcessingQueue)
{
m_justProcessingQueue = true;
QTimer::singleShot(50, this, SLOT(processQueue()));
}
}
void TestRegressionWindow::processQueue()
{
while(!m_processingQueue.isEmpty())
{
QString data = m_processingQueue.dequeue();
TestResult result = Unknown;
QString cacheName = extractTestNameFromData(data, result);
if(result != Unknown) // Yes, we're dealing with a test result...
{
if(cacheName.isEmpty()) // Make sure everything is alright!
{
kError() << "Couldn't extract cacheName from data=\"" << data << "\"! Ignoring!" << endl;
continue;
}
}
parseRegressionTestingOutput(data, result, cacheName);
}
m_justProcessingQueue = false;
}
void TestRegressionWindow::addToIgnores()
{
assert(m_activeTreeItem != 0);
QString treeItemText = pathFromItem(m_activeTreeItem);
// Extract directory/file name...
int position = treeItemText.lastIndexOf('/');
QString directory = treeItemText.mid(0, (position == -1 ? 0 : position));
QString fileName = treeItemText.mid(position + 1);
// Read corresponding ignore file..
QString ignoreFile = m_testsUrl.path() + "/tests/" + directory + "/ignore";
QStringList ignoreFileList = readListFile(ignoreFile);
if(!ignoreFileList.contains(fileName))
ignoreFileList.append(fileName);
// Commit changes...
writeListFile(ignoreFile, ignoreFileList);
// Reset icon status...
m_activeTreeItem->setIcon(0, m_ignorePixmap);
}
void TestRegressionWindow::removeFromIgnores()
{
assert(m_activeTreeItem != 0);
QString treeItemText = pathFromItem(m_activeTreeItem);
// Extract directory/file name...
int position = treeItemText.lastIndexOf('/');
QString directory = treeItemText.mid(0, (position == -1 ? 0 : position));
QString fileName = treeItemText.mid(position + 1);
// Read corresponding ignore file..
QString ignoreFile = m_testsUrl.path() + "/tests/" + directory + "/ignore";
QStringList ignoreFileList = readListFile(ignoreFile);
if(ignoreFileList.contains(fileName))
ignoreFileList.removeAll(fileName);
// Commit changes...
writeListFile(ignoreFile, ignoreFileList);
// Reset icon status...
m_activeTreeItem->setIcon(0, QPixmap());
}
QString TestRegressionWindow::pathFromItem(const QTreeWidgetItem *item) const
{
QString path = item->text(0);
QTreeWidgetItem *parent = item->parent();
while(parent != 0)
{
if(parent->parent() != 0)
path.prepend(parent->text(0) + "/"); //krazy:exclude=duoblequote_chars DOM demands chars
parent = parent->parent();
}
return path;
}
QString TestRegressionWindow::extractTestNameFromData(QString &data, TestResult &result) const
{
if(data.indexOf("PASS") >= 0 || data.indexOf("FAIL") >= 0)
{
// Name extraction regexps...
QString bracesSelector("[0-9a-zA-Z-_<>\\* +-,.:!?$'\"=/\\[\\]\\(\\)]*");
QRegExp expPass("PASS: (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars
QRegExp expPassUnexpected("PASS \\(unexpected!\\): (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars
QRegExp expFail("FAIL: (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars
QRegExp expFailKnown("FAIL \\(known\\): (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars
// Extract name of test... (while using regexps as rare as possible!)
int pos = -1;
QString test;
QRegExp cleanTest(" \\[" + bracesSelector + "\\]");
pos = expPass.indexIn(data);
if(pos > -1) { test = expPass.cap(1); result = Pass; }
if(result == Unknown)
{
pos = expPassUnexpected.indexIn(data);
if(pos > -1) { test = expPassUnexpected.cap(1); result = PassUnexpected; }
}
if(result == Unknown)
{
pos = expFail.indexIn(data);
if(pos > -1) { test = expFail.cap(1); result = Fail; }
}
if(result == Unknown)
{
pos = expFailKnown.indexIn(data);
if(pos > -1) { test = expFailKnown.cap(1); result = FailKnown; }
}
if(!test.isEmpty() && result != Unknown) // Got information about test...
{
// Clean up first, so we only get the file name...
test.replace(cleanTest, QString());
// Extract cached directory/filename pair...
int lastSlashPos = test.lastIndexOf('/');
QString cachedDirectory = (lastSlashPos > 0 ? test.mid(0, lastSlashPos) : QString());
QString cachedFilename = test.mid(lastSlashPos + 1);
if(cachedDirectory == ".") // Handle cases like "./empty.html"
cachedDirectory.clear();
assert(m_directoryMap.constFind(cachedDirectory) != m_directoryMap.constEnd());
QString cacheName = cachedDirectory + "/" + cachedFilename; //krazy:exclude=duoblequote_chars DOM demands chars
if(m_itemMap.constFind(cacheName) != m_itemMap.constEnd())
{
// Highlight test...
data.replace(expPass, "<b><font color='green'>PASS:\t\\1</font></b>");
data.replace(expPassUnexpected, "<b><font color='green'>PASS (unexpected!):\t\\1</font></b>");
data.replace(expFail, "<b><font color='red'>FAIL:\t\\1</font></b>");
data.replace(expFailKnown, "<b><font color='red'>FAIL (known):\t\\1</font></b>");
return cacheName;
}
}
}
return QString();
}
void TestRegressionWindow::parseRegressionTestingOutput(QString data, TestResult result, const QString &cacheName)
{
if(!cacheName.isEmpty())
{
if(m_testCounter >= 0) // Only increment in all-tests mode...
m_testCounter++;
m_runCounter++; // Always increment...
// Update the icon...
updateItemStatus(result, m_itemMap[cacheName], cacheName);
}
// Apply some nice formatting for the statistics...
if(data.indexOf("Total") >= 0 || data.indexOf("Passes") >= 0 || data.indexOf("Tests completed") >= 0 ||
data.indexOf("Errors:") >= 0 || data.indexOf("Failures:") >= 0)
{
QRegExp expTotal("Total: ([0-9]*)");
QRegExp expPasses("Passes: ([0-9 a-z\\(\\)]*)");
QRegExp expErrors("Errors: ([0-9 a-z]*)");
QRegExp expFailures("Failures: ([0-9 a-z\\(\\)]*)");
data.replace("Tests completed.", "<br><center><h2>Tests completed.</h2></center>");
data.replace(expTotal, "<b><font size='4'>Total:&nbsp;\\1</font></b>");
data.replace(expPasses, "<b><font size='4' color='green'>Passes:&nbsp;\\1</font></b>");
data.replace(expErrors, "<b><font size='4' color='blue'>Errors:&nbsp;\\1</font></b>");
data.replace(expFailures, "<b><font size='4' color='red'>Failures:&nbsp;\\1</font></b>");
}
if(!data.contains("</body>\n</html>")) // Don't put <br> behind </html>!
data.append("<br>");
// Update text edit...
updateLogOutput(data);
// Update progressbar...
if(m_testCounter > 0)
m_ui.progressBar->setValue(m_testCounter);
}
void TestRegressionWindow::treeWidgetContextMenuRequested(const QPoint &pos)
{
if((m_testCounter == -1 && m_activeProcess) || (m_testCounter > 0 && m_activeProcess) ||
m_testsUrl.isEmpty() || m_khtmlUrl.isEmpty()) // Still processing/not ready yet...
{
return;
}
QTreeWidgetItem *item = m_ui.treeWidget->itemAt(pos);
if(item && item != m_ui.treeWidget->topLevelItem(0))
{
m_activeTreeItem = item;
// Build & show popup menu...
QMenu menu(m_ui.treeWidget);
menu.addAction(SmallIcon("media-playback-start"), i18n("Run test..."), this, SLOT(runSingleTest()));
menu.addSeparator();
menu.addAction(SmallIcon("list-add"), i18n("Add to ignores..."), this, SLOT(addToIgnores()));
menu.addAction(SmallIcon("dialog-cancel"), i18n("Remove from ignores..."), this, SLOT(removeFromIgnores()));
if(!menu.exec(m_ui.treeWidget->mapToGlobal(pos)))
m_activeTreeItem = 0; // Needs reset...
}
}
void TestRegressionWindow::updateLogOutput(const QString &data)
{
QTextCursor cursor = m_ui.textEdit->textCursor();
// Append 'data'...
m_ui.textEdit->insertHtml(data);
// Keep a maximum of 100 lines in the log...
const int maxLogLines = 100;
long logLines = countLogLines();
if(logLines > maxLogLines)
{
cursor.movePosition(QTextCursor::Start);
cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, logLines - maxLogLines);
cursor.removeSelectedText();
}
// Go to end...
cursor.movePosition(QTextCursor::End);
m_ui.textEdit->setTextCursor(cursor);
// Eventually save log output...
if(!m_saveLogUrl.isEmpty())
{
QString fileName = m_saveLogUrl.path() + "/logOutput.html";
QIODevice::OpenMode fileFlags = QIODevice::WriteOnly;
bool fileExists = QFileInfo(fileName).exists();
if(fileExists)
fileFlags |= QIODevice::Append;
QFile file(fileName);
if(!file.open(fileFlags))
{
kError() << " Can't open " << fileName << endl;
exit(1);
}
if(!fileExists)
- file.write(QString::fromLatin1("<html>\n<body>\n").toAscii());
+ file.write(QString::fromLatin1("<html>\n<body>\n").toLatin1());
- file.write(QString(data + "\n").toAscii()); //krazy:exclude=duoblequote_chars DOM demands chars
+ file.write(QString(data + "\n").toLatin1()); //krazy:exclude=duoblequote_chars DOM demands chars
file.close();
// Reset save log url, if we reached the end...
if(data.contains("</body>\n</html>"))
m_saveLogUrl = KUrl();
}
}
unsigned long TestRegressionWindow::countLogLines() const
{
QTextCursor cursor = m_ui.textEdit->textCursor();
cursor.movePosition(QTextCursor::Start);
unsigned long lines = 0;
while(cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor))
lines++;
return lines;
}
QStringList TestRegressionWindow::readListFile(const QString &fileName) const
{
QStringList files;
QFileInfo fileInfo(fileName);
if(fileInfo.exists())
{
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly))
{
kError() << " Can't open " << fileName << endl;
exit(1);
}
QString line;
QTextStream fileStream(&file);
while(!(line = fileStream.readLine()).isNull())
files.append(line);
file.close();
}
return files;
}
void TestRegressionWindow::writeListFile(const QString &fileName, const QStringList &content) const
{
QFile file(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
kError() << " Can't open " << fileName << endl;
exit(1);
}
- file.write(content.join("\n").toAscii());
+ file.write(content.join("\n").toLatin1());
file.close();
}
// vim:ts=4:tw=4:noet
diff --git a/kinit/kwrapper_win.cpp b/kinit/kwrapper_win.cpp
index c0dbd7f5a7..881920efc5 100644
--- a/kinit/kwrapper_win.cpp
+++ b/kinit/kwrapper_win.cpp
@@ -1,236 +1,236 @@
/*
This file is part of the KDE libraries
Copyright (c) 2007 Ralf Habacker <ralf.habacker@freenet.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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.
*/
/*
kde application starter
- allows starting kde application without any additional path settings [1]
- supports multiple root installation which is often used by packagers
(adds bin/lib directories from KDEDIRS environment to PATH environment)
- designed to start kde application from windows start menu entries
- support for reading KDEDIRS setting from flat file
(located in <path-of-kwrapper.exe>/../../kdedirs.cache)
- planned: automatic KDEDIRS detection support
[1] recent kde cmake buildsystem on win32 installs shared libraries
into lib instead of bin. This requires to have the lib directory
in the PATH environment variable too (required for all pathes in KDEDIRS)
TODO: There is an prelimary concept of setting KDEDIRS environment variable
from a cache file located on a well known path relative from the requested
application.
The recent implementation expects a file name 'kdedirs.cache' two level
above this executable which will be <ProgramFiles> in case kwrapper4 lives
in <Programfiles>/kde4/bin.
This works not in any case especially when running application inside the
build directory.
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <process.h>
#include <windows.h>
#include <QString>
#include <QProcess>
#include <QtDebug>
#include <QFileInfo>
#include <QCoreApplication>
#include <QList>
bool verbose = 0;
int main(int argc, char **argv)
{
QCoreApplication app(argc,argv);
QStringList envPath; /// pathes for using in environment of started process
QStringList searchPath; /// pathes for using to find executable
QString exeToStart;
QString myAppName = "kwrapper4:";
QStringList exeParams;
int firstParam = 1;
if (QCoreApplication::arguments().size() == 1)
{
qDebug() << myAppName << "no application given";
return 1;
}
if (QCoreApplication::arguments().at(1) == "--verbose")
{
verbose = 1;
firstParam = 2;
}
exeToStart = QCoreApplication::arguments().at(firstParam);
for(int i=firstParam+1; i < QCoreApplication::arguments().size(); i++)
exeParams << QCoreApplication::arguments().at(i);
QString path = QString::fromLocal8Bit(qgetenv("PATH")).toLower().replace('\\','/');
/** add pathes from PATH environment
- all to client path environment
- pathes not ending with lib to application search path
*/
foreach(const QString &a, path.split(';'))
{
if (!envPath.contains(a))
envPath << a;
if (!a.endsWith(QLatin1String("/lib")) && !a.endsWith(QLatin1String("/lib/")) && !searchPath.contains(a))
searchPath << a;
}
// add current install path
path = QCoreApplication::applicationDirPath().toLower().replace('\\','/');
if (!envPath.contains(path))
envPath << path;
// detect directory where kdedirs.cache lives
// this is not complete, KDEDIRS path should be used as base too
QFileInfo fi(path + "/../..");
QString rootPath = fi.canonicalPath();
if (verbose)
qDebug() << "try to find kdedirs.cache in" << rootPath;
// add current lib path to client path environment
path = path.replace("bin","lib");
if (!envPath.contains(path))
envPath << path;
/**
add bin and lib pathes from KDEDIRS
- bin/lib to client path environment
- bin to application search path
*/
path = QString::fromLocal8Bit(qgetenv("KDEDIRS")).toLower().replace('\\','/');
QStringList kdedirs;
if (path.size() > 0)
kdedirs = path.split(';');
bool changedKDEDIRS = 0;
// setup kdedirs if not present
if (kdedirs.size() == 0)
{
QStringList kdedirsCacheList;
#ifdef Q_CC_MSVC
kdedirsCacheList << rootPath + "/kdedirs.cache.msvc";
#endif
kdedirsCacheList << rootPath + "/kdedirs.cache";
bool found = false;
foreach(const QString &kdedirsCachePath,kdedirsCacheList)
{
QFile f(kdedirsCachePath);
if (f.exists())
{
f.open(QIODevice::ReadOnly);
QByteArray data = f.readAll();
f.close();
kdedirs = QString(data).split(';');
if (verbose)
qDebug() << "load kdedirs cache from " << kdedirsCachePath << "values=" << kdedirs;
found = true;
break;
}
}
if (!found)
{
/*
f.open(QIODevice::WriteOnly);
// search all pathes one level above for a directory share/apps
// write entries into a cache
- f.write(kdedirs.join(";").toAscii());
+ f.write(kdedirs.join(";").toLatin1());
f.close();
*/
}
changedKDEDIRS = 1;
}
if (verbose)
qDebug() << "found KDEDIRS\n\t" << kdedirs.join("\n\t");
foreach(const QString &a, kdedirs)
{
if (!envPath.contains(a+"/bin"))
envPath << a + "/bin";
if (!envPath.contains(a+"/lib"))
envPath << a + "/lib";
if (!searchPath.contains(a+"/bin"))
searchPath << a + "/bin";
}
// find executable
WCHAR _appName[MAX_PATH+1];
if (verbose)
qDebug() << "search " << exeToStart << "in";
bool found = false;
foreach(const QString &a, searchPath)
{
if (verbose)
qDebug() << "\t" << a;
#ifndef _WIN32_WCE
if (SearchPathW((LPCWSTR)a.utf16(),(LPCWSTR)exeToStart.utf16(),
L".exe",MAX_PATH+1,(LPWSTR)_appName,NULL))
{
found = true;
break;
}
#else
if (QFile::exists(a+"/"+exeToStart+".exe"))
{
found = true;
break;
}
#endif
}
QString appName = QString::fromUtf16((unsigned short*)_appName);
if (!found)
{
qWarning() << myAppName << "application not found";
return 3;
}
if (verbose)
qDebug() << "run" << exeToStart << "with params" << exeParams << "and PATH environment\n\t" << envPath.join("\n\t");
// setup client process envirionment
QStringList env = QProcess::systemEnvironment();
env.replaceInStrings(QRegExp("^PATH=(.*)", Qt::CaseInsensitive), QLatin1String("PATH=") + envPath.join(";"));
if (changedKDEDIRS)
env << QLatin1String("KDEDIRS=") + kdedirs.join(";");
QProcess *process = new QProcess;
process->setEnvironment(env);
process->start(appName,exeParams);
if (process->state() == QProcess::NotRunning)
{
qWarning() << myAppName << "process not running";
return 4;
}
process->waitForStarted();
return 0;
}
diff --git a/kio/kio/slaveinterface.cpp b/kio/kio/slaveinterface.cpp
index c7573762f1..a53c1689de 100644
--- a/kio/kio/slaveinterface.cpp
+++ b/kio/kio/slaveinterface.cpp
@@ -1,521 +1,521 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 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 version 2 as published by the Free Software Foundation.
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 "slaveinterface.h"
#include "slaveinterface_p.h"
#include "slavebase.h"
#include "connection_p.h"
#include "hostinfo_p.h"
#include <errno.h>
#include <assert.h>
#include <kdebug.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <klocalizedstring.h>
#include <ksslinfodialog.h>
#include <kmessagebox.h>
#include <time.h>
#include <QtDBus/QtDBus>
#include <QtCore/QPointer>
#include <QtNetwork/QSslCertificate>
#include <QtNetwork/QSslError>
using namespace KIO;
SlaveInterface::SlaveInterface(SlaveInterfacePrivate &dd, QObject *parent)
: QObject(parent), d_ptr(&dd)
{
connect(&d_ptr->speed_timer, SIGNAL(timeout()), SLOT(calcSpeed()));
}
SlaveInterface::~SlaveInterface()
{
// Note: no kDebug() here (scheduler is deleted very late)
delete d_ptr;
}
void SlaveInterface::setConnection( Connection* connection )
{
Q_D(SlaveInterface);
d->connection = connection;
}
Connection *SlaveInterface::connection() const
{
const Q_D(SlaveInterface);
return d->connection;
}
static KIO::filesize_t readFilesize_t(QDataStream &stream)
{
KIO::filesize_t result;
stream >> result;
return result;
}
bool SlaveInterface::dispatch()
{
Q_D(SlaveInterface);
assert( d->connection );
int cmd;
QByteArray data;
int ret = d->connection->read( &cmd, data );
if (ret == -1)
return false;
return dispatch( cmd, data );
}
void SlaveInterface::calcSpeed()
{
Q_D(SlaveInterface);
if (d->slave_calcs_speed) {
d->speed_timer.stop();
return;
}
struct timeval tv;
gettimeofday(&tv, 0);
long diff = ((tv.tv_sec - d->start_time.tv_sec) * 1000000 +
tv.tv_usec - d->start_time.tv_usec) / 1000;
if (diff - d->last_time >= 900) {
d->last_time = diff;
if (d->nums == max_nums) {
// let's hope gcc can optimize that well enough
// otherwise I'd try memcpy :)
for (unsigned int i = 1; i < max_nums; ++i) {
d->times[i-1] = d->times[i];
d->sizes[i-1] = d->sizes[i];
}
d->nums--;
}
d->times[d->nums] = diff;
d->sizes[d->nums++] = d->filesize - d->offset;
KIO::filesize_t lspeed = 1000 * (d->sizes[d->nums-1] - d->sizes[0]) / (d->times[d->nums-1] - d->times[0]);
// kDebug() << (long)d->filesize << diff
// << long(d->sizes[d->nums-1] - d->sizes[0])
// << d->times[d->nums-1] - d->times[0]
// << long(lspeed) << double(d->filesize) / diff
// << convertSize(lspeed)
// << convertSize(long(double(d->filesize) / diff) * 1000);
if (!lspeed) {
d->nums = 1;
d->times[0] = diff;
d->sizes[0] = d->filesize - d->offset;
}
emit speed(lspeed);
}
}
/*
* Map pid_t to a signed integer type that makes sense for QByteArray;
* only the most common sizes 16 bit and 32 bit are special-cased.
*/
template<int T> struct PIDType { typedef pid_t PID_t; } ;
template<> struct PIDType<2> { typedef qint16 PID_t; } ;
template<> struct PIDType<4> { typedef qint32 PID_t; } ;
bool SlaveInterface::dispatch(int _cmd, const QByteArray &rawdata)
{
Q_D(SlaveInterface);
//kDebug(7007) << "dispatch " << _cmd;
QDataStream stream(rawdata);
QString str1;
qint32 i;
qint8 b;
quint32 ul;
switch(_cmd) {
case MSG_DATA:
emit data(rawdata);
break;
case MSG_DATA_REQ:
emit dataReq();
break;
case MSG_OPENED:
emit open();
break;
case MSG_FINISHED:
//kDebug(7007) << "Finished [this = " << this << "]";
d->offset = 0;
d->speed_timer.stop();
emit finished();
break;
case MSG_STAT_ENTRY: {
UDSEntry entry;
stream >> entry;
emit statEntry(entry);
break;
}
case MSG_LIST_ENTRIES: {
quint32 count;
stream >> count;
UDSEntryList list;
UDSEntry entry;
for (uint i = 0; i < count; i++) {
stream >> entry;
list.append(entry);
}
emit listEntries(list);
break;
}
case MSG_RESUME: { // From the put job
d->offset = readFilesize_t(stream);
emit canResume(d->offset);
break;
}
case MSG_CANRESUME: // From the get job
d->filesize = d->offset;
emit canResume(0); // the arg doesn't matter
break;
case MSG_ERROR:
stream >> i >> str1;
kDebug(7007) << "error " << i << " " << str1;
emit error(i, str1);
break;
case MSG_SLAVE_STATUS: {
PIDType<sizeof(pid_t)>::PID_t stream_pid;
pid_t pid;
QByteArray protocol;
stream >> stream_pid >> protocol >> str1 >> b;
pid = stream_pid;
emit slaveStatus(pid, protocol, str1, (b != 0));
break;
}
case MSG_CONNECTED:
emit connected();
break;
case MSG_WRITTEN: {
KIO::filesize_t size = readFilesize_t(stream);
emit written(size);
break;
}
case INF_TOTAL_SIZE: {
KIO::filesize_t size = readFilesize_t(stream);
gettimeofday(&d->start_time, 0);
d->last_time = 0;
d->filesize = d->offset;
d->sizes[0] = d->filesize - d->offset;
d->times[0] = 0;
d->nums = 1;
d->speed_timer.start(1000);
d->slave_calcs_speed = false;
emit totalSize(size);
break;
}
case INF_PROCESSED_SIZE: {
KIO::filesize_t size = readFilesize_t(stream);
emit processedSize( size );
d->filesize = size;
break;
}
case INF_POSITION: {
KIO::filesize_t pos = readFilesize_t(stream);
emit position(pos);
break;
}
case INF_SPEED:
stream >> ul;
d->slave_calcs_speed = true;
d->speed_timer.stop();
emit speed( ul );
break;
case INF_GETTING_FILE:
break;
case INF_ERROR_PAGE:
emit errorPage();
break;
case INF_REDIRECTION: {
QUrl url;
stream >> url;
emit redirection( url );
break;
}
case INF_MIME_TYPE:
stream >> str1;
emit mimeType(str1);
if (!d->connection->suspended())
d->connection->sendnow(CMD_NONE, QByteArray());
break;
case INF_WARNING:
stream >> str1;
emit warning(str1);
break;
case INF_MESSAGEBOX: {
kDebug(7007) << "needs a msg box";
QString text, caption, buttonYes, buttonNo, dontAskAgainName;
int type;
stream >> type >> text >> caption >> buttonYes >> buttonNo;
if (stream.atEnd()) {
messageBox(type, text, caption, buttonYes, buttonNo);
} else {
stream >> dontAskAgainName;
messageBox(type, text, caption, buttonYes, buttonNo, dontAskAgainName);
}
break;
}
case INF_INFOMESSAGE: {
QString msg;
stream >> msg;
emit infoMessage(msg);
break;
}
case INF_META_DATA: {
MetaData m;
stream >> m;
if (m.contains(QLatin1String("ssl_in_use"))) {
const QLatin1String ssl_("ssl_");
const MetaData constM = m;
for (MetaData::ConstIterator it = constM.lowerBound(ssl_); it != constM.constEnd(); ++it) {
if (it.key().startsWith(ssl_)) {
d->sslMetaData.insert(it.key(), it.value());
} else {
// we're past the ssl_* entries; remember that QMap is ordered.
break;
}
}
}
emit metaData(m);
break;
}
case MSG_NET_REQUEST: {
QString host;
QString slaveid;
stream >> host >> slaveid;
requestNetwork(host, slaveid);
break;
}
case MSG_NET_DROP: {
QString host;
QString slaveid;
stream >> host >> slaveid;
dropNetwork(host, slaveid);
break;
}
case MSG_NEED_SUBURL_DATA: {
emit needSubUrlData();
break;
}
case MSG_HOST_INFO_REQ: {
QString hostName;
stream >> hostName;
HostInfo::lookupHost(hostName, this, SLOT(slotHostInfo(QHostInfo)));
break;
}
default:
kWarning(7007) << "Slave sends unknown command (" << _cmd << "), dropping slave";
return false;
}
return true;
}
void SlaveInterface::setOffset( KIO::filesize_t o)
{
Q_D(SlaveInterface);
d->offset = o;
}
KIO::filesize_t SlaveInterface::offset() const
{
const Q_D(SlaveInterface);
return d->offset;
}
void SlaveInterface::requestNetwork(const QString &host, const QString &slaveid)
{
Q_D(SlaveInterface);
kDebug(7007) << "requestNetwork " << host << slaveid;
QByteArray packedArgs;
QDataStream stream( &packedArgs, QIODevice::WriteOnly );
stream << true;
d->connection->sendnow( INF_NETWORK_STATUS, packedArgs );
}
void SlaveInterface::dropNetwork(const QString &host, const QString &slaveid)
{
kDebug(7007) << "dropNetwork " << host << slaveid;
}
void SlaveInterface::sendResumeAnswer( bool resume )
{
Q_D(SlaveInterface);
kDebug(7007) << "ok for resuming:" << resume;
d->connection->sendnow( resume ? CMD_RESUMEANSWER : CMD_NONE, QByteArray() );
}
void SlaveInterface::messageBox( int type, const QString &text, const QString &_caption,
const QString &buttonYes, const QString &buttonNo )
{
messageBox( type, text, _caption, buttonYes, buttonNo, QString() );
}
void SlaveInterface::messageBox( int type, const QString &text, const QString &caption,
const QString &buttonYes, const QString &buttonNo, const QString &dontAskAgainName )
{
Q_D(SlaveInterface);
kDebug(7007) << "messageBox " << type << " " << text << " - " << caption << " " << dontAskAgainName;
QByteArray packedArgs;
QDataStream stream( &packedArgs, QIODevice::WriteOnly );
QPointer<SlaveInterface> me = this;
if (d->connection) d->connection->suspend();
int result = d->messageBox( type, text, caption, buttonYes, buttonNo, dontAskAgainName );
if ( me && d->connection ) // Don't do anything if deleted meanwhile
{
d->connection->resume();
kDebug(7007) << this << " SlaveInterface result=" << result;
stream << result;
d->connection->sendnow( CMD_MESSAGEBOXANSWER, packedArgs );
}
}
void SlaveInterface::setWindow (QWidget* window)
{
Q_D(SlaveInterface);
d->parentWindow = window;
}
QWidget* SlaveInterface::window() const
{
const Q_D(SlaveInterface);
return d->parentWindow;
}
int SlaveInterfacePrivate::messageBox(int type, const QString &text,
const QString &caption, const QString &buttonYes,
const QString &buttonNo, const QString &dontAskAgainName)
{
kDebug() << type << text << "caption=" << caption;
int result = -1;
KConfig *config = new KConfig("kioslaverc");
KMessageBox::setDontShowAgainConfig(config);
// SMELL: the braindead way to support button icons
KGuiItem buttonYesGui, buttonNoGui;
if (buttonYes == i18n("&Details"))
buttonYesGui = KGuiItem(buttonYes, "help-about");
else if (buttonYes == i18n("&Forever"))
buttonYesGui = KGuiItem(buttonYes, "flag-green");
else
buttonYesGui = KGuiItem(buttonYes);
if (buttonNo == i18n("Co&ntinue"))
buttonNoGui = KGuiItem(buttonNo, "arrow-right");
else if (buttonNo == i18n("&Current Session only"))
buttonNoGui = KGuiItem(buttonNo, "chronometer");
else
buttonNoGui = KGuiItem(buttonNo);
switch (type) {
case KIO::SlaveBase::QuestionYesNo:
result = KMessageBox::questionYesNo(
parentWindow, text, caption, buttonYesGui,
buttonNoGui, dontAskAgainName);
break;
case KIO::SlaveBase::WarningYesNo:
result = KMessageBox::warningYesNo(
parentWindow, text, caption, buttonYesGui,
buttonNoGui, dontAskAgainName);
break;
case KIO::SlaveBase::WarningContinueCancel:
result = KMessageBox::warningContinueCancel(
parentWindow, text, caption, buttonYesGui,
KStandardGuiItem::cancel(), dontAskAgainName);
break;
case KIO::SlaveBase::WarningYesNoCancel:
result = KMessageBox::warningYesNoCancel(
parentWindow, text, caption, buttonYesGui, buttonNoGui,
KStandardGuiItem::cancel(), dontAskAgainName);
break;
case KIO::SlaveBase::Information:
KMessageBox::information(parentWindow, text, caption, dontAskAgainName);
result = 1; // whatever
break;
case KIO::SlaveBase::SSLMessageBox:
{
KIO::MetaData meta = sslMetaData;
QPointer<KSslInfoDialog> kid (new KSslInfoDialog(parentWindow));
//### this is boilerplate code and appears in khtml_part.cpp almost unchanged!
QStringList sl = meta["ssl_peer_chain"].split('\x01', QString::SkipEmptyParts);
QList<QSslCertificate> certChain;
bool decodedOk = true;
foreach (const QString &s, sl) {
- certChain.append(QSslCertificate(s.toAscii())); //or is it toLocal8Bit or whatever?
+ certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
if (certChain.last().isNull()) {
decodedOk = false;
break;
}
}
if (decodedOk || true/*H4X*/) {
kid->setSslInfo(certChain,
meta["ssl_peer_ip"],
text, // the URL
meta["ssl_protocol_version"],
meta["ssl_cipher"],
meta["ssl_cipher_used_bits"].toInt(),
meta["ssl_cipher_bits"].toInt(),
KSslInfoDialog::errorsFromString(meta["ssl_cert_errors"]));
kDebug(7024) << "Showing SSL Info dialog";
kid->exec();
kDebug(7024) << "SSL Info dialog closed";
} else {
KMessageBox::information(0, i18n("The peer SSL certificate chain "
"appears to be corrupt."),
i18n("SSL"));
}
// KSslInfoDialog deletes itself (Qt::WA_DeleteOnClose).
result = 1; // whatever
delete kid;
break;
}
default:
kWarning(7024) << "Unknown type" << type;
result = 0;
break;
}
KMessageBox::setDontShowAgainConfig(0);
delete config;
return result;
}
void SlaveInterfacePrivate::slotHostInfo(const QHostInfo& info)
{
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << info.hostName() << info.addresses() << info.error() << info.errorString();
connection->send(CMD_HOST_INFO, data);
}
#include "moc_slaveinterface.cpp"
diff --git a/kio/kssl/kssl.cpp b/kio/kssl/kssl.cpp
index b3b29c3dcc..97d3c8b91a 100644
--- a/kio/kssl/kssl.cpp
+++ b/kio/kssl/kssl.cpp
@@ -1,213 +1,213 @@
/* This file is part of the KDE project
*
* Copyright (C) 2000-2003 George Staikos <staikos@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.
*/
#include "kssl.h"
#include <ksslconfig.h>
// this hack provided by Malte Starostik to avoid glibc/openssl bug
// on some systems
#if KSSL_HAVE_SSL
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define crypt _openssl_crypt
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#undef crypt
#endif
#include <kdebug.h>
#include <kopenssl.h>
#include <ksslx509v3.h>
#include <ksslcertificate.h>
#include <klocalizedstring.h>
#include <QtNetwork/QAbstractSocket>
#ifdef __GNUC__
#warning "kssl.cc contains temporary functions! Clean up"
#warning "kssl.cc needs to be ported to QSslSocket"
#endif
#define sk_dup d->kossl->sk_dup
class KSSLPrivate {
public:
KSSLPrivate() {
kossl = KOpenSSLProxy::self();
}
~KSSLPrivate() {}
KSSLCertificate::KSSLValidation m_cert_vfy_res;
#if KSSL_HAVE_SSL
SSL *m_ssl;
SSL_CTX *m_ctx;
SSL_METHOD *m_meth;
#endif
KOSSL *kossl;
};
KSSL::KSSL(bool init) {
d = new KSSLPrivate;
m_bInit = false;
m_bAutoReconfig = true;
m_cfg = new KSSLSettings();
#if KSSL_HAVE_SSL
d->m_ssl = 0L;
#endif
if (init)
initialize();
}
KSSL::~KSSL() {
close();
delete m_cfg;
delete d;
}
int KSSL::seedWithEGD() {
int rc = 0;
#if KSSL_HAVE_SSL
if (m_cfg->useEGD() && !m_cfg->getEGDPath().isEmpty()) {
rc = d->kossl->RAND_egd(m_cfg->getEGDPath().toLatin1().constData());
if (rc < 0)
kDebug(7029) << "KSSL: Error seeding PRNG with the EGD.";
else kDebug(7029) << "KSSL: PRNG was seeded with " << rc
<< " bytes from the EGD." << endl;
} else if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) {
rc = d->kossl->RAND_load_file(m_cfg->getEGDPath().toLatin1().constData(), -1);
if (rc < 0)
kDebug(7029) << "KSSL: Error seeding PRNG with the entropy file.";
else kDebug(7029) << "KSSL: PRNG was seeded with " << rc
<< " bytes from the entropy file." << endl;
}
#endif
return rc;
}
bool KSSL::initialize() {
#if KSSL_HAVE_SSL
kDebug(7029) << "KSSL initialize";
if (m_bInit)
return false;
if (m_bAutoReconfig)
m_cfg->load();
seedWithEGD();
d->m_meth = d->kossl->SSLv23_client_method();
d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth);
if (d->m_ctx == 0L) {
return false;
}
// set cipher list
QString clist = m_cfg->getCipherList();
kDebug(7029) << "Cipher list: " << clist;
if (!clist.isEmpty())
- d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.toAscii().constData()));
+ d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.toLatin1().constData()));
m_bInit = true;
return true;
#else
return false;
#endif
}
void KSSL::close() {
#if KSSL_HAVE_SSL
//kDebug(7029) << "KSSL close";
if (!m_bInit)
return;
if (d->m_ssl) {
d->kossl->SSL_shutdown(d->m_ssl);
d->kossl->SSL_free(d->m_ssl);
d->m_ssl = 0L;
}
d->kossl->SSL_CTX_free(d->m_ctx);
if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) {
d->kossl->RAND_write_file(m_cfg->getEGDPath().toLatin1().constData());
}
m_bInit = false;
#endif
}
bool KSSL::reInitialize() {
close();
return initialize();
}
// get the callback file - it's hidden away in here
//#include "ksslcallback.c"
bool KSSL::reconfig() {
return reInitialize();
}
void KSSL::setAutoReconfig(bool ar) {
m_bAutoReconfig = ar;
}
bool KSSL::setSettings(KSSLSettings *settings) {
delete m_cfg;
m_cfg = settings;
return reconfig();
}
KSSLSettings * KSSL::settings()
{
return m_cfg;
}
#if KSSL_HAVE_SSL
bool KSSL::m_bSSLWorks = true;
#else
bool KSSL::m_bSSLWorks = false;
#endif
bool KSSL::doesSSLWork() {
return m_bSSLWorks;
}
#undef sk_dup
diff --git a/kio/kssl/ksslcertificate.cpp b/kio/kssl/ksslcertificate.cpp
index f2e3d92b0b..5bcde03870 100644
--- a/kio/kssl/ksslcertificate.cpp
+++ b/kio/kssl/ksslcertificate.cpp
@@ -1,1345 +1,1345 @@
/* This file is part of the KDE project
*
* Copyright (C) 2000-2003 George Staikos <staikos@kde.org>
* 2008 Richard Hartmann <richih-kde@net.in.tum.de>
*
* 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 "ksslcertificate.h"
#include <ksslconfig.h>
#include <unistd.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QFile>
#include "kssldefs.h"
#include "ksslcertchain.h"
#include "ksslutils.h"
#include <kstandarddirs.h>
#include <kde_file.h>
#include <klocalizedstring.h>
#include <QtCore/QDate>
#include <qtemporaryfile.h>
#include <sys/types.h>
#include <config-kio.h>
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
// this hack provided by Malte Starostik to avoid glibc/openssl bug
// on some systems
#if KSSL_HAVE_SSL
#define crypt _openssl_crypt
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#undef crypt
#endif
#include <kopenssl.h>
#include <kdebug.h>
#include "ksslx509v3.h"
static char hv[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
class KSSLCertificatePrivate {
public:
KSSLCertificatePrivate() {
kossl = KOSSL::self();
_lastPurpose = KSSLCertificate::None;
}
~KSSLCertificatePrivate() {
}
KSSLCertificate::KSSLValidation m_stateCache;
bool m_stateCached;
#if KSSL_HAVE_SSL
X509 *m_cert;
#endif
KOSSL *kossl;
KSSLCertChain _chain;
KSSLX509V3 _extensions;
KSSLCertificate::KSSLPurpose _lastPurpose;
};
KSSLCertificate::KSSLCertificate() {
d = new KSSLCertificatePrivate;
d->m_stateCached = false;
KGlobal::dirs()->addResourceType("kssl", "data", "kssl");
#if KSSL_HAVE_SSL
d->m_cert = NULL;
#endif
}
KSSLCertificate::KSSLCertificate(const KSSLCertificate& x) {
d = new KSSLCertificatePrivate;
d->m_stateCached = false;
KGlobal::dirs()->addResourceType("kssl", "data", "kssl");
#if KSSL_HAVE_SSL
d->m_cert = NULL;
setCert(KOSSL::self()->X509_dup(const_cast<KSSLCertificate&>(x).getCert()));
KSSLCertChain *c = x.d->_chain.replicate();
setChain(c->rawChain());
delete c;
#endif
}
KSSLCertificate::~KSSLCertificate() {
#if KSSL_HAVE_SSL
if (d->m_cert) {
d->kossl->X509_free(d->m_cert);
}
#endif
delete d;
}
KSSLCertChain& KSSLCertificate::chain() {
return d->_chain;
}
KSSLCertificate *KSSLCertificate::fromX509(X509 *x5) {
KSSLCertificate *n = NULL;
#if KSSL_HAVE_SSL
if (x5) {
n = new KSSLCertificate;
n->setCert(KOSSL::self()->X509_dup(x5));
}
#endif
return n;
}
KSSLCertificate *KSSLCertificate::fromString(const QByteArray &cert) {
KSSLCertificate *n = NULL;
#if KSSL_HAVE_SSL
if (cert.isEmpty()) {
return NULL;
}
QByteArray qba = QByteArray::fromBase64(cert);
unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data());
X509 *x5c = KOSSL::self()->d2i_X509(NULL, &qbap, qba.size());
if (!x5c) {
return NULL;
}
n = new KSSLCertificate;
n->setCert(x5c);
#endif
return n;
}
QString KSSLCertificate::getSubject() const {
QString rc = "";
#if KSSL_HAVE_SSL
char *t = d->kossl->X509_NAME_oneline(d->kossl->X509_get_subject_name(d->m_cert), 0, 0);
if (!t) {
return rc;
}
rc = t;
d->kossl->OPENSSL_free(t);
#endif
return rc;
}
QString KSSLCertificate::getSerialNumber() const {
QString rc = "";
#if KSSL_HAVE_SSL
ASN1_INTEGER *aint = d->kossl->X509_get_serialNumber(d->m_cert);
if (aint) {
rc = ASN1_INTEGER_QString(aint);
// d->kossl->ASN1_INTEGER_free(aint); this makes the sig test fail
}
#endif
return rc;
}
QString KSSLCertificate::getSignatureText() const {
QString rc = "";
#if KSSL_HAVE_SSL
char *s;
int n, i;
i = d->kossl->OBJ_obj2nid(d->m_cert->sig_alg->algorithm);
rc = i18n("Signature Algorithm: ");
rc += (i == NID_undef)?i18n("Unknown"):QString(d->kossl->OBJ_nid2ln(i));
rc += '\n';
rc += i18n("Signature Contents:");
n = d->m_cert->signature->length;
s = (char *)d->m_cert->signature->data;
for (i = 0; i < n; ++i) {
if (i%20 != 0) {
rc += ':';
}
else {
rc += '\n';
}
rc.append(QChar(hv[(s[i]&0xf0)>>4]));
rc.append(QChar(hv[s[i]&0x0f]));
}
#endif
return rc;
}
void KSSLCertificate::getEmails(QStringList &to) const {
to.clear();
#if KSSL_HAVE_SSL
if (!d->m_cert) {
return;
}
STACK *s = d->kossl->X509_get1_email(d->m_cert);
if (s) {
for(int n=0; n < s->num; n++) {
to.append(d->kossl->sk_value(s,n));
}
d->kossl->X509_email_free(s);
}
#endif
}
QString KSSLCertificate::getKDEKey() const {
return getSubject() + " (" + getMD5DigestText() + ')';
}
QString KSSLCertificate::getMD5DigestFromKDEKey(const QString &k) {
QString rc;
int pos = k.lastIndexOf('(');
if (pos != -1) {
unsigned int len = k.length();
if (k.at(len-1) == ')') {
rc = k.mid(pos+1, len-pos-2);
}
}
return rc;
}
QString KSSLCertificate::getMD5DigestText() const {
QString rc = "";
#if KSSL_HAVE_SSL
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];
if (!d->kossl->X509_digest(d->m_cert, d->kossl->EVP_md5(), md, &n)) {
return rc;
}
for (unsigned int j = 0; j < n; j++) {
if (j > 0) {
rc += ':';
}
rc.append(QChar(hv[(md[j]&0xf0)>>4]));
rc.append(QChar(hv[md[j]&0x0f]));
}
#endif
return rc;
}
QString KSSLCertificate::getMD5Digest() const {
QString rc = "";
#if KSSL_HAVE_SSL
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];
if (!d->kossl->X509_digest(d->m_cert, d->kossl->EVP_md5(), md, &n)) {
return rc;
}
for (unsigned int j = 0; j < n; j++) {
rc.append(QLatin1Char(hv[(md[j]&0xf0)>>4]));
rc.append(QLatin1Char(hv[md[j]&0x0f]));
}
#endif
return rc;
}
QString KSSLCertificate::getKeyType() const {
QString rc = "";
#if KSSL_HAVE_SSL
EVP_PKEY *pkey = d->kossl->X509_get_pubkey(d->m_cert);
if (pkey) {
#ifndef NO_RSA
if (pkey->type == EVP_PKEY_RSA) {
rc = "RSA";
}
else
#endif
#ifndef NO_DSA
if (pkey->type == EVP_PKEY_DSA) {
rc = "DSA";
}
else
#endif
rc = "Unknown";
d->kossl->EVP_PKEY_free(pkey);
}
#endif
return rc;
}
QString KSSLCertificate::getPublicKeyText() const {
QString rc = "";
char *x = NULL;
#if KSSL_HAVE_SSL
EVP_PKEY *pkey = d->kossl->X509_get_pubkey(d->m_cert);
if (pkey) {
rc = i18nc("Unknown", "Unknown key algorithm");
#ifndef NO_RSA
if (pkey->type == EVP_PKEY_RSA) {
x = d->kossl->BN_bn2hex(pkey->pkey.rsa->n);
rc = i18n("Key type: RSA (%1 bit)", strlen(x)*4) + '\n';
rc += i18n("Modulus: ");
for (unsigned int i = 0; i < strlen(x); i++) {
if (i%40 != 0 && i%2 == 0) {
rc += ':';
}
else if (i%40 == 0) {
rc += '\n';
}
rc += x[i];
}
rc += '\n';
d->kossl->OPENSSL_free(x);
x = d->kossl->BN_bn2hex(pkey->pkey.rsa->e);
rc += i18n("Exponent: 0x") + QLatin1String(x) +
QLatin1String("\n");
d->kossl->OPENSSL_free(x);
}
#endif
#ifndef NO_DSA
if (pkey->type == EVP_PKEY_DSA) {
x = d->kossl->BN_bn2hex(pkey->pkey.dsa->p);
// hack - this may not be always accurate
rc = i18n("Key type: DSA (%1 bit)", strlen(x)*4) + '\n';
rc += i18n("Prime: ");
for (unsigned int i = 0; i < strlen(x); i++) {
if (i%40 != 0 && i%2 == 0) {
rc += ':';
}
else if (i%40 == 0) {
rc += '\n';
}
rc += x[i];
}
rc += '\n';
d->kossl->OPENSSL_free(x);
x = d->kossl->BN_bn2hex(pkey->pkey.dsa->q);
rc += i18n("160 bit prime factor: ");
for (unsigned int i = 0; i < strlen(x); i++) {
if (i%40 != 0 && i%2 == 0) {
rc += ':';
}
else if (i%40 == 0) {
rc += '\n';
}
rc += x[i];
}
rc += '\n';
d->kossl->OPENSSL_free(x);
x = d->kossl->BN_bn2hex(pkey->pkey.dsa->g);
rc += QString("g: ");
for (unsigned int i = 0; i < strlen(x); i++) {
if (i%40 != 0 && i%2 == 0) {
rc += ':';
}
else if (i%40 == 0) {
rc += '\n';
}
rc += x[i];
}
rc += '\n';
d->kossl->OPENSSL_free(x);
x = d->kossl->BN_bn2hex(pkey->pkey.dsa->pub_key);
rc += i18n("Public key: ");
for (unsigned int i = 0; i < strlen(x); i++) {
if (i%40 != 0 && i%2 == 0) {
rc += ':';
}
else if (i%40 == 0) {
rc += '\n';
}
rc += x[i];
}
rc += '\n';
d->kossl->OPENSSL_free(x);
}
#endif
d->kossl->EVP_PKEY_free(pkey);
}
#endif
return rc;
}
QString KSSLCertificate::getIssuer() const {
QString rc = "";
#if KSSL_HAVE_SSL
char *t = d->kossl->X509_NAME_oneline(d->kossl->X509_get_issuer_name(d->m_cert), 0, 0);
if (!t) {
return rc;
}
rc = t;
d->kossl->OPENSSL_free(t);
#endif
return rc;
}
void KSSLCertificate::setChain(void *c) {
#if KSSL_HAVE_SSL
d->_chain.setChain(c);
#endif
d->m_stateCached = false;
d->m_stateCache = KSSLCertificate::Unknown;
}
void KSSLCertificate::setCert(X509 *c) {
#if KSSL_HAVE_SSL
d->m_cert = c;
if (c) {
d->_extensions.flags = 0;
d->kossl->X509_check_purpose(c, -1, 0); // setup the fields (!!)
#if 0
kDebug(7029) << "---------------- Certificate ------------------"
<< endl;
kDebug(7029) << getSubject();
#endif
for (int j = 0; j < d->kossl->X509_PURPOSE_get_count(); j++) {
X509_PURPOSE *ptmp = d->kossl->X509_PURPOSE_get0(j);
int id = d->kossl->X509_PURPOSE_get_id(ptmp);
for (int ca = 0; ca < 2; ca++) {
int idret = d->kossl->X509_check_purpose(c, id, ca);
if (idret == 1 || idret == 2) { // have it
// kDebug() << "PURPOSE: " << id << (ca?" CA":"");
if (!ca) {
d->_extensions.flags |= (1L <<(id-1));
}
else d->_extensions.flags |= (1L <<(16+id-1));
} else {
if (!ca) {
d->_extensions.flags &= ~(1L <<(id-1));
}
else d->_extensions.flags &= ~(1L <<(16+id-1));
}
}
}
#if 0
kDebug(7029) << "flags: " << QString::number(c->ex_flags, 2)
<< "\nkeyusage: " << QString::number(c->ex_kusage, 2)
<< "\nxkeyusage: " << QString::number(c->ex_xkusage, 2)
<< "\nnscert: " << QString::number(c->ex_nscert, 2)
<< endl;
if (c->ex_flags & EXFLAG_KUSAGE)
kDebug(7029) << " --- Key Usage extensions found";
else kDebug(7029) << " --- Key Usage extensions NOT found";
if (c->ex_flags & EXFLAG_XKUSAGE)
kDebug(7029) << " --- Extended key usage extensions found";
else kDebug(7029) << " --- Extended key usage extensions NOT found";
if (c->ex_flags & EXFLAG_NSCERT)
kDebug(7029) << " --- NS extensions found";
else kDebug(7029) << " --- NS extensions NOT found";
if (d->_extensions.certTypeSSLCA())
kDebug(7029) << "NOTE: this is an SSL CA file.";
else kDebug(7029) << "NOTE: this is NOT an SSL CA file.";
if (d->_extensions.certTypeEmailCA())
kDebug(7029) << "NOTE: this is an EMAIL CA file.";
else kDebug(7029) << "NOTE: this is NOT an EMAIL CA file.";
if (d->_extensions.certTypeCodeCA())
kDebug(7029) << "NOTE: this is a CODE CA file.";
else kDebug(7029) << "NOTE: this is NOT a CODE CA file.";
if (d->_extensions.certTypeSSLClient())
kDebug(7029) << "NOTE: this is an SSL client.";
else kDebug(7029) << "NOTE: this is NOT an SSL client.";
if (d->_extensions.certTypeSSLServer())
kDebug(7029) << "NOTE: this is an SSL server.";
else kDebug(7029) << "NOTE: this is NOT an SSL server.";
if (d->_extensions.certTypeNSSSLServer())
kDebug(7029) << "NOTE: this is a NETSCAPE SSL server.";
else kDebug(7029) << "NOTE: this is NOT a NETSCAPE SSL server.";
if (d->_extensions.certTypeSMIME())
kDebug(7029) << "NOTE: this is an SMIME certificate.";
else kDebug(7029) << "NOTE: this is NOT an SMIME certificate.";
if (d->_extensions.certTypeSMIMEEncrypt())
kDebug(7029) << "NOTE: this is an SMIME encrypt cert.";
else kDebug(7029) << "NOTE: this is NOT an SMIME encrypt cert.";
if (d->_extensions.certTypeSMIMESign())
kDebug(7029) << "NOTE: this is an SMIME sign cert.";
else kDebug(7029) << "NOTE: this is NOT an SMIME sign cert.";
if (d->_extensions.certTypeCRLSign())
kDebug(7029) << "NOTE: this is a CRL signer.";
else kDebug(7029) << "NOTE: this is NOT a CRL signer.";
kDebug(7029) << "-----------------------------------------------"
<< endl;
#endif
}
#endif
d->m_stateCached = false;
d->m_stateCache = KSSLCertificate::Unknown;
}
X509 *KSSLCertificate::getCert() {
#if KSSL_HAVE_SSL
return d->m_cert;
#endif
return 0;
}
// pull in the callback. It's common across multiple files but we want
// it to be hidden.
#include "ksslcallback.c"
bool KSSLCertificate::isValid(KSSLCertificate::KSSLPurpose p) {
return (validate(p) == KSSLCertificate::Ok);
}
bool KSSLCertificate::isValid() {
return isValid(KSSLCertificate::SSLServer);
}
int KSSLCertificate::purposeToOpenSSL(KSSLCertificate::KSSLPurpose p) const {
int rc = 0;
#if KSSL_HAVE_SSL
if (p == KSSLCertificate::SSLServer) {
rc = X509_PURPOSE_SSL_SERVER;
} else if (p == KSSLCertificate::SSLClient) {
rc = X509_PURPOSE_SSL_CLIENT;
} else if (p == KSSLCertificate::SMIMEEncrypt) {
rc = X509_PURPOSE_SMIME_ENCRYPT;
} else if (p == KSSLCertificate::SMIMESign) {
rc = X509_PURPOSE_SMIME_SIGN;
} else if (p == KSSLCertificate::Any) {
rc = X509_PURPOSE_ANY;
}
#endif
return rc;
}
// For backward compatibility
KSSLCertificate::KSSLValidation KSSLCertificate::validate() {
return validate(KSSLCertificate::SSLServer);
}
KSSLCertificate::KSSLValidation KSSLCertificate::validate(KSSLCertificate::KSSLPurpose purpose)
{
KSSLValidationList result = validateVerbose(purpose);
if (result.isEmpty()) {
return KSSLCertificate::Ok;
}
else
return result.first();
}
//
// See apps/verify.c in OpenSSL for the source of most of this logic.
//
// CRL files? we don't do that yet
KSSLCertificate::KSSLValidationList KSSLCertificate::validateVerbose(KSSLCertificate::KSSLPurpose purpose)
{
return validateVerbose(purpose, 0);
}
KSSLCertificate::KSSLValidationList KSSLCertificate::validateVerbose(KSSLCertificate::KSSLPurpose purpose, KSSLCertificate *ca)
{
KSSLValidationList errors;
if (ca || (d->_lastPurpose != purpose)) {
d->m_stateCached = false;
}
if (!d->m_stateCached) {
d->_lastPurpose = purpose;
}
#if KSSL_HAVE_SSL
X509_STORE *certStore;
X509_LOOKUP *certLookup;
X509_STORE_CTX *certStoreCTX;
int rc = 0;
if (!d->m_cert) {
errors << KSSLCertificate::Unknown;
return errors;
}
if (d->m_stateCached) {
errors << d->m_stateCache;
return errors;
}
const QStringList qsl = KGlobal::dirs()->resourceDirs("kssl");
if (qsl.isEmpty()) {
errors << KSSLCertificate::NoCARoot;
return errors;
}
KSSLCertificate::KSSLValidation ksslv = Unknown;
for (QStringList::ConstIterator j = qsl.begin(); j != qsl.end(); ++j) {
KDE_struct_stat sb;
QString _j = (*j) + "ca-bundle.crt";
- if (-1 == KDE_stat(_j.toAscii().constData(), &sb)) {
+ if (-1 == KDE_stat(_j.toLatin1().constData(), &sb)) {
continue;
}
certStore = d->kossl->X509_STORE_new();
if (!certStore) {
errors << KSSLCertificate::Unknown;
return errors;
}
X509_STORE_set_verify_cb_func(certStore, X509Callback);
certLookup = d->kossl->X509_STORE_add_lookup(certStore, d->kossl->X509_LOOKUP_file());
if (!certLookup) {
ksslv = KSSLCertificate::Unknown;
d->kossl->X509_STORE_free(certStore);
continue;
}
- if (!d->kossl->X509_LOOKUP_load_file(certLookup, _j.toAscii().constData(), X509_FILETYPE_PEM)) {
+ if (!d->kossl->X509_LOOKUP_load_file(certLookup, _j.toLatin1().constData(), X509_FILETYPE_PEM)) {
// error accessing directory and loading pems
kDebug(7029) << "KSSL couldn't read CA root: "
<< _j << endl;
ksslv = KSSLCertificate::ErrorReadingRoot;
d->kossl->X509_STORE_free(certStore);
continue;
}
// This is the checking code
certStoreCTX = d->kossl->X509_STORE_CTX_new();
// this is a bad error - could mean no free memory.
// This may be the wrong thing to do here
if (!certStoreCTX) {
kDebug(7029) << "KSSL couldn't create an X509 store context.";
d->kossl->X509_STORE_free(certStore);
continue;
}
d->kossl->X509_STORE_CTX_init(certStoreCTX, certStore, d->m_cert, NULL);
if (d->_chain.isValid()) {
d->kossl->X509_STORE_CTX_set_chain(certStoreCTX, (STACK_OF(X509)*)d->_chain.rawChain());
}
//kDebug(7029) << "KSSL setting CRL..............";
// int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x);
d->kossl->X509_STORE_CTX_set_purpose(certStoreCTX, purposeToOpenSSL(purpose));
KSSL_X509CallBack_ca = ca ? ca->d->m_cert : 0;
KSSL_X509CallBack_ca_found = false;
certStoreCTX->error = X509_V_OK;
rc = d->kossl->X509_verify_cert(certStoreCTX);
int errcode = certStoreCTX->error;
if (ca && !KSSL_X509CallBack_ca_found) {
ksslv = KSSLCertificate::Irrelevant;
} else {
ksslv = processError(errcode);
}
// For servers, we can try NS_SSL_SERVER too
if ((ksslv != KSSLCertificate::Ok) &&
(ksslv != KSSLCertificate::Irrelevant) &&
purpose == KSSLCertificate::SSLServer) {
d->kossl->X509_STORE_CTX_set_purpose(certStoreCTX,
X509_PURPOSE_NS_SSL_SERVER);
certStoreCTX->error = X509_V_OK;
rc = d->kossl->X509_verify_cert(certStoreCTX);
errcode = certStoreCTX->error;
ksslv = processError(errcode);
}
d->kossl->X509_STORE_CTX_free(certStoreCTX);
d->kossl->X509_STORE_free(certStore);
// end of checking code
//
//kDebug(7029) << "KSSL Validation procedure RC: "
// << rc << endl;
//kDebug(7029) << "KSSL Validation procedure errcode: "
// << errcode << endl;
//kDebug(7029) << "KSSL Validation procedure RESULTS: "
// << ksslv << endl;
if (ksslv != NoCARoot && ksslv != InvalidCA && ksslv != GetIssuerCertFailed && ksslv != DecodeIssuerPublicKeyFailed && ksslv != GetIssuerCertLocallyFailed ) {
d->m_stateCached = true;
d->m_stateCache = ksslv;
}
break;
}
if (ksslv != KSSLCertificate::Ok) {
errors << ksslv;
}
#else
errors << KSSLCertificate::NoSSL;
#endif
return errors;
}
KSSLCertificate::KSSLValidation KSSLCertificate::revalidate() {
return revalidate(KSSLCertificate::SSLServer);
}
KSSLCertificate::KSSLValidation KSSLCertificate::revalidate(KSSLCertificate::KSSLPurpose p) {
d->m_stateCached = false;
return validate(p);
}
KSSLCertificate::KSSLValidation KSSLCertificate::processError(int ec) {
KSSLCertificate::KSSLValidation rc;
rc = KSSLCertificate::Unknown;
#if KSSL_HAVE_SSL
switch (ec) {
// see man 1 verify for a detailed listing of all error codes
// error 0
case X509_V_OK:
rc = KSSLCertificate::Ok;
break;
// error 2
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
rc = KSSLCertificate::GetIssuerCertFailed;
break;
// error 3
case X509_V_ERR_UNABLE_TO_GET_CRL:
rc = KSSLCertificate::GetCRLFailed;
break;
// error 4
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
rc = KSSLCertificate::DecryptCertificateSignatureFailed;
break;
// error 5
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
rc = KSSLCertificate::DecryptCRLSignatureFailed;
break;
// error 6
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
rc = KSSLCertificate::DecodeIssuerPublicKeyFailed;
break;
// error 7
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
rc = KSSLCertificate::CertificateSignatureFailed;
break;
// error 8
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
rc = KSSLCertificate::CRLSignatureFailed;
break;
// error 9
case X509_V_ERR_CERT_NOT_YET_VALID:
rc = KSSLCertificate::CertificateNotYetValid;
break;
// error 10
case X509_V_ERR_CERT_HAS_EXPIRED:
rc = KSSLCertificate::CertificateHasExpired;
kDebug(7029) << "KSSL apparently this is expired. Not after: "
<< getNotAfter() << endl;
break;
// error 11
case X509_V_ERR_CRL_NOT_YET_VALID:
rc = KSSLCertificate::CRLNotYetValid;
break;
// error 12
case X509_V_ERR_CRL_HAS_EXPIRED:
rc = KSSLCertificate::CRLHasExpired;
break;
// error 13
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
rc = KSSLCertificate::CertificateFieldNotBeforeErroneous;
break;
// error 14
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
rc = KSSLCertificate::CertificateFieldNotAfterErroneous;
break;
// error 15 - unused as of OpenSSL 0.9.8g
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
rc = KSSLCertificate::CRLFieldLastUpdateErroneous;
break;
// error 16 - unused as of OpenSSL 0.9.8g
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
rc = KSSLCertificate::CRLFieldNextUpdateErroneous;
break;
// error 17
case X509_V_ERR_OUT_OF_MEM:
rc = KSSLCertificate::OutOfMemory;
break;
// error 18
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
rc = KSSLCertificate::SelfSigned;
break;
// error 19
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
rc = KSSLCertificate::SelfSignedInChain;
break;
// error 20
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
rc = KSSLCertificate::GetIssuerCertLocallyFailed;
break;
// error 21
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
rc = KSSLCertificate::VerifyLeafSignatureFailed;
break;
// error 22 - unused as of OpenSSL 0.9.8g
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
rc = KSSLCertificate::CertificateChainTooLong;
break;
// error 23 - unused as of OpenSSL 0.9.8g
case X509_V_ERR_CERT_REVOKED:
rc = KSSLCertificate::CertificateRevoked;
break;
// error 24
case X509_V_ERR_INVALID_CA:
rc = KSSLCertificate::InvalidCA;
break;
// error 25
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
rc = KSSLCertificate::PathLengthExceeded;
break;
// error 26
case X509_V_ERR_INVALID_PURPOSE:
rc = KSSLCertificate::InvalidPurpose;
break;
// error 27
case X509_V_ERR_CERT_UNTRUSTED:
rc = KSSLCertificate::CertificateUntrusted;
break;
// error 28
case X509_V_ERR_CERT_REJECTED:
rc = KSSLCertificate::CertificateRejected;
break;
// error 29 - only used with -issuer_checks
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
rc = KSSLCertificate::IssuerSubjectMismatched;
break;
// error 30 - only used with -issuer_checks
case X509_V_ERR_AKID_SKID_MISMATCH:
rc = KSSLCertificate::AuthAndSubjectKeyIDMismatched;
break;
// error 31 - only used with -issuer_checks
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
rc = KSSLCertificate::AuthAndSubjectKeyIDAndNameMismatched;
break;
// error 32
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
rc = KSSLCertificate::KeyMayNotSignCertificate;
break;
// error 50 - unused as of OpenSSL 0.9.8g
case X509_V_ERR_APPLICATION_VERIFICATION:
rc = KSSLCertificate::ApplicationVerificationFailed;
break;
default:
rc = KSSLCertificate::Unknown;
break;
}
d->m_stateCache = rc;
d->m_stateCached = true;
#endif
return rc;
}
QString KSSLCertificate::getNotBefore() const {
#if KSSL_HAVE_SSL
return ASN1_UTCTIME_QString(X509_get_notBefore(d->m_cert));
#else
return QString();
#endif
}
QString KSSLCertificate::getNotAfter() const {
#if KSSL_HAVE_SSL
return ASN1_UTCTIME_QString(X509_get_notAfter(d->m_cert));
#else
return QString();
#endif
}
QDateTime KSSLCertificate::getQDTNotBefore() const {
#if KSSL_HAVE_SSL
return ASN1_UTCTIME_QDateTime(X509_get_notBefore(d->m_cert), NULL);
#else
return QDateTime::currentDateTime();
#endif
}
QDateTime KSSLCertificate::getQDTNotAfter() const {
#if KSSL_HAVE_SSL
return ASN1_UTCTIME_QDateTime(X509_get_notAfter(d->m_cert), NULL);
#else
return QDateTime::currentDateTime();
#endif
}
int operator==(KSSLCertificate &x, KSSLCertificate &y) {
#ifndef KSSL_HAVE_SSL
return 1;
#else
if (!KOSSL::self()->X509_cmp(x.getCert(), y.getCert())) {
return 1;
}
return 0;
#endif
}
KSSLCertificate *KSSLCertificate::replicate() {
// The new certificate doesn't have the cached value. It's probably
// better this way. We can't anticipate every reason for doing this.
KSSLCertificate *newOne = new KSSLCertificate();
#if KSSL_HAVE_SSL
newOne->setCert(d->kossl->X509_dup(getCert()));
KSSLCertChain *c = d->_chain.replicate();
newOne->setChain(c->rawChain());
delete c;
#endif
return newOne;
}
QString KSSLCertificate::toString()
{
return toDer().toBase64();
}
QString KSSLCertificate::verifyText(KSSLValidation x) {
switch (x) {
// messages for errors defined in verify(1)
case KSSLCertificate::Ok:
return i18n("The certificate is valid.");
case KSSLCertificate::GetIssuerCertFailed:
return i18n("Retrieval of the issuer certificate failed. This means the CA's (Certificate Authority) certificate can not be found.");
case KSSLCertificate::GetCRLFailed:
return i18n("Retrieval of the CRL (Certificate Revocation List) failed. This means the CA's (Certificate Authority) CRL can not be found.");
case KSSLCertificate::DecryptCertificateSignatureFailed:
return i18n("The decryption of the certificate's signature failed. This means it could not even be calculated as opposed to just not matching the expected result.");
case KSSLCertificate::DecryptCRLSignatureFailed:
return i18n("The decryption of the CRL's (Certificate Revocation List) signature failed. This means it could not even be calculated as opposed to just not matching the expected result.");
case KSSLCertificate::DecodeIssuerPublicKeyFailed:
return i18n("The decoding of the public key of the issuer failed. This means that the CA's (Certificate Authority) certificate can not be used to verify the certificate you wanted to use.");
case KSSLCertificate::CertificateSignatureFailed:
return i18n("The certificate's signature is invalid. This means that the certificate can not be verified.");
case KSSLCertificate::CRLSignatureFailed:
return i18n("The CRL's (Certificate Revocation List) signature is invalid. This means that the CRL can not be verified.");
case KSSLCertificate::CertificateNotYetValid:
return i18n("The certificate is not valid, yet.");
case KSSLCertificate::CertificateHasExpired:
return i18n("The certificate is not valid, any more.");
case KSSLCertificate::CRLNotYetValid:
return i18n("The CRL (Certificate Revocation List) is not valid, yet.");
case KSSLCertificate::CRLHasExpired:
return i18n("The CRL (Certificate Revocation List) is not valid, yet.");
case KSSLCertificate::CertificateFieldNotBeforeErroneous:
return i18n("The time format of the certificate's 'notBefore' field is invalid.");
case KSSLCertificate::CertificateFieldNotAfterErroneous:
return i18n("The time format of the certificate's 'notAfter' field is invalid.");
case KSSLCertificate::CRLFieldLastUpdateErroneous:
return i18n("The time format of the CRL's (Certificate Revocation List) 'lastUpdate' field is invalid.");
case KSSLCertificate::CRLFieldNextUpdateErroneous:
return i18n("The time format of the CRL's (Certificate Revocation List) 'nextUpdate' field is invalid.");
case KSSLCertificate::OutOfMemory:
return i18n("The OpenSSL process ran out of memory.");
case KSSLCertificate::SelfSigned:
return i18n("The certificate is self-signed and not in the list of trusted certificates. If you want to accept this certificate, import it into the list of trusted certificates.");
case KSSLCertificate::SelfSignedChain: // this is obsolete and kept around for backwards compatibility, only
case KSSLCertificate::SelfSignedInChain:
return i18n("The certificate is self-signed. While the trust chain could be built up, the root CA's (Certificate Authority) certificate can not be found.");
case KSSLCertificate::GetIssuerCertLocallyFailed:
return i18n("The CA's (Certificate Authority) certificate can not be found. Most likely, your trust chain is broken.");
case KSSLCertificate::VerifyLeafSignatureFailed:
return i18n("The certificate can not be verified as it is the only certificate in the trust chain and not self-signed. If you self-sign the certificate, make sure to import it into the list of trusted certificates.");
case KSSLCertificate::CertificateChainTooLong:
return i18n("The certificate chain is longer than the maximum depth specified.");
case KSSLCertificate::Revoked: // this is obsolete and kept around for backwards compatibility, only
case KSSLCertificate::CertificateRevoked:
return i18n("The certificate has been revoked.");
case KSSLCertificate::InvalidCA:
return i18n("The certificate's CA (Certificate Authority) is invalid.");
case KSSLCertificate::PathLengthExceeded:
return i18n("The length of the trust chain exceeded one of the CA's (Certificate Authority) 'pathlength' parameters, making all subsequent signatures invalid.");
case KSSLCertificate::InvalidPurpose:
return i18n("The certificate has not been signed for the purpose you tried to use it for. This means the CA (Certificate Authority) does not allow this usage.");
case KSSLCertificate::Untrusted: // this is obsolete and kept around for backwards compatibility, only
case KSSLCertificate::CertificateUntrusted:
return i18n("The root CA (Certificate Authority) is not trusted for the purpose you tried to use this certificate for.");
case KSSLCertificate::Rejected: // this is obsolete and kept around for backwards compatibility, only // this is obsolete and kept around for backwards compatibility, onle
case KSSLCertificate::CertificateRejected:
return i18n("The root CA (Certificate Authority) has been marked to be rejected for the purpose you tried to use it for.");
case KSSLCertificate::IssuerSubjectMismatched:
return i18n("The certificate's CA (Certificate Authority) does not match the CA name of the certificate.");
case KSSLCertificate::AuthAndSubjectKeyIDMismatched:
return i18n("The CA (Certificate Authority) certificate's key ID does not match the key ID in the 'Issuer' section of the certificate you are trying to use.");
case KSSLCertificate::AuthAndSubjectKeyIDAndNameMismatched:
return i18n("The CA (Certificate Authority) certificate's key ID and name do not match the key ID and name in the 'Issuer' section of the certificate you are trying to use.");
case KSSLCertificate::KeyMayNotSignCertificate:
return i18n("The certificate's CA (Certificate Authority) is not allowed to sign certificates.");
case KSSLCertificate::ApplicationVerificationFailed:
return i18n("OpenSSL could not be verified.");
// this is obsolete and kept around for backwards compatibility, only
case KSSLCertificate::SignatureFailed:
return i18n("The signature test for this certificate failed. This could mean that the signature of this certificate or any in its trust path are invalid, could not be decoded or that the CRL (Certificate Revocation List) could not be verified. If you see this message, please let the author of the software you are using know that he or she should use the new, more specific error messages.");
case KSSLCertificate::Expired:
return i18n("This certificate, any in its trust path or its CA's (Certificate Authority) CRL (Certificate Revocation List) is not valid. Any of them could not be valid yet or not valid any more. If you see this message, please let the author of the software you are using know that he or she should use the new, more specific error messages.");
// continue 'useful' messages
// other error messages
case KSSLCertificate::ErrorReadingRoot:
case KSSLCertificate::NoCARoot:
return i18n("Certificate signing authority root files could not be found so the certificate is not verified.");
case KSSLCertificate::NoSSL:
return i18n("SSL support was not found.");
case KSSLCertificate::PrivateKeyFailed:
return i18n("Private key test failed.");
case KSSLCertificate::InvalidHost:
return i18n("The certificate has not been issued for this host.");
case KSSLCertificate::Irrelevant:
return i18n("This certificate is not relevant.");
default:
break;
}
return i18n("The certificate is invalid.");
}
QByteArray KSSLCertificate::toDer() {
QByteArray qba;
#if KSSL_HAVE_SSL
int certlen = d->kossl->i2d_X509(getCert(), NULL);
if (certlen >= 0) {
// These should technically be unsigned char * but it doesn't matter
// for our purposes
char *cert = new char[certlen];
unsigned char *p = (unsigned char *)cert;
// FIXME: return code!
d->kossl->i2d_X509(getCert(), &p);
// encode it into a QString
qba = QByteArray(cert, certlen);
delete[] cert;
}
#endif
return qba;
}
QByteArray KSSLCertificate::toPem() {
QByteArray qba;
QString thecert = toString();
const char *header = "-----BEGIN CERTIFICATE-----\n";
const char *footer = "-----END CERTIFICATE-----\n";
// We just do base64 on the ASN1
// 64 character lines (unpadded)
unsigned int xx = thecert.length() - 1;
for (unsigned int i = 0; i < xx/64; i++) {
thecert.insert(64*(i+1)+i, '\n');
}
thecert.prepend(header);
if (thecert[thecert.length()-1] != '\n') {
thecert += '\n';
}
thecert.append(footer);
qba = thecert.toLocal8Bit();
return qba;
}
#define NETSCAPE_CERT_HDR "certificate"
#if KSSL_HAVE_SSL
#if OPENSSL_VERSION_NUMBER < 0x00909000L
typedef struct NETSCAPE_X509_st
{
ASN1_OCTET_STRING *header;
X509 *cert;
} NETSCAPE_X509;
#endif
#endif
// what a piece of crap this is
QByteArray KSSLCertificate::toNetscape() {
QByteArray qba;
#if KSSL_HAVE_SSL
NETSCAPE_X509 nx;
ASN1_OCTET_STRING hdr;
QTemporaryFile ktf;
ktf.open();
FILE *ktf_fs = fopen(QFile::encodeName(ktf.fileName()), "r+");
hdr.data = (unsigned char *)NETSCAPE_CERT_HDR;
hdr.length = strlen(NETSCAPE_CERT_HDR);
nx.header = &hdr;
nx.cert = getCert();
d->kossl->ASN1_item_i2d_fp(ktf_fs,(unsigned char *)&nx);
fclose(ktf_fs);
QFile qf(ktf.fileName());
qf.open(QIODevice::ReadOnly);
char *buf = new char[qf.size()];
qf.read(buf, qf.size());
qba = QByteArray(buf, qf.size());
qf.close();
delete[] buf;
#endif
return qba;
}
QString KSSLCertificate::toText() {
QString text;
#if KSSL_HAVE_SSL
QTemporaryFile ktf;
ktf.open();
FILE *ktf_fs = fopen(QFile::encodeName(ktf.fileName()), "r+");
d->kossl->X509_print(ktf_fs, getCert());
fclose(ktf_fs);
QFile qf(ktf.fileName());
qf.open(QIODevice::ReadOnly);
char *buf = new char[qf.size()+1];
qf.read(buf, qf.size());
buf[qf.size()] = 0;
text = buf;
delete[] buf;
qf.close();
#endif
return text;
}
bool KSSLCertificate::setCert(const QString& cert) {
#if KSSL_HAVE_SSL
QByteArray qba, qbb = cert.toLocal8Bit();
qba = QByteArray::fromBase64(qbb);
unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data());
X509 *x5c = KOSSL::self()->d2i_X509(NULL, &qbap, qba.size());
if (x5c) {
setCert(x5c);
return true;
}
#endif
return false;
}
KSSLX509V3& KSSLCertificate::x509V3Extensions() {
return d->_extensions;
}
bool KSSLCertificate::isSigner() {
return d->_extensions.certTypeCA();
}
QStringList KSSLCertificate::subjAltNames() const {
QStringList rc;
#if KSSL_HAVE_SSL
STACK_OF(GENERAL_NAME) *names;
names = (STACK_OF(GENERAL_NAME)*)d->kossl->X509_get_ext_d2i(d->m_cert, NID_subject_alt_name, 0, 0);
if (!names) {
return rc;
}
int cnt = d->kossl->sk_GENERAL_NAME_num(names);
for (int i = 0; i < cnt; i++) {
const GENERAL_NAME *val = (const GENERAL_NAME *)d->kossl->sk_value(names, i);
if (val->type != GEN_DNS) {
continue;
}
QString s = (const char *)d->kossl->ASN1_STRING_data(val->d.ia5);
if (!s.isEmpty()) {
rc += s;
}
}
d->kossl->sk_free(names);
#endif
return rc;
}
QDataStream& operator<<(QDataStream& s, const KSSLCertificate& r) {
QStringList qsl;
QList<KSSLCertificate *> cl = const_cast<KSSLCertificate&>(r).chain().getChain();
foreach(KSSLCertificate *c, cl) {
qsl << c->toString();
}
qDeleteAll(cl);
s << const_cast<KSSLCertificate&>(r).toString() << qsl;
return s;
}
QDataStream& operator>>(QDataStream& s, KSSLCertificate& r) {
QStringList qsl;
QString cert;
s >> cert >> qsl;
if (r.setCert(cert) && !qsl.isEmpty()) {
r.chain().setCertChain(qsl);
}
return s;
}
diff --git a/kio/kssl/ksslx509map.cpp b/kio/kssl/ksslx509map.cpp
index 2669e290d9..fe72cdf913 100644
--- a/kio/kssl/ksslx509map.cpp
+++ b/kio/kssl/ksslx509map.cpp
@@ -1,106 +1,106 @@
/* This file is part of the KDE project
*
* Copyright (C) 2000 George Staikos <staikos@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.
*/
#include "ksslx509map.h"
#include <QtCore/QStringList>
#include <QtCore/QRegExp>
KSSLX509Map::KSSLX509Map(const QString& name) {
parse(name);
}
KSSLX509Map::~KSSLX509Map() {
}
void KSSLX509Map::setValue(const QString& key, const QString& value) {
m_pairs.insert(key, value);
}
QString KSSLX509Map::getValue(const QString& key) const {
if (!m_pairs.contains(key)) {
return QString();
}
return m_pairs[key];
}
static QStringList tokenizeBy(const QString& str, const QRegExp& tok, bool keepEmpties = false) {
QStringList tokens;
unsigned int head, tail;
-QByteArray bastr = str.toAscii();
+QByteArray bastr = str.toLatin1();
const char *chstr = bastr.constData();
unsigned int length = str.length();
if (length < 1) {
return tokens;
}
if (length == 1) {
tokens.append(str);
return tokens;
}
for(head = 0, tail = 0; tail < length-1; head = tail+1) {
QString thisline;
tail = str.indexOf(tok, head);
if (tail > length) // last token - none at end
tail = length;
if (tail-head > 0 || keepEmpties) { // it has to be at least 1 long!
thisline = &(chstr[head]);
thisline.truncate(tail-head);
tokens.append(thisline);
}
}
return tokens;
}
void KSSLX509Map::parse(const QString& name) {
const QStringList vl = tokenizeBy(name, QRegExp("/[A-Za-z]+="), false);
m_pairs.clear();
for (QStringList::ConstIterator j = vl.begin(); j != vl.end(); ++j) {
const QStringList apair = tokenizeBy(*j, QRegExp("="), false);
if( apair.count() >0 ) {
if (m_pairs.contains(apair[0])) {
QString oldValue = m_pairs[apair[0]];
oldValue += '\n';
oldValue += (apair.count()>1) ? apair[1]:"";
m_pairs.insert(apair[0], oldValue);
} else {
m_pairs.insert(apair[0], (apair.count()>1) ? apair[1]: "" );
}
}
}
}
void KSSLX509Map::reset(const QString& name) {
parse(name);
}
diff --git a/kio/misc/ksendbugmail/smtp.cpp b/kio/misc/ksendbugmail/smtp.cpp
index be728c1a04..f7f9ad0d33 100644
--- a/kio/misc/ksendbugmail/smtp.cpp
+++ b/kio/misc/ksendbugmail/smtp.cpp
@@ -1,340 +1,340 @@
/*
Copyright (c) 2000 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
Copyright (c) 2000 Stephan Kulow <coolo@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "smtp.h"
#include <sys/utsname.h>
#include <unistd.h>
#include <stdio.h>
#include <kdebug.h>
SMTP::SMTP(char *serverhost, unsigned short int port, int timeout)
{
struct utsname uts;
serverHost = serverhost;
hostPort = port;
timeOut = timeout * 1000;
senderAddress = "user@example.net";
recipientAddress = "user@example.net";
messageSubject = "(no subject)";
messageBody = "empty";
messageHeader = "";
connected = false;
finished = false;
sock = 0L;
state = Init;
serverState = None;
uname(&uts);
domainName = uts.nodename;
if(domainName.isEmpty())
domainName = "somemachine.example.net";
kDebug() << "SMTP object created";
connect(&connectTimer, SIGNAL(timeout()), this, SLOT(connectTimerTick()));
connect(&timeOutTimer, SIGNAL(timeout()), this, SLOT(connectTimedOut()));
connect(&interactTimer, SIGNAL(timeout()), this, SLOT(interactTimedOut()));
// some sendmail will give 'duplicate helo' error, quick fix for now
connect(this, SIGNAL(messageSent()), SLOT(closeConnection()));
}
SMTP::~SMTP()
{
delete sock;
sock = 0L;
connectTimer.stop();
timeOutTimer.stop();
}
void SMTP::setServerHost(const QString& serverhost)
{
serverHost = serverhost;
}
void SMTP::setPort(unsigned short int port)
{
hostPort = port;
}
void SMTP::setTimeOut(int timeout)
{
timeOut = timeout;
}
void SMTP::setSenderAddress(const QString& sender)
{
senderAddress = sender;
int index = senderAddress.indexOf('<');
if (index == -1)
return;
senderAddress = senderAddress.mid(index + 1);
index = senderAddress.indexOf('>');
if (index != -1)
senderAddress = senderAddress.left(index);
senderAddress = senderAddress.simplified();
while (1) {
index = senderAddress.indexOf(' ');
if (index != -1)
senderAddress = senderAddress.mid(index + 1); // take one side
else
break;
}
index = senderAddress.indexOf('@');
if (index == -1)
senderAddress.append("@localhost"); // won't go through without a local mail system
}
void SMTP::setRecipientAddress(const QString& recipient)
{
recipientAddress = recipient;
}
void SMTP::setMessageSubject(const QString& subject)
{
messageSubject = subject;
}
void SMTP::setMessageBody(const QString& message)
{
messageBody = message;
}
void SMTP::setMessageHeader(const QString &header)
{
messageHeader = header;
}
void SMTP::openConnection(void)
{
kDebug() << "started connect timer";
connectTimer.setSingleShot(true);
connectTimer.start(100);
}
void SMTP::closeConnection(void)
{
socketClosed();
}
void SMTP::sendMessage(void)
{
if(!connected)
connectTimerTick();
if(state == Finished && connected){
kDebug() << "state was == Finished\n";
finished = false;
state = In;
writeString = QString::fromLatin1("helo %1\r\n").arg(domainName);
- sock->write(writeString.toAscii().constData(), writeString.length());
+ sock->write(writeString.toLatin1().constData(), writeString.length());
}
if(connected){
kDebug() << "enabling read on sock...\n";
interactTimer.setSingleShot(true);
interactTimer.start(timeOut);
}
}
void SMTP::connectTimerTick(void)
{
connectTimer.stop();
// timeOutTimer.start(timeOut, true);
kDebug() << "connectTimerTick called...";
delete sock;
sock = 0L;
kDebug() << "connecting to " << serverHost << ":" << hostPort << " ..... ";
sock = KSocketFactory::connectToHost("smtp", serverHost, hostPort, this);
connected = true;
finished = false;
state = Init;
serverState = None;
connect(sock, SIGNAL(readyRead()), this, SLOT(socketReadyToRead()));
connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this,
SLOT(socketError(QAbstractSocket::SocketError)));
connect(sock, SIGNAL(disconnected()), this, SLOT(socketClosed()));
timeOutTimer.stop();
kDebug() << "connected";
}
void SMTP::connectTimedOut(void)
{
timeOutTimer.stop();
kDebug() << "socket connection timed out";
socketClosed();
emit error(ConnectTimeout);
}
void SMTP::interactTimedOut(void)
{
interactTimer.stop();
kDebug() << "time out waiting for server interaction";
socketClosed();
emit error(InteractTimeout);
}
void SMTP::socketReadyToRead()
{
int n, nl;
kDebug() << "socketRead() called...";
interactTimer.stop();
if (!sock)
return;
n = sock->read(readBuffer, SMTP_READ_BUFFER_SIZE-1);
if (n < 0)
return;
readBuffer[n] = 0;
lineBuffer += readBuffer;
nl = lineBuffer.indexOf('\n');
if(nl == -1)
return;
lastLine = lineBuffer.left(nl);
lineBuffer = lineBuffer.right(lineBuffer.length() - nl - 1);
processLine(&lastLine);
if(connected) {
interactTimer.setSingleShot(true);
interactTimer.start(timeOut);
}
}
void SMTP::socketError(QAbstractSocket::SocketError socketError)
{
kDebug() << socketError << sock->errorString();
Q_UNUSED(socketError);
emit error(ConnectError);
socketClosed();
}
void SMTP::socketClosed()
{
timeOutTimer.stop();
kDebug() << "connection terminated";
connected = false;
if (sock)
sock->deleteLater();
sock = 0;
emit connectionClosed();
}
void SMTP::processLine(QString *line)
{
int i, stat;
QString tmpstr;
i = line->indexOf(' ');
tmpstr = line->left(i);
if(i > 3)
kDebug() << "warning: SMTP status code longer than 3 digits: " << tmpstr;
stat = tmpstr.toInt();
serverState = (SMTPServerStatus)stat;
lastState = state;
kDebug() << "smtp state: [" << stat << "][" << *line << "]";
switch(stat){
case Greet: //220
state = In;
writeString = QString::fromLatin1("helo %1\r\n").arg(domainName);
kDebug() << "out: " << writeString;
- sock->write(writeString.toAscii().constData(), writeString.length());
+ sock->write(writeString.toLatin1().constData(), writeString.length());
break;
case Goodbye: //221
state = Quit;
break;
case Successful://250
switch(state){
case In:
state = Ready;
writeString = QString::fromLatin1("mail from: %1\r\n").arg(senderAddress);
kDebug() << "out: " << writeString;
- sock->write(writeString.toAscii().constData(), writeString.length());
+ sock->write(writeString.toLatin1().constData(), writeString.length());
break;
case Ready:
state = SentFrom;
writeString = QString::fromLatin1("rcpt to: %1\r\n").arg(recipientAddress);
kDebug() << "out: " << writeString;
- sock->write(writeString.toAscii().constData(), writeString.length());
+ sock->write(writeString.toLatin1().constData(), writeString.length());
break;
case SentFrom:
state = SentTo;
writeString = QLatin1String("data\r\n");
kDebug() << "out: " << writeString;
- sock->write(writeString.toAscii().constData(), writeString.length());
+ sock->write(writeString.toLatin1().constData(), writeString.length());
break;
case Data:
state = Finished;
finished = true;
emit messageSent();
break;
default:
state = CError;
kDebug() << "smtp error (state error): [" << lastState << "]:[" << stat << "][" << *line << "]";
socketClosed();
emit error(Command);
break;
}
break;
case ReadyData: //354
state = Data;
writeString = QString::fromLatin1("Subject: %1\r\n").arg(messageSubject);
writeString += messageHeader;
writeString += "\r\n";
writeString += messageBody;
writeString += QLatin1String(".\r\n");
kDebug() << "out: " << writeString;
- sock->write(writeString.toAscii().constData(), writeString.length());
+ sock->write(writeString.toLatin1().constData(), writeString.length());
break;
case Error: //501
state = CError;
kDebug() << "smtp error (command error): [" << lastState << "]:[" << stat << "][" << *line << "]\n";
socketClosed();
emit error(Command);
break;
case Unknown: //550
state = CError;
kDebug() << "smtp error (unknown user): [" << lastState << "]:[" << stat << "][" << *line << "]";
socketClosed();
emit error(UnknownUser);
break;
default:
state = CError;
kDebug() << "unknown response: [" << lastState << "]:[" << stat << "][" << *line << "]";
socketClosed();
emit error(UnknownResponse);
}
}
diff --git a/kioslave/file/file.cpp b/kioslave/file/file.cpp
index a53a333c91..ff3ba4dfe6 100644
--- a/kioslave/file/file.cpp
+++ b/kioslave/file/file.cpp
@@ -1,1346 +1,1346 @@
/*
Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org>
Copyright (C) 2000-2002 David Faure <faure@kde.org>
Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org>
Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
Copyright (C) 2007 Thiago Macieira <thiago@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 (LGPL) 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.
*/
#define QT_NO_CAST_FROM_ASCII
#include "file.h"
#include <QDirIterator>
#include <config-kioslave-file.h>
#include <qmimedatabase.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#if HAVE_STRING_H
#include <string.h>
#endif
#include <QtCore/QByteRef>
#include <QtCore/QDate>
#include <QtCore/QVarLengthArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QRegExp>
#include <QtCore/QFile>
#include <qtemporaryfile.h>
#ifdef Q_OS_WIN
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#endif
#include <kdebug.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <klocale.h>
#include <limits.h>
#include <kshell.h>
#include <kmountpoint.h>
#if HAVE_VOLMGT
#include <volmgt.h>
#include <sys/mnttab.h>
#endif
#include <kdirnotify.h>
#include <kio/ioslave_defaults.h>
#include <kde_file.h>
using namespace KIO;
#define MAX_IPC_SIZE (1024*32)
static QString testLogFile( const QByteArray&_filename );
#if HAVE_POSIX_ACL
static void appendACLAtoms( const QByteArray & path, UDSEntry& entry,
mode_t type, bool withACL );
#endif
extern "C" Q_DECL_EXPORT int kdemain( int argc, char **argv )
{
QCoreApplication app( argc, argv ); // needed for QSocketNotifier
app.setApplicationName(QLatin1String("kio_file"));
( void ) KLocale::global();
kDebug(7101) << "Starting" << getpid();
if (argc != 4)
{
fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n");
exit(-1);
}
FileProtocol slave(argv[2], argv[3]);
slave.dispatchLoop();
kDebug(7101) << "Done";
return 0;
}
FileProtocol::FileProtocol( const QByteArray &pool, const QByteArray &app )
: SlaveBase( "file", pool, app ), openFd(-1)
{
}
FileProtocol::~FileProtocol()
{
}
#if HAVE_POSIX_ACL
static QString aclToText(acl_t acl) {
ssize_t size = 0;
char* txt = acl_to_text(acl, &size);
const QString ret = QString::fromLatin1(txt, size);
acl_free(txt);
return ret;
}
#endif
int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault )
{
int ret = 0;
#if HAVE_POSIX_ACL
const QString ACLString = metaData(QLatin1String("ACL_STRING"));
const QString defaultACLString = metaData(QLatin1String("DEFAULT_ACL_STRING"));
// Empty strings mean leave as is
if ( !ACLString.isEmpty() ) {
acl_t acl = 0;
if (ACLString == QLatin1String("ACL_DELETE")) {
// user told us to delete the extended ACL, so let's write only
// the minimal (UNIX permission bits) part
acl = acl_from_mode( perm );
}
acl = acl_from_text( ACLString.toLatin1() );
if ( acl_valid( acl ) == 0 ) { // let's be safe
ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
kDebug(7101) << "Set ACL on:" << path << "to:" << aclToText(acl);
}
acl_free( acl );
if ( ret != 0 ) return ret; // better stop trying right away
}
if ( directoryDefault && !defaultACLString.isEmpty() ) {
if ( defaultACLString == QLatin1String("ACL_DELETE") ) {
// user told us to delete the default ACL, do so
ret += acl_delete_def_file( path );
} else {
acl_t acl = acl_from_text( defaultACLString.toLatin1() );
if ( acl_valid( acl ) == 0 ) { // let's be safe
ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
kDebug(7101) << "Set Default ACL on:" << path << "to:" << aclToText(acl);
}
acl_free( acl );
}
}
#else
Q_UNUSED(path);
Q_UNUSED(perm);
Q_UNUSED(directoryDefault);
#endif
return ret;
}
void FileProtocol::chmod( const QUrl& url, int permissions )
{
const QString path(url.toLocalFile());
const QByteArray _path( QFile::encodeName(path) );
/* FIXME: Should be atomic */
if ( KDE::chmod( path, permissions ) == -1 ||
( setACL( _path.data(), permissions, false ) == -1 ) ||
/* if not a directory, cannot set default ACLs */
( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
switch (errno) {
case EPERM:
case EACCES:
error(KIO::ERR_ACCESS_DENIED, path);
break;
#if defined(ENOTSUP)
case ENOTSUP: // from setACL since chmod can't return ENOTSUP
error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Setting ACL for %1", path));
break;
#endif
case ENOSPC:
error(KIO::ERR_DISK_FULL, path);
break;
default:
error(KIO::ERR_CANNOT_CHMOD, path);
}
} else
finished();
}
void FileProtocol::setModificationTime( const QUrl& url, const QDateTime& mtime )
{
const QString path(url.toLocalFile());
KDE_struct_stat statbuf;
if (KDE::lstat(path, &statbuf) == 0) {
struct utimbuf utbuf;
utbuf.actime = statbuf.st_atime; // access time, unchanged
utbuf.modtime = mtime.toTime_t(); // modification time
if (KDE::utime(path, &utbuf) != 0) {
// TODO: errno could be EACCES, EPERM, EROFS
error(KIO::ERR_CANNOT_SETTIME, path);
} else {
finished();
}
} else {
error(KIO::ERR_DOES_NOT_EXIST, path);
}
}
void FileProtocol::mkdir( const QUrl& url, int permissions )
{
const QString path(url.toLocalFile());
kDebug(7101) << path << "permission=" << permissions;
// Remove existing file or symlink, if requested (#151851)
if (metaData(QLatin1String("overwrite")) == QLatin1String("true"))
QFile::remove(path);
KDE_struct_stat buff;
if ( KDE::lstat( path, &buff ) == -1 ) {
if ( KDE::mkdir( path, 0777 /*umask will be applied*/ ) != 0 ) {
if ( errno == EACCES ) {
error(KIO::ERR_ACCESS_DENIED, path);
return;
} else if ( errno == ENOSPC ) {
error(KIO::ERR_DISK_FULL, path);
return;
} else {
error(KIO::ERR_COULD_NOT_MKDIR, path);
return;
}
} else {
if ( permissions != -1 )
chmod( url, permissions );
else
finished();
return;
}
}
if ( S_ISDIR( buff.st_mode ) ) {
kDebug(7101) << "ERR_DIR_ALREADY_EXIST";
error(KIO::ERR_DIR_ALREADY_EXIST, path);
return;
}
error(KIO::ERR_FILE_ALREADY_EXIST, path);
return;
}
void FileProtocol::get( const QUrl& url )
{
if (!url.isLocalFile()) {
QUrl redir(url);
redir.setScheme(config()->readEntry("DefaultRemoteProtocol", "smb"));
redirection(redir);
finished();
return;
}
const QString path(url.toLocalFile());
KDE_struct_stat buff;
if ( KDE::stat( path, &buff ) == -1 ) {
if ( errno == EACCES )
error(KIO::ERR_ACCESS_DENIED, path);
else
error(KIO::ERR_DOES_NOT_EXIST, path);
return;
}
if ( S_ISDIR( buff.st_mode ) ) {
error(KIO::ERR_IS_DIRECTORY, path);
return;
}
if ( !S_ISREG( buff.st_mode ) ) {
error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
return;
}
int fd = KDE::open( path, O_RDONLY);
if ( fd < 0 ) {
error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
return;
}
#if HAVE_FADVISE
posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
#endif
// Determine the mimetype of the file to be retrieved, and emit it.
// This is mandatory in all slaves (for KRun/BrowserRun to work)
// In real "remote" slaves, this is usually done using mimeTypeForFileNameAndData
// after receiving some data. But we don't know how much data the mimemagic rules
// need, so for local files, better use mimeTypeForFile.
QMimeDatabase db;
QMimeType mt = db.mimeTypeForFile(url.toLocalFile());
emit mimeType(mt.name());
// Emit total size AFTER mimetype
totalSize( buff.st_size );
KIO::filesize_t processed_size = 0;
const QString resumeOffset = metaData(QLatin1String("resume"));
if ( !resumeOffset.isEmpty() )
{
bool ok;
KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
if (ok && (offset > 0) && (offset < buff.st_size))
{
if (KDE_lseek(fd, offset, SEEK_SET) == offset)
{
canResume ();
processed_size = offset;
kDebug(7101) << "Resume offset:" << KIO::number(offset);
}
}
}
char buffer[ MAX_IPC_SIZE ];
QByteArray array;
while( 1 )
{
int n = ::read( fd, buffer, MAX_IPC_SIZE );
if (n == -1)
{
if (errno == EINTR)
continue;
error(KIO::ERR_COULD_NOT_READ, path);
::close(fd);
return;
}
if (n == 0)
break; // Finished
array = QByteArray::fromRawData(buffer, n);
data( array );
array.clear();
processed_size += n;
processedSize( processed_size );
//kDebug(7101) << "Processed: " << KIO::number (processed_size);
}
data( QByteArray() );
::close( fd );
processedSize( buff.st_size );
finished();
}
int write_all(int fd, const char *buf, size_t len)
{
while (len > 0)
{
ssize_t written = write(fd, buf, len);
if (written < 0)
{
if (errno == EINTR)
continue;
return -1;
}
buf += written;
len -= written;
}
return 0;
}
void FileProtocol::open(const QUrl &url, QIODevice::OpenMode mode)
{
kDebug(7101) << url;
openPath = url.toLocalFile();
KDE_struct_stat buff;
if (KDE::stat(openPath, &buff) == -1) {
if ( errno == EACCES )
error(KIO::ERR_ACCESS_DENIED, openPath);
else
error(KIO::ERR_DOES_NOT_EXIST, openPath);
return;
}
if ( S_ISDIR( buff.st_mode ) ) {
error(KIO::ERR_IS_DIRECTORY, openPath);
return;
}
if ( !S_ISREG( buff.st_mode ) ) {
error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath);
return;
}
int flags = 0;
if (mode & QIODevice::ReadOnly) {
if (mode & QIODevice::WriteOnly) {
flags = O_RDWR | O_CREAT;
} else {
flags = O_RDONLY;
}
} else if (mode & QIODevice::WriteOnly) {
flags = O_WRONLY | O_CREAT;
}
if (mode & QIODevice::Append) {
flags |= O_APPEND;
} else if (mode & QIODevice::Truncate) {
flags |= O_TRUNC;
}
int fd = -1;
if ( flags & O_CREAT)
fd = KDE::open( openPath, flags, 0666);
else
fd = KDE::open( openPath, flags);
if ( fd < 0 ) {
error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath);
return;
}
// Determine the mimetype of the file to be retrieved, and emit it.
// This is mandatory in all slaves (for KRun/BrowserRun to work).
// If we're not opening the file ReadOnly or ReadWrite, don't attempt to
// read the file and send the mimetype.
if (mode & QIODevice::ReadOnly){
QMimeDatabase db;
QMimeType mt = db.mimeTypeForFile(url.toLocalFile());
emit mimeType(mt.name());
}
totalSize( buff.st_size );
position( 0 );
emit opened();
openFd = fd;
}
void FileProtocol::read(KIO::filesize_t bytes)
{
kDebug(7101) << "File::open -- read";
Q_ASSERT(openFd != -1);
QVarLengthArray<char> buffer(bytes);
while (true) {
int res;
do {
res = ::read(openFd, buffer.data(), bytes);
} while (res == -1 && errno == EINTR);
if (res > 0) {
QByteArray array = QByteArray::fromRawData(buffer.data(), res);
data( array );
bytes -= res;
} else {
// empty array designates eof
data(QByteArray());
if (res != 0) {
error(KIO::ERR_COULD_NOT_READ, openPath);
close();
}
break;
}
if (bytes <= 0) break;
}
}
void FileProtocol::write(const QByteArray &data)
{
kDebug(7101) << "File::open -- write";
Q_ASSERT(openFd != -1);
if (write_all(openFd, data.constData(), data.size())) {
if (errno == ENOSPC) { // disk full
error(KIO::ERR_DISK_FULL, openPath);
close();
} else {
kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
error(KIO::ERR_COULD_NOT_WRITE, openPath);
close();
}
} else {
written(data.size());
}
}
void FileProtocol::seek(KIO::filesize_t offset)
{
kDebug(7101) << "File::open -- seek";
Q_ASSERT(openFd != -1);
int res = KDE_lseek(openFd, offset, SEEK_SET);
if (res != -1) {
position( offset );
} else {
error(KIO::ERR_COULD_NOT_SEEK, openPath);
close();
}
}
void FileProtocol::close()
{
kDebug(7101) << "File::open -- close ";
Q_ASSERT(openFd != -1);
::close( openFd );
openFd = -1;
openPath.clear();
finished();
}
void FileProtocol::put( const QUrl& url, int _mode, KIO::JobFlags _flags )
{
const QString dest_orig = url.toLocalFile();
kDebug(7101) << dest_orig << "mode=" << _mode;
QString dest_part(dest_orig + QLatin1String(".part"));
KDE_struct_stat buff_orig;
const bool bOrigExists = (KDE::lstat(dest_orig, &buff_orig) != -1);
bool bPartExists = false;
const bool bMarkPartial = config()->readEntry("MarkPartial", true);
if (bMarkPartial)
{
KDE_struct_stat buff_part;
bPartExists = (KDE::stat( dest_part, &buff_part ) != -1);
if (bPartExists && !(_flags & KIO::Resume) && !(_flags & KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
{
kDebug(7101) << "calling canResume with" << KIO::number(buff_part.st_size);
// Maybe we can use this partial file for resuming
// Tell about the size we have, and the app will tell us
// if it's ok to resume or not.
_flags |= canResume( buff_part.st_size ) ? KIO::Resume : KIO::DefaultFlags;
kDebug(7101) << "got answer" << (_flags & KIO::Resume);
}
}
if ( bOrigExists && !(_flags & KIO::Overwrite) && !(_flags & KIO::Resume))
{
if (S_ISDIR(buff_orig.st_mode))
error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig );
else
error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig );
return;
}
int result;
QString dest;
QByteArray _dest;
int fd = -1;
// Loop until we got 0 (end of data)
do
{
QByteArray buffer;
dataReq(); // Request for data
result = readData( buffer );
if (result >= 0)
{
if (dest.isEmpty())
{
if (bMarkPartial)
{
kDebug(7101) << "Appending .part extension to" << dest_orig;
dest = dest_part;
if ( bPartExists && !(_flags & KIO::Resume) )
{
kDebug(7101) << "Deleting partial file" << dest_part;
QFile::remove( dest_part );
// Catch errors when we try to open the file.
}
}
else
{
dest = dest_orig;
if ( bOrigExists && !(_flags & KIO::Resume) )
{
kDebug(7101) << "Deleting destination file" << dest_orig;
QFile::remove( dest_orig );
// Catch errors when we try to open the file.
}
}
if ( (_flags & KIO::Resume) )
{
fd = KDE::open( dest, O_RDWR ); // append if resuming
KDE_lseek(fd, 0, SEEK_END); // Seek to end
}
else
{
// WABA: Make sure that we keep writing permissions ourselves,
// otherwise we can be in for a surprise on NFS.
mode_t initialMode;
if (_mode != -1)
initialMode = _mode | S_IWUSR | S_IRUSR;
else
initialMode = 0666;
fd = KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
}
if ( fd < 0 )
{
kDebug(7101) << "####################### COULD NOT WRITE" << dest << "_mode=" << _mode;
kDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")";
if ( errno == EACCES )
error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
else
error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
return;
}
}
if (write_all( fd, buffer.data(), buffer.size()))
{
if ( errno == ENOSPC ) // disk full
{
error(KIO::ERR_DISK_FULL, dest_orig);
result = -2; // means: remove dest file
}
else
{
kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
result = -1;
}
}
}
}
while ( result > 0 );
// An error occurred deal with it.
if (result < 0)
{
kDebug(7101) << "Error during 'put'. Aborting.";
if (fd != -1)
{
::close(fd);
KDE_struct_stat buff;
if (bMarkPartial && KDE::stat( dest, &buff ) == 0)
{
int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
if (buff.st_size < size)
remove(_dest.data());
}
}
::exit(255);
}
if ( fd == -1 ) // we got nothing to write out, so we never opened the file
{
finished();
return;
}
if ( ::close(fd) )
{
kWarning(7101) << "Error when closing file descriptor:" << strerror(errno);
error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
return;
}
// after full download rename the file back to original name
if ( bMarkPartial )
{
// If the original URL is a symlink and we were asked to overwrite it,
// remove the symlink first. This ensures that we do not overwrite the
// current source if the symlink points to it.
if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
QFile::remove( dest_orig );
if ( KDE::rename( dest, dest_orig ) )
{
kWarning(7101) << " Couldn't rename " << _dest << " to " << dest_orig;
error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig);
return;
}
org::kde::KDirNotify::emitFileRenamed(QUrl::fromLocalFile(dest), QUrl::fromLocalFile(dest_orig));
}
// set final permissions
if ( _mode != -1 && !(_flags & KIO::Resume) )
{
if (KDE::chmod(dest_orig, _mode) != 0)
{
// couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest_orig);
if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod))
warning( i18n( "Could not change permissions for\n%1" , dest_orig ) );
}
}
// set modification time
const QString mtimeStr = metaData(QLatin1String("modified"));
if ( !mtimeStr.isEmpty() ) {
QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
if ( dt.isValid() ) {
KDE_struct_stat dest_statbuf;
if (KDE::stat( dest_orig, &dest_statbuf ) == 0) {
struct timeval utbuf[2];
// access time
utbuf[0].tv_sec = dest_statbuf.st_atime; // access time, unchanged ## TODO preserve msec
utbuf[0].tv_usec = 0;
// modification time
utbuf[1].tv_sec = dt.toTime_t();
utbuf[1].tv_usec = dt.time().msec() * 1000;
utimes( QFile::encodeName(dest_orig), utbuf );
}
}
}
// We have done our job => finish
finished();
}
QString FileProtocol::getUserName( uid_t uid ) const
{
if ( !mUsercache.contains( uid ) ) {
struct passwd *user = getpwuid( uid );
if ( user ) {
mUsercache.insert( uid, QString::fromLatin1(user->pw_name) );
}
else
return QString::number( uid );
}
return mUsercache[uid];
}
QString FileProtocol::getGroupName( gid_t gid ) const
{
if ( !mGroupcache.contains( gid ) ) {
struct group *grp = getgrgid( gid );
if ( grp ) {
mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) );
}
else
return QString::number( gid );
}
return mGroupcache[gid];
}
bool FileProtocol::createUDSEntry( const QString & filename, const QByteArray & path, UDSEntry & entry,
short int details, bool withACL )
{
#ifndef HAVE_POSIX_ACL
Q_UNUSED(withACL);
#endif
assert(entry.count() == 0); // by contract :-)
// entry.reserve( 8 ); // speed up QHash insertion
entry.insert( KIO::UDSEntry::UDS_NAME, filename );
mode_t type;
mode_t access;
KDE_struct_stat buff;
if ( KDE_lstat( path.data(), &buff ) == 0 ) {
if (details > 2) {
entry.insert( KIO::UDSEntry::UDS_DEVICE_ID, buff.st_dev );
entry.insert( KIO::UDSEntry::UDS_INODE, buff.st_ino );
}
if (S_ISLNK(buff.st_mode)) {
char buffer2[ 1000 ];
int n = readlink( path.data(), buffer2, 999 );
if ( n != -1 ) {
buffer2[ n ] = 0;
}
entry.insert( KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName( buffer2 ) );
// A symlink -> follow it only if details>1
if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) {
// It is a link pointing to nowhere
type = S_IFMT - 1;
access = S_IRWXU | S_IRWXG | S_IRWXO;
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL );
goto notype;
}
}
} else {
// kWarning() << "lstat didn't work on " << path.data();
return false;
}
type = buff.st_mode & S_IFMT; // extract file type
access = buff.st_mode & 07777; // extract permissions
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
entry.insert( KIO::UDSEntry::UDS_SIZE, buff.st_size );
#if HAVE_POSIX_ACL
if (details > 0) {
/* Append an atom indicating whether the file has extended acl information
* and if withACL is specified also one with the acl itself. If it's a directory
* and it has a default ACL, also append that. */
appendACLAtoms( path, entry, type, withACL );
}
#endif
notype:
if (details > 0) {
entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime );
entry.insert( KIO::UDSEntry::UDS_USER, getUserName( buff.st_uid ) );
entry.insert( KIO::UDSEntry::UDS_GROUP, getGroupName( buff.st_gid ) );
entry.insert( KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime );
}
// Note: buff.st_ctime isn't the creation time !
// We made that mistake for KDE 2.0, but it's in fact the
// "file status" change time, which we don't care about.
return true;
}
void FileProtocol::special( const QByteArray &data)
{
int tmp;
QDataStream stream(data);
stream >> tmp;
switch (tmp) {
case 1:
{
QString fstype, dev, point;
qint8 iRo;
stream >> iRo >> fstype >> dev >> point;
bool ro = ( iRo != 0 );
kDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro;
bool ok = pmount( dev );
if (ok)
finished();
else
- mount( ro, fstype.toAscii(), dev, point );
+ mount( ro, fstype.toLatin1(), dev, point );
}
break;
case 2:
{
QString point;
stream >> point;
bool ok = pumount( point );
if (ok)
finished();
else
unmount( point );
}
break;
default:
break;
}
}
static QStringList fallbackSystemPath() {
return QStringList() << QLatin1String("/sbin") << QLatin1String("/bin");
}
void FileProtocol::mount( bool _ro, const char *_fstype, const QString& _dev, const QString& _point )
{
kDebug(7101) << "fstype=" << _fstype;
#ifndef _WIN32_WCE
#if HAVE_VOLMGT
/*
* support for Solaris volume management
*/
QString err;
QByteArray devname = QFile::encodeName( _dev );
if( volmgt_running() ) {
// kDebug(7101) << "VOLMGT: vold ok.";
if( volmgt_check( devname.data() ) == 0 ) {
kDebug(7101) << "VOLMGT: no media in "
<< devname.data();
err = i18n("No Media inserted or Media not recognized.");
error( KIO::ERR_COULD_NOT_MOUNT, err );
return;
} else {
kDebug(7101) << "VOLMGT: " << devname.data()
<< ": media ok";
finished();
return;
}
} else {
err = i18n("\"vold\" is not running.");
kDebug(7101) << "VOLMGT: " << err;
error( KIO::ERR_COULD_NOT_MOUNT, err );
return;
}
#else
QTemporaryFile tmpFile;
tmpFile.setAutoRemove(false);
tmpFile.open();
QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
QByteArray dev;
if (_dev.startsWith(QLatin1String("LABEL="))) { // turn LABEL=foo into -L foo (#71430)
QString labelName = _dev.mid( 6 );
dev = "-L ";
dev += QFile::encodeName( KShell::quoteArg( labelName ) ); // is it correct to assume same encoding as filesystem?
} else if (_dev.startsWith(QLatin1String("UUID="))) { // and UUID=bar into -U bar
QString uuidName = _dev.mid( 5 );
dev = "-U ";
dev += QFile::encodeName( KShell::quoteArg( uuidName ) );
}
else
dev = QFile::encodeName( KShell::quoteArg(_dev) ); // get those ready to be given to a shell
QByteArray point = QFile::encodeName( KShell::quoteArg(_point) );
bool fstype_empty = !_fstype || !*_fstype;
QByteArray fstype = KShell::quoteArg(QString::fromLatin1(_fstype)).toLatin1(); // good guess
QByteArray readonly = _ro ? "-r" : "";
QByteArray mountProg = QStandardPaths::findExecutable(QLatin1String("mount")).toLocal8Bit();
if (mountProg.isEmpty()) {
mountProg = QStandardPaths::findExecutable(QLatin1String("mount"), fallbackSystemPath()).toLocal8Bit();
}
if (mountProg.isEmpty()){
error( KIO::ERR_COULD_NOT_MOUNT, i18n("Could not find program \"mount\""));
return;
}
// Two steps, in case mount doesn't like it when we pass all options
for ( int step = 0 ; step <= 1 ; step++ )
{
QByteArray buffer = mountProg + ' ';
// Mount using device only if no fstype nor mountpoint (KDE-1.x like)
if ( !dev.isEmpty() && _point.isEmpty() && fstype_empty )
buffer += dev;
else
// Mount using the mountpoint, if no fstype nor device (impossible in first step)
if ( !_point.isEmpty() && dev.isEmpty() && fstype_empty )
buffer += point;
else
// mount giving device + mountpoint but no fstype
if ( !_point.isEmpty() && !dev.isEmpty() && fstype_empty )
buffer += readonly + ' ' + dev + ' ' + point;
else
// mount giving device + mountpoint + fstype
#if defined(__svr4__) && defined(Q_OS_SOLARIS) // MARCO for Solaris 8 and I
// believe this is true for SVR4 in general
buffer += "-F " + fstype + ' ' + (_ro ? "-oro" : "") + ' ' + dev + ' ' + point;
#else
buffer += readonly + " -t " + fstype + ' ' + dev + ' ' + point;
#endif
buffer += " 2>" + tmpFileName;
kDebug(7101) << buffer;
int mount_ret = system( buffer.constData() );
QString err = testLogFile( tmpFileName );
if ( err.isEmpty() && mount_ret == 0)
{
finished();
return;
}
else
{
// Didn't work - or maybe we just got a warning
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( _dev );
// Is the device mounted ?
if ( mp && mount_ret == 0)
{
kDebug(7101) << "mount got a warning:" << err;
warning( err );
finished();
return;
}
else
{
if ( (step == 0) && !_point.isEmpty())
{
kDebug(7101) << err;
kDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint";
fstype = "";
fstype_empty = true;
dev = "";
// The reason for trying with only mountpoint (instead of
// only device) is that some people (hi Malte!) have the
// same device associated with two mountpoints
// for different fstypes, like /dev/fd0 /mnt/e2floppy and
// /dev/fd0 /mnt/dosfloppy.
// If the user has the same mountpoint associated with two
// different devices, well they shouldn't specify the
// mountpoint but just the device.
}
else
{
error( KIO::ERR_COULD_NOT_MOUNT, err );
return;
}
}
}
}
#endif /* ! HAVE_VOLMGT */
#else
QString err;
err = i18n("mounting is not supported by wince.");
error( KIO::ERR_COULD_NOT_MOUNT, err );
#endif
}
void FileProtocol::unmount( const QString& _point )
{
#ifndef _WIN32_WCE
QByteArray buffer;
QTemporaryFile tmpFile;
tmpFile.setAutoRemove(false);
tmpFile.open();
QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
QString err;
#if HAVE_VOLMGT
/*
* support for Solaris volume management
*/
char *devname;
char *ptr;
FILE *mnttab;
struct mnttab mnt;
if( volmgt_running() ) {
kDebug(7101) << "VOLMGT: looking for "
<< _point.toLocal8Bit();
if( (mnttab = KDE_fopen( MNTTAB, "r" )) == NULL ) {
err = QLatin1String("could not open mnttab");
kDebug(7101) << "VOLMGT: " << err;
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
return;
}
/*
* since there's no way to derive the device name from
* the mount point through the volmgt library (and
* media_findname() won't work in this case), we have to
* look ourselves...
*/
devname = NULL;
rewind( mnttab );
while( getmntent( mnttab, &mnt ) == 0 ) {
if( strcmp( _point.toLocal8Bit(), mnt.mnt_mountp ) == 0 ){
devname = mnt.mnt_special;
break;
}
}
fclose( mnttab );
if( devname == NULL ) {
err = QLatin1String("not in mnttab");
kDebug(7101) << "VOLMGT: "
<< QFile::encodeName(_point).data()
<< ": " << err;
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
return;
}
/*
* strip off the directory name (volume name)
* the eject(1) command will handle unmounting and
* physically eject the media (if possible)
*/
ptr = strrchr( devname, '/' );
*ptr = '\0';
QByteArray qdevname(QFile::encodeName(KShell::quoteArg(QFile::decodeName(QByteArray(devname)))).data());
buffer = "/usr/bin/eject " + qdevname + " 2>" + tmpFileName;
kDebug(7101) << "VOLMGT: eject " << qdevname;
/*
* from eject(1): exit status == 0 => need to manually eject
* exit status == 4 => media was ejected
*/
if( WEXITSTATUS( system( buffer.constData() )) == 4 ) {
/*
* this is not an error, so skip "testLogFile()"
* to avoid wrong/confusing error popup. The
* temporary file is removed by QTemporaryFile's
* destructor, so don't do that manually.
*/
finished();
return;
}
} else {
/*
* eject(1) should do its job without vold(1M) running,
* so we probably could call eject anyway, but since the
* media is mounted now, vold must've died for some reason
* during the user's session, so it should be restarted...
*/
err = i18n("\"vold\" is not running.");
kDebug(7101) << "VOLMGT: " << err;
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
return;
}
#else
QByteArray umountProg = QStandardPaths::findExecutable(QLatin1String("umount")).toLocal8Bit();
if (umountProg.isEmpty()) {
umountProg = QStandardPaths::findExecutable(QLatin1String("umount"), fallbackSystemPath()).toLocal8Bit();
}
if (umountProg.isEmpty()) {
error( KIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\""));
return;
}
buffer = umountProg + ' ' + QFile::encodeName(KShell::quoteArg(_point)) + " 2>" + tmpFileName;
system( buffer.constData() );
#endif /* HAVE_VOLMGT */
err = testLogFile( tmpFileName );
if ( err.isEmpty() )
finished();
else
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
#else
QString err;
err = i18n("unmounting is not supported by wince.");
error( KIO::ERR_COULD_NOT_MOUNT, err );
#endif
}
/*************************************
*
* pmount handling
*
*************************************/
bool FileProtocol::pmount(const QString &dev)
{
#ifndef _WIN32_WCE
QString pmountProg = QStandardPaths::findExecutable(QLatin1String("pmount"));
if (pmountProg.isEmpty())
pmountProg = QStandardPaths::findExecutable(QLatin1String("pmount"), fallbackSystemPath());
if (pmountProg.isEmpty())
return false;
QByteArray buffer = QFile::encodeName(pmountProg) + ' ' +
QFile::encodeName(KShell::quoteArg(dev));
int res = system( buffer.constData() );
return res==0;
#else
return false;
#endif
}
bool FileProtocol::pumount(const QString &point)
{
#ifndef _WIN32_WCE
KMountPoint::Ptr mp = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName).findByPath(point);
if (!mp)
return false;
QString dev = mp->realDeviceName();
if (dev.isEmpty()) return false;
QString pumountProg = QStandardPaths::findExecutable(QLatin1String("pumount"));
if (pumountProg.isEmpty())
pumountProg = QStandardPaths::findExecutable(QLatin1String("pumount"), fallbackSystemPath());
if (pumountProg.isEmpty())
return false;
QByteArray buffer = QFile::encodeName(pumountProg);
buffer += ' ';
buffer += QFile::encodeName(KShell::quoteArg(dev));
int res = system( buffer.data() );
return res==0;
#else
return false;
#endif
}
/*************************************
*
* Utilities
*
*************************************/
static QString testLogFile( const QByteArray& _filename )
{
char buffer[ 1024 ];
KDE_struct_stat buff;
QString result;
KDE_stat( _filename, &buff );
int size = buff.st_size;
if ( size == 0 ) {
unlink( _filename );
return result;
}
FILE * f = KDE_fopen( _filename, "rb" );
if ( f == 0L ) {
unlink( _filename );
result = i18n("Could not read %1", QFile::decodeName(_filename));
return result;
}
result.clear();
const char *p = "";
while ( p != 0L ) {
p = fgets( buffer, sizeof(buffer)-1, f );
if ( p != 0L )
result += QString::fromLocal8Bit(buffer);
}
fclose( f );
unlink( _filename );
return result;
}
/*************************************
*
* ACL handling helpers
*
*************************************/
#if HAVE_POSIX_ACL
bool FileProtocol::isExtendedACL( acl_t acl )
{
return ( acl_equiv_mode( acl, 0 ) != 0 );
}
static void appendACLAtoms( const QByteArray & path, UDSEntry& entry, mode_t type, bool withACL )
{
// first check for a noop
if ( acl_extended_file( path.data() ) == 0 ) return;
acl_t acl = 0;
acl_t defaultAcl = 0;
bool isDir = S_ISDIR( type );
// do we have an acl for the file, and/or a default acl for the dir, if it is one?
acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
/* Sadly libacl does not provided a means of checking for extended ACL and default
* ACL separately. Since a directory can have both, we need to check again. */
if ( isDir ) {
if ( acl ) {
if ( !FileProtocol::isExtendedACL( acl ) ) {
acl_free( acl );
acl = 0;
}
}
defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
}
if ( acl || defaultAcl ) {
kDebug(7101) << path.constData() << "has extended ACL entries";
entry.insert( KIO::UDSEntry::UDS_EXTENDED_ACL, 1 );
}
if ( withACL ) {
if ( acl ) {
const QString str = aclToText(acl);
entry.insert( KIO::UDSEntry::UDS_ACL_STRING, str );
kDebug(7101) << path.constData() << "ACL:" << str;
}
if ( defaultAcl ) {
const QString str = aclToText(defaultAcl);
entry.insert( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str );
kDebug(7101) << path.constData() << "DEFAULT ACL:" << str;
}
}
if ( acl ) acl_free( acl );
if ( defaultAcl ) acl_free( defaultAcl );
}
#endif
// We could port this to KTempDir::removeDir but then we wouldn't be able to tell the user
// where exactly the deletion failed, in case of errors.
bool FileProtocol::deleteRecursive(const QString& path)
{
//kDebug() << path;
QDirIterator it(path, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden,
QDirIterator::Subdirectories);
QStringList dirsToDelete;
while ( it.hasNext() ) {
const QString itemPath = it.next();
//kDebug() << "itemPath=" << itemPath;
const QFileInfo info = it.fileInfo();
if (info.isDir() && !info.isSymLink())
dirsToDelete.prepend(itemPath);
else {
//kDebug() << "QFile::remove" << itemPath;
if (!QFile::remove(itemPath)) {
error(KIO::ERR_CANNOT_DELETE, itemPath);
return false;
}
}
}
QDir dir;
Q_FOREACH(const QString& itemPath, dirsToDelete) {
//kDebug() << "QDir::rmdir" << itemPath;
if (!dir.rmdir(itemPath)) {
error(KIO::ERR_CANNOT_DELETE, itemPath);
return false;
}
}
return true;
}
diff --git a/kioslave/file/file_unix.cpp b/kioslave/file/file_unix.cpp
index a6fb997baf..324cf3eb23 100644
--- a/kioslave/file/file_unix.cpp
+++ b/kioslave/file/file_unix.cpp
@@ -1,671 +1,671 @@
/*
Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org>
Copyright (C) 2000-2002 David Faure <faure@kde.org>
Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org>
Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
Copyright (C) 2007 Thiago Macieira <thiago@kde.org>
Copyright (C) 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) 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.
*/
#define QT_NO_CAST_FROM_ASCII
#include "file.h"
#include <config-kioslave-file.h>
#include <QtCore/QFile>
#include <qurlpathinfo.h>
#include <QtCore/QDir>
#include <kde_file.h>
#include <kdebug.h>
#include <kconfiggroup.h>
#include <kmountpoint.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <utime.h>
#include <pwd.h>
#include <stdlib.h>
#if HAVE_LIMITS_H
#include <limits.h> // PATH_MAX
#endif
//sendfile has different semantics in different platforms
#if defined HAVE_SENDFILE && defined Q_OS_LINUX
#define USE_SENDFILE 1
#endif
#ifdef USE_SENDFILE
#include <sys/sendfile.h>
#endif
using namespace KIO;
#define MAX_IPC_SIZE (1024*32)
static bool
same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
{
if (src.st_ino == dest.st_ino &&
src.st_dev == dest.st_dev)
return true;
return false;
}
extern int write_all(int fd, const char *buf, size_t len);
void FileProtocol::copy( const QUrl &srcUrl, const QUrl &destUrl,
int _mode, JobFlags _flags )
{
kDebug(7101) << "copy(): " << srcUrl << " -> " << destUrl << ", mode=" << _mode;
const QString src = srcUrl.toLocalFile();
const QString dest = destUrl.toLocalFile();
QByteArray _src( QFile::encodeName(src));
QByteArray _dest( QFile::encodeName(dest));
KDE_struct_stat buff_src;
#if HAVE_POSIX_ACL
acl_t acl;
#endif
if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
if ( errno == EACCES )
error(KIO::ERR_ACCESS_DENIED, src);
else
error(KIO::ERR_DOES_NOT_EXIST, src);
return;
}
if ( S_ISDIR( buff_src.st_mode ) ) {
error(KIO::ERR_IS_DIRECTORY, src);
return;
}
if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
return;
}
KDE_struct_stat buff_dest;
bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
if ( dest_exists )
{
if (S_ISDIR(buff_dest.st_mode))
{
error(KIO::ERR_DIR_ALREADY_EXIST, dest);
return;
}
if ( same_inode( buff_dest, buff_src) )
{
error(KIO::ERR_IDENTICAL_FILES, dest);
return;
}
if (!(_flags & KIO::Overwrite))
{
error(KIO::ERR_FILE_ALREADY_EXIST, dest);
return;
}
// If the destination is a symlink and overwrite is TRUE,
// remove the symlink first to prevent the scenario where
// the symlink actually points to current source!
if ((_flags & KIO::Overwrite) && S_ISLNK(buff_dest.st_mode))
{
//kDebug(7101) << "copy(): LINK DESTINATION";
remove( _dest.data() );
}
}
int src_fd = KDE_open( _src.data(), O_RDONLY);
if ( src_fd < 0 ) {
error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
return;
}
#if HAVE_FADVISE
posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
#endif
// WABA: Make sure that we keep writing permissions ourselves,
// otherwise we can be in for a surprise on NFS.
mode_t initialMode;
if (_mode != -1)
initialMode = _mode | S_IWUSR;
else
initialMode = 0666;
int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
if ( dest_fd < 0 ) {
kDebug(7101) << "###### COULD NOT WRITE " << dest;
if ( errno == EACCES ) {
error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
} else {
error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
}
::close(src_fd);
return;
}
#if HAVE_FADVISE
posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
#endif
#if HAVE_POSIX_ACL
acl = acl_get_fd(src_fd);
if ( acl && !isExtendedACL( acl ) ) {
kDebug(7101) << _dest.data() << " doesn't have extended ACL";
acl_free( acl );
acl = NULL;
}
#endif
totalSize( buff_src.st_size );
KIO::filesize_t processed_size = 0;
char buffer[ MAX_IPC_SIZE ];
int n;
#ifdef USE_SENDFILE
bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
#endif
while( 1 )
{
#ifdef USE_SENDFILE
if (use_sendfile) {
off_t sf = processed_size;
n = KDE_sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
processed_size = sf;
if ( n == -1 && ( errno == EINVAL || errno == ENOSYS ) ) { //not all filesystems support sendfile()
kDebug(7101) << "sendfile() not supported, falling back ";
use_sendfile = false;
}
}
if (!use_sendfile)
#endif
n = ::read( src_fd, buffer, MAX_IPC_SIZE );
if (n == -1)
{
if (errno == EINTR)
continue;
#ifdef USE_SENDFILE
if ( use_sendfile ) {
kDebug(7101) << "sendfile() error:" << strerror(errno);
if ( errno == ENOSPC ) // disk full
{
error(KIO::ERR_DISK_FULL, dest);
remove( _dest.data() );
}
else {
error(KIO::ERR_SLAVE_DEFINED,
i18n("Cannot copy file from %1 to %2. (Errno: %3)",
src, dest, errno));
}
} else
#endif
error(KIO::ERR_COULD_NOT_READ, src);
::close(src_fd);
::close(dest_fd);
#if HAVE_POSIX_ACL
if (acl) acl_free(acl);
#endif
return;
}
if (n == 0)
break; // Finished
#ifdef USE_SENDFILE
if ( !use_sendfile ) {
#endif
if (write_all( dest_fd, buffer, n))
{
::close(src_fd);
::close(dest_fd);
if ( errno == ENOSPC ) // disk full
{
error(KIO::ERR_DISK_FULL, dest);
remove( _dest.data() );
}
else
{
kWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno);
error(KIO::ERR_COULD_NOT_WRITE, dest);
}
#if HAVE_POSIX_ACL
if (acl) acl_free(acl);
#endif
return;
}
processed_size += n;
#ifdef USE_SENDFILE
}
#endif
processedSize( processed_size );
}
::close( src_fd );
if (::close( dest_fd))
{
kWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno);
error(KIO::ERR_COULD_NOT_WRITE, dest);
#if HAVE_POSIX_ACL
if (acl) acl_free(acl);
#endif
return;
}
// set final permissions
if ( _mode != -1 )
{
if ( (::chmod(_dest.data(), _mode) != 0)
#if HAVE_POSIX_ACL
|| (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
#endif
)
{
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest);
// Eat the error if the filesystem apparently doesn't support chmod.
if ( mp && mp->testFileSystemFlag( KMountPoint::SupportsChmod ) )
warning(i18n("Could not change permissions for\n%1", dest));
}
}
#if HAVE_POSIX_ACL
if (acl) acl_free(acl);
#endif
// copy access and modification time
struct utimbuf ut;
ut.actime = buff_src.st_atime;
ut.modtime = buff_src.st_mtime;
if ( ::utime( _dest.data(), &ut ) != 0 )
{
kWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg(dest);
}
processedSize( buff_src.st_size );
finished();
}
static bool isLocalFileSameHost(const QUrl& url)
{
if (!url.isLocalFile())
return false;
if (url.host().isEmpty() || (url.host() == QLatin1String("localhost")))
return true;
char hostname[ 256 ];
hostname[ 0 ] = '\0';
if (!gethostname(hostname, 255))
hostname[sizeof(hostname)-1] = '\0';
return (QString::compare(url.host(), QLatin1String(hostname), Qt::CaseInsensitive) == 0);
}
void FileProtocol::listDir( const QUrl& url)
{
if (!isLocalFileSameHost(url)) {
QUrl redir(url);
redir.setScheme(config()->readEntry("DefaultRemoteProtocol", "smb"));
redirection(redir);
kDebug(7101) << "redirecting to " << redir;
finished();
return;
}
const QString path(url.toLocalFile());
const QByteArray _path(QFile::encodeName(path));
DIR* dp = opendir(_path.data());
if (dp == 0) {
switch (errno) {
case ENOENT:
error(KIO::ERR_DOES_NOT_EXIST, path);
return;
case ENOTDIR:
error(KIO::ERR_IS_FILE, path);
break;
#ifdef ENOMEDIUM
case ENOMEDIUM:
error(ERR_SLAVE_DEFINED,
i18n("No media in device for %1", path));
break;
#endif
default:
error(KIO::ERR_CANNOT_ENTER_DIRECTORY, path);
break;
}
return;
}
/* set the current dir to the path to speed up
in not having to pass an absolute path.
We restore the path later to get out of the
path - the kernel wouldn't unmount or delete
directories we keep as active directory. And
as the slave runs in the background, it's hard
to see for the user what the problem would be */
const QString pathBuffer(QDir::currentPath());
if (!QDir::setCurrent(path)) {
error(ERR_CANNOT_ENTER_DIRECTORY, path);
return;
}
const QString sDetails = metaData(QLatin1String("details"));
const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
//kDebug(7101) << "========= LIST " << url << "details=" << details << " =========";
UDSEntry entry;
#ifndef HAVE_DIRENT_D_TYPE
KDE_struct_stat st;
#endif
KDE_struct_dirent *ep;
while ((ep = KDE_readdir(dp)) != 0 ) {
entry.clear();
const QString filename = QFile::decodeName(ep->d_name);
/*
* details == 0 (if statement) is the fast code path.
* We only get the file name and type. After that we emit
* the result.
*
* The else statement is the slow path that requests all
* file information in file.cpp. It executes a stat call
* for every entry thus becoming slower.
*
*/
if (details == 0) {
entry.insert(KIO::UDSEntry::UDS_NAME, filename);
#ifdef HAVE_DIRENT_D_TYPE
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,
(ep->d_type & DT_DIR) ? S_IFDIR : S_IFREG );
const bool isSymLink = (ep->d_type & DT_LNK);
#else
// oops, no fast way, we need to stat (e.g. on Solaris)
if (KDE_lstat(ep->d_name, &st) == -1) {
continue; // how can stat fail?
}
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,
(S_ISDIR(st.st_mode)) ? S_IFDIR : S_IFREG );
const bool isSymLink = S_ISLNK(st.st_mode);
#endif
if (isSymLink) {
// for symlinks obey the UDSEntry contract and provide UDS_LINK_DEST
// even if we don't know the link dest (and DeleteJob doesn't care...)
entry.insert(KIO::UDSEntry::UDS_LINK_DEST, QLatin1String("Dummy Link Target"));
}
listEntry(entry, false);
} else {
if (createUDSEntry(filename, QByteArray(ep->d_name), entry, details, true)) {
listEntry(entry, false);
}
}
}
closedir(dp);
listEntry(entry, true); // ready
// Restore the path
QDir::setCurrent(pathBuffer);
finished();
}
void FileProtocol::rename( const QUrl &srcUrl, const QUrl &destUrl,
KIO::JobFlags _flags )
{
char off_t_should_be_64_bits[sizeof(off_t) >= 8 ? 1 : -1]; (void) off_t_should_be_64_bits;
const QString src = srcUrl.toLocalFile();
const QString dest = destUrl.toLocalFile();
const QByteArray _src(QFile::encodeName(src));
const QByteArray _dest(QFile::encodeName(dest));
KDE_struct_stat buff_src;
if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) {
if ( errno == EACCES )
error(KIO::ERR_ACCESS_DENIED, src);
else
error(KIO::ERR_DOES_NOT_EXIST, src);
return;
}
KDE_struct_stat buff_dest;
// stat symlinks here (lstat, not stat), to avoid ERR_IDENTICAL_FILES when replacing symlink
// with its target (#169547)
bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
if ( dest_exists )
{
if (S_ISDIR(buff_dest.st_mode))
{
error(KIO::ERR_DIR_ALREADY_EXIST, dest);
return;
}
if ( same_inode( buff_dest, buff_src) )
{
error(KIO::ERR_IDENTICAL_FILES, dest);
return;
}
if (!(_flags & KIO::Overwrite))
{
error(KIO::ERR_FILE_ALREADY_EXIST, dest);
return;
}
}
if ( KDE_rename( _src.data(), _dest.data()))
{
if (( errno == EACCES ) || (errno == EPERM)) {
error(KIO::ERR_ACCESS_DENIED, dest);
}
else if (errno == EXDEV) {
error(KIO::ERR_UNSUPPORTED_ACTION, QLatin1String("rename"));
}
else if (errno == EROFS) { // The file is on a read-only filesystem
error(KIO::ERR_CANNOT_DELETE, src);
}
else {
error(KIO::ERR_CANNOT_RENAME, src);
}
return;
}
finished();
}
void FileProtocol::symlink( const QString &target, const QUrl &destUrl, KIO::JobFlags flags )
{
const QString dest = destUrl.toLocalFile();
// Assume dest is local too (wouldn't be here otherwise)
if ( ::symlink( QFile::encodeName(target), QFile::encodeName(dest) ) == -1 )
{
// Does the destination already exist ?
if ( errno == EEXIST )
{
if ( (flags & KIO::Overwrite) )
{
// Try to delete the destination
if ( unlink( QFile::encodeName(dest) ) != 0 )
{
error(KIO::ERR_CANNOT_DELETE, dest);
return;
}
// Try again - this won't loop forever since unlink succeeded
symlink( target, destUrl, flags );
}
else
{
KDE_struct_stat buff_dest;
KDE_lstat( QFile::encodeName(dest), &buff_dest );
if (S_ISDIR(buff_dest.st_mode))
error(KIO::ERR_DIR_ALREADY_EXIST, dest);
else
error(KIO::ERR_FILE_ALREADY_EXIST, dest);
return;
}
}
else
{
// Some error occurred while we tried to symlink
error(KIO::ERR_CANNOT_SYMLINK, dest);
return;
}
}
finished();
}
void FileProtocol::del(const QUrl& url, bool isfile)
{
const QString path = url.toLocalFile();
const QByteArray _path( QFile::encodeName(path));
/*****
* Delete files
*****/
if (isfile) {
kDebug(7101) << "Deleting file "<< url;
if ( unlink( _path.data() ) == -1 ) {
if ((errno == EACCES) || (errno == EPERM))
error(KIO::ERR_ACCESS_DENIED, path);
else if (errno == EISDIR)
error(KIO::ERR_IS_DIRECTORY, path);
else
error(KIO::ERR_CANNOT_DELETE, path);
return;
}
} else {
/*****
* Delete empty directory
*****/
kDebug( 7101 ) << "Deleting directory " << url;
if (metaData(QLatin1String("recurse")) == QLatin1String("true")) {
if (!deleteRecursive(path))
return;
}
if ( ::rmdir( _path.data() ) == -1 ) {
if ((errno == EACCES) || (errno == EPERM))
error(KIO::ERR_ACCESS_DENIED, path);
else {
kDebug( 7101 ) << "could not rmdir " << perror;
error(KIO::ERR_COULD_NOT_RMDIR, path);
return;
}
}
}
finished();
}
void FileProtocol::chown( const QUrl& url, const QString& owner, const QString& group )
{
const QString path = url.toLocalFile();
const QByteArray _path( QFile::encodeName(path) );
uid_t uid;
gid_t gid;
// get uid from given owner
{
- struct passwd *p = ::getpwnam(owner.toAscii());
+ struct passwd *p = ::getpwnam(owner.toLatin1());
if ( ! p ) {
error( KIO::ERR_SLAVE_DEFINED,
i18n( "Could not get user id for given user name %1", owner ) );
return;
}
uid = p->pw_uid;
}
// get gid from given group
{
- struct group *p = ::getgrnam(group.toAscii());
+ struct group *p = ::getgrnam(group.toLatin1());
if ( ! p ) {
error( KIO::ERR_SLAVE_DEFINED,
i18n( "Could not get group id for given group name %1", group ) );
return;
}
gid = p->gr_gid;
}
if ( ::chown(_path, uid, gid) == -1 ) {
switch ( errno ) {
case EPERM:
case EACCES:
error(KIO::ERR_ACCESS_DENIED, path);
break;
case ENOSPC:
error(KIO::ERR_DISK_FULL, path);
break;
default:
error(KIO::ERR_CANNOT_CHOWN, path);
}
} else
finished();
}
void FileProtocol::stat( const QUrl & url )
{
if (!url.isLocalFile()) {
QUrl redir(url);
redir.setScheme(config()->readEntry("DefaultRemoteProtocol", "smb"));
redirection(redir);
kDebug(7101) << "redirecting to " << redir;
finished();
return;
}
/* directories may not have a slash at the end if
* we want to stat() them; it requires that we
* change into it .. which may not be allowed
* stat("/is/unaccessible") -> rwx------
* stat("/is/unaccessible/") -> EPERM H.Z.
* This is the reason for the -1
*/
const QUrlPathInfo pathInfo(url);
const QString path(pathInfo.localPath(QUrlPathInfo::StripTrailingSlash));
const QByteArray _path(QFile::encodeName(path));
const QString sDetails = metaData(QLatin1String("details"));
const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
UDSEntry entry;
if (!createUDSEntry(pathInfo.fileName(), _path, entry, details, true /*with acls*/)) {
error(KIO::ERR_DOES_NOT_EXIST, path);
return;
}
#if 0
///////// debug code
MetaData::iterator it1 = mOutgoingMetaData.begin();
for ( ; it1 != mOutgoingMetaData.end(); it1++ ) {
kDebug(7101) << it1.key() << " = " << it1.data();
}
/////////
#endif
statEntry( entry );
finished();
}
diff --git a/kioslave/ftp/ftp.cpp b/kioslave/ftp/ftp.cpp
index cced3f3883..d5ca32ba61 100644
--- a/kioslave/ftp/ftp.cpp
+++ b/kioslave/ftp/ftp.cpp
@@ -1,2684 +1,2684 @@
// -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; c-file-style: "stroustrup" -*-
/* This file is part of the KDE libraries
Copyright (C) 2000-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.
*/
/*
Recommended reading explaining FTP details and quirks:
http://cr.yp.to/ftp.html (by D.J. Bernstein)
RFC:
RFC 959 "File Transfer Protocol (FTP)"
RFC 1635 "How to Use Anonymous FTP"
RFC 2428 "FTP Extensions for IPv6 and NATs" (defines EPRT and EPSV)
*/
#include <config-kioslave-ftp.h>
#define KIO_FTP_PRIVATE_INCLUDE
#include "ftp.h"
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#if TIME_WITH_SYS_TIME
#include <ctime>
#endif
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QAuthenticator>
#include <qmimedatabase.h>
#include <qurlpathinfo.h>
#include <kdebug.h>
#include <klocale.h>
#include <kio/ioslave_defaults.h>
#include <kremoteencoding.h>
#include <ksocketfactory.h>
#include <kde_file.h>
#include <kconfiggroup.h>
#if HAVE_STRTOLL
#define charToLongLong(a) strtoll(a, 0, 10)
#else
#define charToLongLong(a) strtol(a, 0, 10)
#endif
#define FTP_LOGIN "anonymous"
#define FTP_PASSWD "anonymous@"
//#undef kDebug
#define ENABLE_CAN_RESUME
static QString ftpCleanPath(const QString& path)
{
if (path.endsWith(QLatin1String(";type=A"), Qt::CaseInsensitive) ||
path.endsWith(QLatin1String(";type=I"), Qt::CaseInsensitive) ||
path.endsWith(QLatin1String(";type=D"), Qt::CaseInsensitive)) {
return path.left((path.length() - qstrlen(";type=X")));
}
return path;
}
static char ftpModeFromPath(const QString& path, char defaultMode = '\0')
{
const int index = path.lastIndexOf(QLatin1String(";type="));
if (index > -1 && (index+6) < path.size()) {
const QChar mode = path.at(index+6);
// kio_ftp supports only A (ASCII) and I(BINARY) modes.
if (mode == QLatin1Char('A') || mode == QLatin1Char('a') ||
mode == QLatin1Char('I') || mode == QLatin1Char('i')) {
return mode.toUpper().toLatin1();
}
}
return defaultMode;
}
static bool supportedProxyScheme(const QString& scheme)
{
return (scheme == QLatin1String("ftp") || scheme == QLatin1String("socks"));
}
static bool isSocksProxy()
{
return (QNetworkProxy::applicationProxy().type() == QNetworkProxy::Socks5Proxy);
}
// JPF: somebody should find a better solution for this or move this to KIO
// JPF: anyhow, in KDE 3.2.0 I found diffent MAX_IPC_SIZE definitions!
namespace KIO {
enum buffersizes
{ /**
* largest buffer size that should be used to transfer data between
* KIO slaves using the data() function
*/
maximumIpcSize = 32 * 1024,
/**
* this is a reasonable value for an initial read() that a KIO slave
* can do to obtain data via a slow network connection.
*/
initialIpcSize = 2 * 1024,
/**
* recommended size of a data block passed to findBufferFileType()
*/
minimumMimeSize = 1024
};
// JPF: this helper was derived from write_all in file.cc (FileProtocol).
static // JPF: in ftp.cc we make it static
/**
* This helper handles some special issues (blocking and interrupted
* system call) when writing to a file handle.
*
* @return 0 on success or an error code on failure (ERR_COULD_NOT_WRITE,
* ERR_DISK_FULL, ERR_CONNECTION_BROKEN).
*/
int WriteToFile(int fd, const char *buf, size_t len)
{
while (len > 0)
{ // JPF: shouldn't there be a KDE_write?
ssize_t written = write(fd, buf, len);
if (written >= 0)
{ buf += written;
len -= written;
continue;
}
switch(errno)
{ case EINTR: continue;
case EPIPE: return ERR_CONNECTION_BROKEN;
case ENOSPC: return ERR_DISK_FULL;
default: return ERR_COULD_NOT_WRITE;
}
}
return 0;
}
}
KIO::filesize_t Ftp::UnknownSize = (KIO::filesize_t)-1;
using namespace KIO;
extern "C" Q_DECL_EXPORT int kdemain( int argc, char **argv )
{
QCoreApplication app(argc, argv);
app.setApplicationName(QLatin1String("kio_ftp"));
( void ) KLocale::global();
kDebug(7102) << "Starting " << getpid();
if (argc != 4)
{
fprintf(stderr, "Usage: kio_ftp protocol domain-socket1 domain-socket2\n");
exit(-1);
}
Ftp slave(argv[2], argv[3]);
slave.dispatchLoop();
kDebug(7102) << "Done";
return 0;
}
//===============================================================================
// Ftp
//===============================================================================
Ftp::Ftp( const QByteArray &pool, const QByteArray &app )
: SlaveBase( "ftp", pool, app )
{
// init the socket data
m_data = m_control = NULL;
m_server = NULL;
ftpCloseControlConnection();
// init other members
m_port = 0;
m_socketProxyAuth = 0;
}
Ftp::~Ftp()
{
kDebug(7102);
closeConnection();
}
/**
* This closes a data connection opened by ftpOpenDataConnection().
*/
void Ftp::ftpCloseDataConnection()
{
delete m_data;
m_data = NULL;
delete m_server;
m_server = NULL;
}
/**
* This closes a control connection opened by ftpOpenControlConnection() and reinits the
* related states. This method gets called from the constructor with m_control = NULL.
*/
void Ftp::ftpCloseControlConnection()
{
m_extControl = 0;
delete m_control;
m_control = NULL;
m_cDataMode = 0;
m_bLoggedOn = false; // logon needs control connction
m_bTextMode = false;
m_bBusy = false;
}
/**
* Returns the last response from the server (iOffset >= 0) -or- reads a new response
* (iOffset < 0). The result is returned (with iOffset chars skipped for iOffset > 0).
*/
const char* Ftp::ftpResponse(int iOffset)
{
Q_ASSERT(m_control != NULL); // must have control connection socket
const char *pTxt = m_lastControlLine.data();
// read the next line ...
if(iOffset < 0)
{
int iMore = 0;
m_iRespCode = 0;
if (!pTxt) return 0; // avoid using a NULL when calling atoi.
// If the server sends a multiline response starting with
// "nnn-text" we loop here until a final "nnn text" line is
// reached. Only data from the final line will be stored.
do {
while (!m_control->canReadLine() && m_control->waitForReadyRead((readTimeout() * 1000))) {}
m_lastControlLine = m_control->readLine();
pTxt = m_lastControlLine.data();
int iCode = atoi(pTxt);
if (iMore == 0) {
// first line
kDebug(7102) << " > " << pTxt;
if(iCode >= 100) {
m_iRespCode = iCode;
if (pTxt[3] == '-') {
// marker for a multiple line response
iMore = iCode;
}
} else {
kWarning(7102) << "Cannot parse valid code from line" << pTxt;
}
} else {
// multi-line
kDebug(7102) << " > " << pTxt;
if (iCode >= 100 && iCode == iMore && pTxt[3] == ' ') {
iMore = 0;
}
}
} while(iMore != 0);
kDebug(7102) << "resp> " << pTxt;
m_iRespType = (m_iRespCode > 0) ? m_iRespCode / 100 : 0;
}
// return text with offset ...
while(iOffset-- > 0 && pTxt[0])
pTxt++;
return pTxt;
}
void Ftp::closeConnection()
{
if(m_control != NULL || m_data != NULL)
kDebug(7102) << "m_bLoggedOn=" << m_bLoggedOn << " m_bBusy=" << m_bBusy;
if(m_bBusy) // ftpCloseCommand not called
{
kWarning(7102) << "Abandoned data stream";
ftpCloseDataConnection();
}
if(m_bLoggedOn) // send quit
{
if( !ftpSendCmd( "quit", 0 ) || (m_iRespType != 2) )
kWarning(7102) << "QUIT returned error: " << m_iRespCode;
}
// close the data and control connections ...
ftpCloseDataConnection();
ftpCloseControlConnection();
}
void Ftp::setHost( const QString& _host, quint16 _port, const QString& _user,
const QString& _pass )
{
kDebug(7102) << _host << "port=" << _port << "user=" << _user;
m_proxyURL.clear();
m_proxyUrls = config()->readEntry("ProxyUrls", QStringList());
kDebug(7102) << "proxy urls:" << m_proxyUrls;
if ( m_host != _host || m_port != _port ||
m_user != _user || m_pass != _pass )
closeConnection();
m_host = _host;
m_port = _port;
m_user = _user;
m_pass = _pass;
}
void Ftp::openConnection()
{
ftpOpenConnection(loginExplicit);
}
bool Ftp::ftpOpenConnection (LoginMode loginMode)
{
// check for implicit login if we are already logged on ...
if(loginMode == loginImplicit && m_bLoggedOn)
{
Q_ASSERT(m_control != NULL); // must have control connection socket
return true;
}
kDebug(7102) << "host=" << m_host << ", port=" << m_port << ", user=" << m_user << "password= [password hidden]";
infoMessage( i18n("Opening connection to host %1", m_host) );
if ( m_host.isEmpty() )
{
error( ERR_UNKNOWN_HOST, QString() );
return false;
}
Q_ASSERT( !m_bLoggedOn );
m_initialPath.clear();
m_currentPath.clear();
if (!ftpOpenControlConnection() )
return false; // error emitted by ftpOpenControlConnection
infoMessage( i18n("Connected to host %1", m_host) );
bool userNameChanged = false;
if(loginMode != loginDefered)
{
m_bLoggedOn = ftpLogin(&userNameChanged);
if( !m_bLoggedOn )
return false; // error emitted by ftpLogin
}
m_bTextMode = config()->readEntry("textmode", false);
connected();
// Redirected due to credential change...
if (userNameChanged && m_bLoggedOn)
{
QUrl realURL;
realURL.setScheme( "ftp" );
if (m_user != FTP_LOGIN)
realURL.setUserName( m_user );
if (m_pass != FTP_PASSWD)
realURL.setPassword( m_pass );
realURL.setHost( m_host );
if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
realURL.setPort( m_port );
if ( m_initialPath.isEmpty() )
m_initialPath = '/';
realURL.setPath( m_initialPath );
kDebug(7102) << "User name changed! Redirecting to" << realURL;
redirection( realURL );
finished();
return false;
}
return true;
}
/**
* Called by @ref openConnection. It opens the control connection to the ftp server.
*
* @return true on success.
*/
bool Ftp::ftpOpenControlConnection()
{
if (m_proxyUrls.isEmpty())
return ftpOpenControlConnection(m_host, m_port);
int errorCode = 0;
QString errorMessage;
Q_FOREACH (const QString& proxyUrl, m_proxyUrls) {
const QUrl url(proxyUrl);
const QString scheme(url.scheme());
if (!supportedProxyScheme(scheme)) {
// TODO: Need a new error code to indicate unsupported URL scheme.
errorCode = ERR_COULD_NOT_CONNECT;
errorMessage = url.toString();
continue;
}
if (scheme == QLatin1String("socks")) {
kDebug(7102) << "Connecting to SOCKS proxy @" << url;
const int proxyPort = url.port();
QNetworkProxy proxy (QNetworkProxy::Socks5Proxy, url.host(), (proxyPort == -1 ? 0 : proxyPort));
QNetworkProxy::setApplicationProxy(proxy);
if (ftpOpenControlConnection(m_host, m_port)) {
return true;
}
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
} else {
if (ftpOpenControlConnection(url.host(), url.port())) {
m_proxyURL = url;
return true;
}
}
}
if (errorCode) {
error(errorCode, errorMessage);
}
return false;
}
bool Ftp::ftpOpenControlConnection( const QString &host, int port )
{
// implicitly close, then try to open a new connection ...
closeConnection();
QString sErrorMsg;
// now connect to the server and read the login message ...
if (port == 0)
port = 21; // default FTP port
m_control = KSocketFactory::synchronousConnectToHost(QLatin1String("ftp"), host, port, connectTimeout() * 1000);
connect(m_control, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthentication(QNetworkProxy,QAuthenticator*)));
int iErrorCode = m_control->state() == QAbstractSocket::ConnectedState ? 0 : ERR_COULD_NOT_CONNECT;
// on connect success try to read the server message...
if(iErrorCode == 0)
{
const char* psz = ftpResponse(-1);
if(m_iRespType != 2)
{ // login not successful, do we have an message text?
if(psz[0])
sErrorMsg = i18n("%1.\n\nReason: %2", host, psz);
iErrorCode = ERR_COULD_NOT_CONNECT;
}
}
else
{
if (m_control->error() == QAbstractSocket::HostNotFoundError)
iErrorCode = ERR_UNKNOWN_HOST;
sErrorMsg = QString("%1: %2").arg(host).arg(m_control->errorString());
}
// if there was a problem - report it ...
if(iErrorCode == 0) // OK, return success
return true;
closeConnection(); // clean-up on error
error(iErrorCode, sErrorMsg);
return false;
}
/**
* Called by @ref openConnection. It logs us in.
* @ref m_initialPath is set to the current working directory
* if logging on was successful.
*
* @return true on success.
*/
bool Ftp::ftpLogin(bool* userChanged)
{
infoMessage( i18n("Sending login information") );
Q_ASSERT( !m_bLoggedOn );
QString user (m_user);
QString pass (m_pass);
if ( config()->readEntry("EnableAutoLogin", false) )
{
QString au = config()->readEntry("autoLoginUser");
if ( !au.isEmpty() )
{
user = au;
pass = config()->readEntry("autoLoginPass");
}
}
AuthInfo info;
info.url.setScheme( "ftp" );
info.url.setHost( m_host );
if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
info.url.setPort( m_port );
if (!user.isEmpty())
info.url.setUserName(user);
// Check for cached authentication first and fallback to
// anonymous login when no stored credentials are found.
if (!config()->readEntry("TryAnonymousLoginFirst", false) &&
pass.isEmpty() && checkCachedAuthentication(info))
{
user = info.username;
pass = info.password;
}
// Try anonymous login if both username/password
// information is blank.
if (user.isEmpty() && pass.isEmpty())
{
user = FTP_LOGIN;
pass = FTP_PASSWD;
}
QByteArray tempbuf;
QString lastServerResponse;
int failedAuth = 0;
bool promptForRetry = false;
// Give the user the option to login anonymously...
info.setExtraField(QLatin1String("anonymous"), false);
do
{
// Check the cache and/or prompt user for password if 1st
// login attempt failed OR the user supplied a login name,
// but no password.
if ( failedAuth > 0 || (!user.isEmpty() && pass.isEmpty()) )
{
QString errorMsg;
kDebug(7102) << "Prompting user for login info...";
// Ask user if we should retry after when login fails!
if( failedAuth > 0 && promptForRetry)
{
errorMsg = i18n("Message sent:\nLogin using username=%1 and "
"password=[hidden]\n\nServer replied:\n%2\n\n"
, user, lastServerResponse);
}
if ( user != FTP_LOGIN )
info.username = user;
info.prompt = i18n("You need to supply a username and a password "
"to access this site.");
info.commentLabel = i18n( "Site:" );
info.comment = i18n("<b>%1</b>", m_host );
info.keepPassword = true; // Prompt the user for persistence as well.
info.setModified(false); // Default the modified flag since we reuse authinfo.
bool disablePassDlg = config()->readEntry( "DisablePassDlg", false );
if ( disablePassDlg || !openPasswordDialog( info, errorMsg ) )
{
error( ERR_USER_CANCELED, m_host );
return false;
}
else
{
// User can decide go anonymous using checkbox
if( info.getExtraField( "anonymous" ).toBool() )
{
user = FTP_LOGIN;
pass = FTP_PASSWD;
}
else
{
user = info.username;
pass = info.password;
}
promptForRetry = true;
}
}
tempbuf = "USER ";
tempbuf += user.toLatin1();
if ( m_proxyURL.isValid() )
{
tempbuf += '@';
tempbuf += m_host.toLatin1();
if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
{
tempbuf += ':';
tempbuf += QString::number(m_port).toLatin1();
}
}
kDebug(7102) << "Sending Login name: " << tempbuf;
bool loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
bool needPass = (m_iRespCode == 331);
// Prompt user for login info if we do not
// get back a "230" or "331".
if ( !loggedIn && !needPass )
{
lastServerResponse = ftpResponse(0);
kDebug(7102) << "Login failed: " << lastServerResponse;
++failedAuth;
continue; // Well we failed, prompt the user please!!
}
if( needPass )
{
tempbuf = "PASS ";
tempbuf += pass.toLatin1();
kDebug(7102) << "Sending Login password: " << "[protected]";
loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
}
if ( loggedIn )
{
// Make sure the user name changed flag is properly set.
if (userChanged)
*userChanged = (!m_user.isEmpty() && (m_user != user));
// Do not cache the default login!!
if( user != FTP_LOGIN && pass != FTP_PASSWD )
{
// Update the username in case it was changed during login.
if (!m_user.isEmpty()) {
info.url.setUserName(user);
m_user = user;
}
// Cache the password if the user requested it.
if (info.keepPassword) {
cacheAuthentication(info);
}
}
failedAuth = -1;
}
else
{
// some servers don't let you login anymore
// if you fail login once, so restart the connection here
lastServerResponse = ftpResponse(0);
if (!ftpOpenControlConnection())
{
return false;
}
}
} while( ++failedAuth );
kDebug(7102) << "Login OK";
infoMessage( i18n("Login OK") );
// Okay, we're logged in. If this is IIS 4, switch dir listing style to Unix:
// Thanks to jk@soegaard.net (Jens Kristian Sgaard) for this hint
if( ftpSendCmd("SYST") && (m_iRespType == 2) )
{
if( !qstrncmp( ftpResponse(0), "215 Windows_NT", 14 ) ) // should do for any version
{
ftpSendCmd( "site dirstyle" );
// Check if it was already in Unix style
// Patch from Keith Refson <Keith.Refson@earth.ox.ac.uk>
if( !qstrncmp( ftpResponse(0), "200 MSDOS-like directory output is on", 37 ))
//It was in Unix style already!
ftpSendCmd( "site dirstyle" );
// windows won't support chmod before KDE konquers their desktop...
m_extControl |= chmodUnknown;
}
}
else
kWarning(7102) << "SYST failed";
if ( config()->readEntry ("EnableAutoLoginMacro", false) )
ftpAutoLoginMacro ();
// Get the current working directory
kDebug(7102) << "Searching for pwd";
if( !ftpSendCmd("PWD") || (m_iRespType != 2) )
{
kDebug(7102) << "Couldn't issue pwd command";
error( ERR_COULD_NOT_LOGIN, i18n("Could not login to %1.", m_host) ); // or anything better ?
return false;
}
QString sTmp = remoteEncoding()->decode( ftpResponse(3) );
int iBeg = sTmp.indexOf('"');
int iEnd = sTmp.lastIndexOf('"');
if(iBeg > 0 && iBeg < iEnd)
{
m_initialPath = sTmp.mid(iBeg+1, iEnd-iBeg-1);
if(m_initialPath[0] != '/') m_initialPath.prepend('/');
kDebug(7102) << "Initial path set to: " << m_initialPath;
m_currentPath = m_initialPath;
}
return true;
}
void Ftp::ftpAutoLoginMacro ()
{
QString macro = metaData( "autoLoginMacro" );
if ( macro.isEmpty() )
return;
const QStringList list = macro.split('\n',QString::SkipEmptyParts);
for(QStringList::const_iterator it = list.begin() ; it != list.end() ; ++it )
{
if ( (*it).startsWith(QLatin1String("init")) )
{
const QStringList list2 = macro.split( '\\',QString::SkipEmptyParts);
it = list2.begin();
++it; // ignore the macro name
for( ; it != list2.end() ; ++it )
{
// TODO: Add support for arbitrary commands
// besides simply changing directory!!
if ( (*it).startsWith( QLatin1String("cwd") ) )
ftpFolder( (*it).mid(4), false );
}
break;
}
}
}
/**
* ftpSendCmd - send a command (@p cmd) and read response
*
* @param maxretries number of time it should retry. Since it recursively
* calls itself if it can't read the answer (this happens especially after
* timeouts), we need to limit the recursiveness ;-)
*
* return true if any response received, false on error
*/
bool Ftp::ftpSendCmd( const QByteArray& cmd, int maxretries )
{
Q_ASSERT(m_control != NULL); // must have control connection socket
if ( cmd.indexOf( '\r' ) != -1 || cmd.indexOf( '\n' ) != -1)
{
kWarning(7102) << "Invalid command received (contains CR or LF):"
<< cmd.data();
error( ERR_UNSUPPORTED_ACTION, m_host );
return false;
}
// Don't print out the password...
bool isPassCmd = (cmd.left(4).toLower() == "pass");
if ( !isPassCmd )
kDebug(7102) << "send> " << cmd.data();
else
kDebug(7102) << "send> pass [protected]";
// Send the message...
QByteArray buf = cmd;
buf += "\r\n"; // Yes, must use CR/LF - see http://cr.yp.to/ftp/request.html
int num = m_control->write(buf);
while (m_control->bytesToWrite() && m_control->waitForBytesWritten()) {}
// If we were able to successfully send the command, then we will
// attempt to read the response. Otherwise, take action to re-attempt
// the login based on the maximum number of retries specified...
if( num > 0 )
ftpResponse(-1);
else
{
m_iRespType = m_iRespCode = 0;
}
// If respCh is NULL or the response is 421 (Timed-out), we try to re-send
// the command based on the value of maxretries.
if( (m_iRespType <= 0) || (m_iRespCode == 421) )
{
// We have not yet logged on...
if (!m_bLoggedOn)
{
// The command was sent from the ftpLogin function, i.e. we are actually
// attempting to login in. NOTE: If we already sent the username, we
// return false and let the user decide whether (s)he wants to start from
// the beginning...
if (maxretries > 0 && !isPassCmd)
{
closeConnection ();
if( ftpOpenConnection(loginDefered) )
ftpSendCmd ( cmd, maxretries - 1 );
}
return false;
}
else
{
if ( maxretries < 1 )
return false;
else
{
kDebug(7102) << "Was not able to communicate with " << m_host
<< "Attempting to re-establish connection.";
closeConnection(); // Close the old connection...
openConnection(); // Attempt to re-establish a new connection...
if (!m_bLoggedOn)
{
if (m_control != NULL) // if openConnection succeeded ...
{
kDebug(7102) << "Login failure, aborting";
error (ERR_COULD_NOT_LOGIN, m_host);
closeConnection ();
}
return false;
}
kDebug(7102) << "Logged back in, re-issuing command";
// If we were able to login, resend the command...
if (maxretries)
maxretries--;
return ftpSendCmd( cmd, maxretries );
}
}
}
return true;
}
/*
* ftpOpenPASVDataConnection - set up data connection, using PASV mode
*
* return 0 if successful, ERR_INTERNAL otherwise
* doesn't set error message, since non-pasv mode will always be tried if
* this one fails
*/
int Ftp::ftpOpenPASVDataConnection()
{
Q_ASSERT(m_control != NULL); // must have control connection socket
Q_ASSERT(m_data == NULL); // ... but no data connection
// Check that we can do PASV
QHostAddress address = m_control->peerAddress();
if (address.protocol() != QAbstractSocket::IPv4Protocol)
return ERR_INTERNAL; // no PASV for non-PF_INET connections
if (m_extControl & pasvUnknown)
return ERR_INTERNAL; // already tried and got "unknown command"
m_bPasv = true;
/* Let's PASsiVe*/
if( !ftpSendCmd("PASV") || (m_iRespType != 2) )
{
kDebug(7102) << "PASV attempt failed";
// unknown command?
if( m_iRespType == 5 )
{
kDebug(7102) << "disabling use of PASV";
m_extControl |= pasvUnknown;
}
return ERR_INTERNAL;
}
// The usual answer is '227 Entering Passive Mode. (160,39,200,55,6,245)'
// but anonftpd gives '227 =160,39,200,55,6,245'
int i[6];
const char *start = strchr(ftpResponse(3), '(');
if ( !start )
start = strchr(ftpResponse(3), '=');
if ( !start ||
( sscanf(start, "(%d,%d,%d,%d,%d,%d)",&i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 &&
sscanf(start, "=%d,%d,%d,%d,%d,%d", &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 ) )
{
kError(7102) << "parsing IP and port numbers failed. String parsed: " << start;
return ERR_INTERNAL;
}
// we ignore the host part on purpose for two reasons
// a) it might be wrong anyway
// b) it would make us being suceptible to a port scanning attack
// now connect the data socket ...
quint16 port = i[4] << 8 | i[5];
const QString host = (isSocksProxy() ? m_host : address.toString());
m_data = KSocketFactory::synchronousConnectToHost("ftp-data", host, port, connectTimeout() * 1000);
return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL;
}
/*
* ftpOpenEPSVDataConnection - opens a data connection via EPSV
*/
int Ftp::ftpOpenEPSVDataConnection()
{
Q_ASSERT(m_control != NULL); // must have control connection socket
Q_ASSERT(m_data == NULL); // ... but no data connection
QHostAddress address = m_control->peerAddress();
int portnum;
if (m_extControl & epsvUnknown)
return ERR_INTERNAL;
m_bPasv = true;
if( !ftpSendCmd("EPSV") || (m_iRespType != 2) )
{
// unknown command?
if( m_iRespType == 5 )
{
kDebug(7102) << "disabling use of EPSV";
m_extControl |= epsvUnknown;
}
return ERR_INTERNAL;
}
const char *start = strchr(ftpResponse(3), '|');
if ( !start || sscanf(start, "|||%d|", &portnum) != 1)
return ERR_INTERNAL;
const QString host = (isSocksProxy() ? m_host : address.toString());
m_data = KSocketFactory::synchronousConnectToHost("ftp-data", host, portnum, connectTimeout() * 1000);
return m_data->isOpen() ? 0 : ERR_INTERNAL;
}
/*
* ftpOpenDataConnection - set up data connection
*
* The routine calls several ftpOpenXxxxConnection() helpers to find
* the best connection mode. If a helper cannot connect if returns
* ERR_INTERNAL - so this is not really an error! All other error
* codes are treated as fatal, e.g. they are passed back to the caller
* who is responsible for calling error(). ftpOpenPortDataConnection
* can be called as last try and it does never return ERR_INTERNAL.
*
* @return 0 if successful, err code otherwise
*/
int Ftp::ftpOpenDataConnection()
{
// make sure that we are logged on and have no data connection...
Q_ASSERT( m_bLoggedOn );
ftpCloseDataConnection();
int iErrCode = 0;
int iErrCodePASV = 0; // Remember error code from PASV
// First try passive (EPSV & PASV) modes
if( !config()->readEntry("DisablePassiveMode", false) )
{
iErrCode = ftpOpenPASVDataConnection();
if(iErrCode == 0)
return 0; // success
iErrCodePASV = iErrCode;
ftpCloseDataConnection();
if( !config()->readEntry("DisableEPSV", false) )
{
iErrCode = ftpOpenEPSVDataConnection();
if(iErrCode == 0)
return 0; // success
ftpCloseDataConnection();
}
// if we sent EPSV ALL already and it was accepted, then we can't
// use active connections any more
if (m_extControl & epsvAllSent)
return iErrCodePASV ? iErrCodePASV : iErrCode;
}
// fall back to port mode
iErrCode = ftpOpenPortDataConnection();
if(iErrCode == 0)
return 0; // success
ftpCloseDataConnection();
// prefer to return the error code from PASV if any, since that's what should have worked in the first place
return iErrCodePASV ? iErrCodePASV : iErrCode;
}
/*
* ftpOpenPortDataConnection - set up data connection
*
* @return 0 if successful, err code otherwise (but never ERR_INTERNAL
* because this is the last connection mode that is tried)
*/
int Ftp::ftpOpenPortDataConnection()
{
Q_ASSERT(m_control != NULL); // must have control connection socket
Q_ASSERT(m_data == NULL); // ... but no data connection
m_bPasv = false;
if (m_extControl & eprtUnknown)
return ERR_INTERNAL;
if (!m_server)
m_server = KSocketFactory::listen("ftp-data");
if (!m_server->isListening()) {
delete m_server;
m_server = NULL;
return ERR_COULD_NOT_LISTEN;
}
m_server->setMaxPendingConnections(1);
QString command;
QHostAddress localAddress = m_control->localAddress();
if (localAddress.protocol() == QAbstractSocket::IPv4Protocol)
{
struct
{
quint32 ip4;
quint16 port;
} data;
data.ip4 = localAddress.toIPv4Address();
data.port = m_server->serverPort();
unsigned char *pData = reinterpret_cast<unsigned char*>(&data);
command.sprintf("PORT %d,%d,%d,%d,%d,%d",pData[3],pData[2],pData[1],pData[0],pData[5],pData[4]);
}
else if (localAddress.protocol() == QAbstractSocket::IPv6Protocol)
{
command = QString("EPRT |2|%2|%3|").arg(localAddress.toString()).arg(m_server->serverPort());
}
if( ftpSendCmd(command.toLatin1()) && (m_iRespType == 2) )
{
return 0;
}
delete m_server;
m_server = NULL;
return ERR_INTERNAL;
}
bool Ftp::ftpOpenCommand( const char *_command, const QString & _path, char _mode,
int errorcode, KIO::fileoffset_t _offset )
{
int errCode = 0;
if( !ftpDataMode(ftpModeFromPath(_path, _mode)) )
errCode = ERR_COULD_NOT_CONNECT;
else
errCode = ftpOpenDataConnection();
if(errCode != 0)
{
error(errCode, m_host);
return false;
}
if ( _offset > 0 ) {
// send rest command if offset > 0, this applies to retr and stor commands
char buf[100];
sprintf(buf, "rest %lld", _offset);
if ( !ftpSendCmd( buf ) )
return false;
if( m_iRespType != 3 )
{
error( ERR_CANNOT_RESUME, _path ); // should never happen
return false;
}
}
QByteArray tmp = _command;
QString errormessage;
if ( !_path.isEmpty() ) {
tmp += ' ';
tmp += remoteEncoding()->encode(ftpCleanPath(_path));
}
if( !ftpSendCmd( tmp ) || (m_iRespType != 1) )
{
if( _offset > 0 && qstrcmp(_command, "retr") == 0 && (m_iRespType == 4) )
errorcode = ERR_CANNOT_RESUME;
// The error here depends on the command
errormessage = _path;
}
else
{
// Only now we know for sure that we can resume
if ( _offset > 0 && qstrcmp(_command, "retr") == 0 )
canResume();
if(m_server && !m_data) {
kDebug(7102) << "waiting for connection from remote.";
m_server->waitForNewConnection(connectTimeout() * 1000);
m_data = m_server->nextPendingConnection();
}
if(m_data) {
kDebug(7102) << "connected with remote.";
m_bBusy = true; // cleared in ftpCloseCommand
return true;
}
kDebug(7102) << "no connection received from remote.";
errorcode=ERR_COULD_NOT_ACCEPT;
errormessage=m_host;
return false;
}
error(errorcode, errormessage);
return false;
}
bool Ftp::ftpCloseCommand()
{
// first close data sockets (if opened), then read response that
// we got for whatever was used in ftpOpenCommand ( should be 226 )
ftpCloseDataConnection();
if(!m_bBusy)
return true;
kDebug(7102) << "ftpCloseCommand: reading command result";
m_bBusy = false;
if(!ftpResponse(-1) || (m_iRespType != 2) )
{
kDebug(7102) << "ftpCloseCommand: no transfer complete message";
return false;
}
return true;
}
void Ftp::mkdir( const QUrl & url, int permissions )
{
if( !ftpOpenConnection(loginImplicit) )
return;
const QByteArray encodedPath (remoteEncoding()->encode(url));
const QString path = QString::fromLatin1(encodedPath.constData(), encodedPath.size());
if( !ftpSendCmd( (QByteArray ("mkd ") + encodedPath) ) || (m_iRespType != 2) )
{
QString currentPath( m_currentPath );
// Check whether or not mkdir failed because
// the directory already exists...
if( ftpFolder( path, false ) )
{
error( ERR_DIR_ALREADY_EXIST, path );
// Change the directory back to what it was...
(void) ftpFolder( currentPath, false );
return;
}
error( ERR_COULD_NOT_MKDIR, path );
return;
}
if ( permissions != -1 )
{
// chmod the dir we just created, ignoring errors.
(void) ftpChmod( path, permissions );
}
finished();
}
void Ftp::rename( const QUrl& src, const QUrl& dst, KIO::JobFlags flags )
{
if( !ftpOpenConnection(loginImplicit) )
return;
// The actual functionality is in ftpRename because put needs it
if ( ftpRename( src.path(), dst.path(), flags ) )
finished();
}
bool Ftp::ftpRename(const QString & src, const QString & dst, KIO::JobFlags jobFlags)
{
Q_ASSERT(m_bLoggedOn);
// Must check if dst already exists, RNFR+RNTO overwrites by default (#127793).
if (!(jobFlags & KIO::Overwrite)) {
if (ftpFileExists(dst)) {
error(ERR_FILE_ALREADY_EXIST, dst);
return false;
}
}
if (ftpFolder(dst, false)) {
error(ERR_DIR_ALREADY_EXIST, dst);
return false;
}
// CD into parent folder
const int pos = src.lastIndexOf('/');
if (pos > 0) {
if(!ftpFolder(src.left(pos+1), false))
return false;
}
QByteArray from_cmd = "RNFR ";
from_cmd += remoteEncoding()->encode(src.mid(pos+1));
if (!ftpSendCmd(from_cmd) || (m_iRespType != 3)) {
error( ERR_CANNOT_RENAME, src );
return false;
}
QByteArray to_cmd = "RNTO ";
to_cmd += remoteEncoding()->encode(dst);
if (!ftpSendCmd(to_cmd) || (m_iRespType != 2)) {
error( ERR_CANNOT_RENAME, src );
return false;
}
return true;
}
void Ftp::del( const QUrl& url, bool isfile )
{
if( !ftpOpenConnection(loginImplicit) )
return;
// When deleting a directory, we must exit from it first
// The last command probably went into it (to stat it)
if ( !isfile )
ftpFolder(remoteEncoding()->directory(url), false); // ignore errors
QByteArray cmd = isfile ? "DELE " : "RMD ";
cmd += remoteEncoding()->encode(url);
if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
error( ERR_CANNOT_DELETE, url.path() );
else
finished();
}
bool Ftp::ftpChmod( const QString & path, int permissions )
{
Q_ASSERT( m_bLoggedOn );
if(m_extControl & chmodUnknown) // previous errors?
return false;
// we need to do bit AND 777 to get permissions, in case
// we were sent a full mode (unlikely)
QString cmd = QString::fromLatin1("SITE CHMOD ") + QString::number( permissions & 511, 8 /*octal*/ ) + ' ';
cmd += path;
ftpSendCmd(remoteEncoding()->encode(cmd));
if(m_iRespType == 2)
return true;
if(m_iRespCode == 500)
{
m_extControl |= chmodUnknown;
kDebug(7102) << "ftpChmod: CHMOD not supported - disabling";
}
return false;
}
void Ftp::chmod( const QUrl & url, int permissions )
{
if( !ftpOpenConnection(loginImplicit) )
return;
if ( !ftpChmod( url.path(), permissions ) )
error( ERR_CANNOT_CHMOD, url.path() );
else
finished();
}
void Ftp::ftpCreateUDSEntry( const QString & filename, const FtpEntry& ftpEnt, UDSEntry& entry, bool isDir )
{
Q_ASSERT(entry.count() == 0); // by contract :-)
entry.insert( KIO::UDSEntry::UDS_NAME, filename );
entry.insert( KIO::UDSEntry::UDS_SIZE, ftpEnt.size );
entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, ftpEnt.date );
entry.insert( KIO::UDSEntry::UDS_ACCESS, ftpEnt.access );
entry.insert( KIO::UDSEntry::UDS_USER, ftpEnt.owner );
if ( !ftpEnt.group.isEmpty() )
{
entry.insert( KIO::UDSEntry::UDS_GROUP, ftpEnt.group );
}
if ( !ftpEnt.link.isEmpty() )
{
entry.insert( KIO::UDSEntry::UDS_LINK_DEST, ftpEnt.link );
QMimeDatabase db;
QMimeType mime = db.mimeTypeForUrl(QUrl("ftp://host/" + filename));
// Links on ftp sites are often links to dirs, and we have no way to check
// that. Let's do like Netscape : assume dirs generally.
// But we do this only when the mimetype can't be known from the filename.
// --> we do better than Netscape :-)
if (mime.isDefault()) {
kDebug(7102) << "Setting guessed mime type to inode/directory for " << filename;
entry.insert( KIO::UDSEntry::UDS_GUESSED_MIME_TYPE, QString::fromLatin1( "inode/directory" ) );
isDir = true;
}
}
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : ftpEnt.type );
// entry.insert KIO::UDSEntry::UDS_ACCESS_TIME,buff.st_atime);
// entry.insert KIO::UDSEntry::UDS_CREATION_TIME,buff.st_ctime);
}
void Ftp::ftpShortStatAnswer( const QString& filename, bool isDir )
{
UDSEntry entry;
entry.insert( KIO::UDSEntry::UDS_NAME, filename );
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG );
entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
if (isDir) {
entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String("inode/directory"));
}
// No details about size, ownership, group, etc.
statEntry(entry);
finished();
}
void Ftp::ftpStatAnswerNotFound( const QString & path, const QString & filename )
{
// Only do the 'hack' below if we want to download an existing file (i.e. when looking at the "source")
// When e.g. uploading a file, we still need stat() to return "not found"
// when the file doesn't exist.
QString statSide = metaData("statSide");
kDebug(7102) << "statSide=" << statSide;
if ( statSide == "source" )
{
kDebug(7102) << "Not found, but assuming found, because some servers don't allow listing";
// MS Server is incapable of handling "list <blah>" in a case insensitive way
// But "retr <blah>" works. So lie in stat(), to get going...
//
// There's also the case of ftp://ftp2.3ddownloads.com/90380/linuxgames/loki/patches/ut/ut-patch-436.run
// where listing permissions are denied, but downloading is still possible.
ftpShortStatAnswer( filename, false /*file, not dir*/ );
return;
}
error( ERR_DOES_NOT_EXIST, path );
}
void Ftp::stat(const QUrl &url)
{
kDebug(7102) << "path=" << url.path();
if( !ftpOpenConnection(loginImplicit) )
return;
const QString path = ftpCleanPath( QDir::cleanPath( url.path() ) );
kDebug(7102) << "cleaned path=" << path;
// We can't stat root, but we know it's a dir.
if( path.isEmpty() || path == "/" )
{
UDSEntry entry;
//entry.insert( KIO::UDSEntry::UDS_NAME, UDSField( QString() ) );
entry.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1( "." ) );
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String("inode/directory"));
entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
entry.insert( KIO::UDSEntry::UDS_USER, QString::fromLatin1( "root" ) );
entry.insert( KIO::UDSEntry::UDS_GROUP, QString::fromLatin1( "root" ) );
// no size
statEntry( entry );
finished();
return;
}
QUrl tempurl( url );
tempurl.setPath( path ); // take the clean one
const QUrlPathInfo tempurlInfo(tempurl);
QString listarg; // = tempurl.directory(QUrl::ObeyTrailingSlash);
QString parentDir;
QString filename = tempurlInfo.fileName();
Q_ASSERT(!filename.isEmpty());
QString search = filename;
// Try cwd into it, if it works it's a dir (and then we'll list the parent directory to get more info)
// if it doesn't work, it's a file (and then we'll use dir filename)
bool isDir = ftpFolder(path, false);
// if we're only interested in "file or directory", we should stop here
QString sDetails = metaData("details");
int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
kDebug(7102) << "details=" << details;
if ( details == 0 )
{
if ( !isDir && !ftpFileExists(path) ) // ok, not a dir -> is it a file ?
{ // no -> it doesn't exist at all
ftpStatAnswerNotFound( path, filename );
return;
}
ftpShortStatAnswer( filename, isDir ); // successfully found a dir or a file -> done
return;
}
if (!isDir)
{
// It is a file or it doesn't exist, try going to parent directory
parentDir = tempurlInfo.directory(QUrlPathInfo::AppendTrailingSlash);
// With files we can do "LIST <filename>" to avoid listing the whole dir
listarg = filename;
}
else
{
// --- New implementation:
// Don't list the parent dir. Too slow, might not show it, etc.
// Just return that it's a dir.
UDSEntry entry;
entry.insert( KIO::UDSEntry::UDS_NAME, filename );
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
// No clue about size, ownership, group, etc.
statEntry(entry);
finished();
return;
}
// Now cwd the parent dir, to prepare for listing
if( !ftpFolder(parentDir, true) )
return;
if( !ftpOpenCommand( "list", listarg, 'I', ERR_DOES_NOT_EXIST ) )
{
kError(7102) << "COULD NOT LIST";
return;
}
kDebug(7102) << "Starting of list was ok";
Q_ASSERT( !search.isEmpty() && search != "/" );
bool bFound = false;
QUrl linkURL;
FtpEntry ftpEnt;
QList<FtpEntry> ftpValidateEntList;
while (ftpReadDir(ftpEnt)) {
if (!ftpEnt.name.isEmpty() && ftpEnt.name.at(0).isSpace()) {
ftpValidateEntList.append(ftpEnt);
continue;
}
// We look for search or filename, since some servers (e.g. ftp.tuwien.ac.at)
// return only the filename when doing "dir /full/path/to/file"
if (!bFound) {
bFound = maybeEmitStatEntry(ftpEnt, search, filename, isDir);
}
// kDebug(7102) << ftpEnt.name;
}
for (int i = 0, count = ftpValidateEntList.count(); i < count; ++i) {
FtpEntry& ftpEnt = ftpValidateEntList[i];
fixupEntryName(&ftpEnt);
if (maybeEmitStatEntry(ftpEnt, search, filename, isDir)) {
break;
}
}
ftpCloseCommand(); // closes the data connection only
if ( !bFound )
{
ftpStatAnswerNotFound( path, filename );
return;
}
if ( !linkURL.isEmpty() )
{
if ( linkURL == url || linkURL == tempurl )
{
error( ERR_CYCLIC_LINK, linkURL.toString() );
return;
}
Ftp::stat( linkURL );
return;
}
kDebug(7102) << "stat : finished successfully";
finished();
}
bool Ftp::maybeEmitStatEntry(FtpEntry& ftpEnt, const QString& search, const QString& filename, bool isDir)
{
if ((search == ftpEnt.name || filename == ftpEnt.name) && !filename.isEmpty()) {
UDSEntry entry;
ftpCreateUDSEntry( filename, ftpEnt, entry, isDir );
statEntry( entry );
return true;
}
return false;
}
void Ftp::listDir( const QUrl &url )
{
kDebug(7102) << url;
if( !ftpOpenConnection(loginImplicit) )
return;
// No path specified ?
QString path = url.path();
if ( path.isEmpty() )
{
QUrl realURL;
realURL.setScheme( "ftp" );
realURL.setUserName( m_user );
realURL.setPassword( m_pass );
realURL.setHost( m_host );
if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
realURL.setPort( m_port );
if ( m_initialPath.isEmpty() )
m_initialPath = '/';
realURL.setPath( m_initialPath );
kDebug(7102) << "REDIRECTION to " << realURL;
redirection( realURL );
finished();
return;
}
kDebug(7102) << "hunting for path" << path;
if (!ftpOpenDir(path)) {
if (ftpFileExists(path)) {
error(ERR_IS_FILE, path);
} else {
// not sure which to emit
//error( ERR_DOES_NOT_EXIST, path );
error( ERR_CANNOT_ENTER_DIRECTORY, path );
}
return;
}
UDSEntry entry;
FtpEntry ftpEnt;
QList<FtpEntry> ftpValidateEntList;
while( ftpReadDir(ftpEnt) )
{
//kDebug(7102) << ftpEnt.name;
//Q_ASSERT( !ftpEnt.name.isEmpty() );
if (!ftpEnt.name.isEmpty()) {
if (ftpEnt.name.at(0).isSpace()) {
ftpValidateEntList.append(ftpEnt);
continue;
}
//if ( S_ISDIR( (mode_t)ftpEnt.type ) )
// kDebug(7102) << "is a dir";
//if ( !ftpEnt.link.isEmpty() )
// kDebug(7102) << "is a link to " << ftpEnt.link;
ftpCreateUDSEntry( ftpEnt.name, ftpEnt, entry, false );
listEntry( entry, false );
entry.clear();
}
}
for (int i = 0, count = ftpValidateEntList.count(); i < count; ++i) {
FtpEntry& ftpEnt = ftpValidateEntList[i];
fixupEntryName(&ftpEnt);
ftpCreateUDSEntry( ftpEnt.name, ftpEnt, entry, false );
listEntry( entry, false );
entry.clear();
}
listEntry( entry, true ); // ready
ftpCloseCommand(); // closes the data connection only
finished();
}
void Ftp::slave_status()
{
- kDebug(7102) << "Got slave_status host = " << (!m_host.toAscii().isEmpty() ? m_host.toAscii() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]";
+ kDebug(7102) << "Got slave_status host = " << (!m_host.toLatin1().isEmpty() ? m_host.toAscii() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]";
slaveStatus( m_host, m_bLoggedOn );
}
bool Ftp::ftpOpenDir( const QString & path )
{
//QString path( _url.path(QUrl::RemoveTrailingSlash) );
// We try to change to this directory first to see whether it really is a directory.
// (And also to follow symlinks)
QString tmp = path.isEmpty() ? QString("/") : path;
// We get '550', whether it's a file or doesn't exist...
if( !ftpFolder(tmp, false) )
return false;
// Don't use the path in the list command:
// We changed into this directory anyway - so it's enough just to send "list".
// We use '-a' because the application MAY be interested in dot files.
// The only way to really know would be to have a metadata flag for this...
// Since some windows ftp server seems not to support the -a argument, we use a fallback here.
// In fact we have to use -la otherwise -a removes the default -l (e.g. ftp.trolltech.com)
if( !ftpOpenCommand( "list -la", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) )
{
if ( !ftpOpenCommand( "list", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) )
{
kWarning(7102) << "Can't open for listing";
return false;
}
}
kDebug(7102) << "Starting of list was ok";
return true;
}
bool Ftp::ftpReadDir(FtpEntry& de)
{
Q_ASSERT(m_data != NULL);
// get a line from the data connecetion ...
while( true )
{
while (!m_data->canReadLine() && m_data->waitForReadyRead((readTimeout() * 1000))) {}
QByteArray data = m_data->readLine();
if (data.size() == 0)
break;
const char* buffer = data.data();
kDebug(7102) << "dir > " << buffer;
//Normally the listing looks like
// -rw-r--r-- 1 dfaure dfaure 102 Nov 9 12:30 log
// but on Netware servers like ftp://ci-1.ci.pwr.wroc.pl/ it looks like (#76442)
// d [RWCEAFMS] Admin 512 Oct 13 2004 PSI
// we should always get the following 5 fields ...
const char *p_access, *p_junk, *p_owner, *p_group, *p_size;
if( (p_access = strtok((char*)buffer," ")) == 0) continue;
if( (p_junk = strtok(NULL," ")) == 0) continue;
if( (p_owner = strtok(NULL," ")) == 0) continue;
if( (p_group = strtok(NULL," ")) == 0) continue;
if( (p_size = strtok(NULL," ")) == 0) continue;
//kDebug(7102) << "p_access=" << p_access << " p_junk=" << p_junk << " p_owner=" << p_owner << " p_group=" << p_group << " p_size=" << p_size;
de.access = 0;
if ( qstrlen( p_access ) == 1 && p_junk[0] == '[' ) { // Netware
de.access = S_IRWXU | S_IRWXG | S_IRWXO; // unknown -> give all permissions
}
const char *p_date_1, *p_date_2, *p_date_3, *p_name;
// A special hack for "/dev". A listing may look like this:
// crw-rw-rw- 1 root root 1, 5 Jun 29 1997 zero
// So we just ignore the number in front of the ",". Ok, it is a hack :-)
if ( strchr( p_size, ',' ) != 0L )
{
//kDebug(7102) << "Size contains a ',' -> reading size again (/dev hack)";
if ((p_size = strtok(NULL," ")) == 0)
continue;
}
// Check whether the size we just read was really the size
// or a month (this happens when the server lists no group)
// Used to be the case on sunsite.uio.no, but not anymore
// This is needed for the Netware case, too.
if ( !isdigit( *p_size ) )
{
p_date_1 = p_size;
p_size = p_group;
p_group = 0;
//kDebug(7102) << "Size didn't have a digit -> size=" << p_size << " date_1=" << p_date_1;
}
else
{
p_date_1 = strtok(NULL," ");
//kDebug(7102) << "Size has a digit -> ok. p_date_1=" << p_date_1;
}
if ( p_date_1 != 0 &&
(p_date_2 = strtok(NULL," ")) != 0 &&
(p_date_3 = strtok(NULL," ")) != 0 &&
(p_name = strtok(NULL,"\r\n")) != 0 )
{
{
QByteArray tmp( p_name );
if ( p_access[0] == 'l' )
{
int i = tmp.lastIndexOf( " -> " );
if ( i != -1 ) {
de.link = remoteEncoding()->decode(p_name + i + 4);
tmp.truncate( i );
}
else
de.link.clear();
}
else
de.link.clear();
if ( tmp[0] == '/' ) // listing on ftp://ftp.gnupg.org/ starts with '/'
tmp.remove( 0, 1 );
if (tmp.indexOf('/') != -1)
continue; // Don't trick us!
de.name = remoteEncoding()->decode(tmp);
}
de.type = S_IFREG;
switch ( p_access[0] ) {
case 'd':
de.type = S_IFDIR;
break;
case 's':
de.type = S_IFSOCK;
break;
case 'b':
de.type = S_IFBLK;
break;
case 'c':
de.type = S_IFCHR;
break;
case 'l':
de.type = S_IFREG;
// we don't set S_IFLNK here. de.link says it.
break;
default:
break;
}
if ( p_access[1] == 'r' )
de.access |= S_IRUSR;
if ( p_access[2] == 'w' )
de.access |= S_IWUSR;
if ( p_access[3] == 'x' || p_access[3] == 's' )
de.access |= S_IXUSR;
if ( p_access[4] == 'r' )
de.access |= S_IRGRP;
if ( p_access[5] == 'w' )
de.access |= S_IWGRP;
if ( p_access[6] == 'x' || p_access[6] == 's' )
de.access |= S_IXGRP;
if ( p_access[7] == 'r' )
de.access |= S_IROTH;
if ( p_access[8] == 'w' )
de.access |= S_IWOTH;
if ( p_access[9] == 'x' || p_access[9] == 't' )
de.access |= S_IXOTH;
if ( p_access[3] == 's' || p_access[3] == 'S' )
de.access |= S_ISUID;
if ( p_access[6] == 's' || p_access[6] == 'S' )
de.access |= S_ISGID;
if ( p_access[9] == 't' || p_access[9] == 'T' )
de.access |= S_ISVTX;
de.owner = remoteEncoding()->decode(p_owner);
de.group = remoteEncoding()->decode(p_group);
de.size = charToLongLong(p_size);
// Parsing the date is somewhat tricky
// Examples : "Oct 6 22:49", "May 13 1999"
// First get current time - we need the current month and year
time_t currentTime = time( 0L );
struct tm * tmptr = gmtime( &currentTime );
int currentMonth = tmptr->tm_mon;
//kDebug(7102) << "Current time :" << asctime( tmptr );
// Reset time fields
tmptr->tm_isdst = -1; // We do not anything about day saving time
tmptr->tm_sec = 0;
tmptr->tm_min = 0;
tmptr->tm_hour = 0;
// Get day number (always second field)
if (p_date_2)
tmptr->tm_mday = atoi( p_date_2 );
// Get month from first field
// NOTE : no, we don't want to use KLocale here
// It seems all FTP servers use the English way
//kDebug(7102) << "Looking for month " << p_date_1;
static const char * const s_months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
for ( int c = 0 ; c < 12 ; c ++ )
if ( !qstrcmp( p_date_1, s_months[c]) )
{
//kDebug(7102) << "Found month " << c << " for " << p_date_1;
tmptr->tm_mon = c;
break;
}
// Parse third field
if ( qstrlen( p_date_3 ) == 4 ) // 4 digits, looks like a year
tmptr->tm_year = atoi( p_date_3 ) - 1900;
else
{
// otherwise, the year is implicit
// according to man ls, this happens when it is between than 6 months
// old and 1 hour in the future.
// So the year is : current year if tm_mon <= currentMonth+1
// otherwise current year minus one
// (The +1 is a security for the "+1 hour" at the end of the month issue)
if ( tmptr->tm_mon > currentMonth + 1 )
tmptr->tm_year--;
// and p_date_3 contains probably a time
char * semicolon;
if ( p_date_3 && ( semicolon = (char*)strchr( p_date_3, ':' ) ) )
{
*semicolon = '\0';
tmptr->tm_min = atoi( semicolon + 1 );
tmptr->tm_hour = atoi( p_date_3 );
}
else
kWarning(7102) << "Can't parse third field " << p_date_3;
}
//kDebug(7102) << asctime( tmptr );
de.date = mktime( tmptr );
return true;
}
} // line invalid, loop to get another line
return false;
}
//===============================================================================
// public: get download file from server
// helper: ftpGet called from get() and copy()
//===============================================================================
void Ftp::get( const QUrl & url )
{
kDebug(7102) << url;
int iError = 0;
const StatusCode cs = ftpGet(iError, -1, url, 0);
ftpCloseCommand(); // must close command!
if (cs == statusSuccess) {
finished();
return;
}
if (iError) { // can have only server side errs
error(iError, url.path());
}
}
Ftp::StatusCode Ftp::ftpGet(int& iError, int iCopyFile, const QUrl& url, KIO::fileoffset_t llOffset)
{
// Calls error() by itself!
if( !ftpOpenConnection(loginImplicit) )
return statusServerError;
// Try to find the size of the file (and check that it exists at
// the same time). If we get back a 550, "File does not exist"
// or "not a plain file", check if it is a directory. If it is a
// directory, return an error; otherwise simply try to retrieve
// the request...
if ( !ftpSize( url.path(), '?' ) && (m_iRespCode == 550) &&
ftpFolder(url.path(), false) )
{
// Ok it's a dir in fact
kDebug(7102) << "it is a directory in fact";
iError = ERR_IS_DIRECTORY;
return statusServerError;
}
QString resumeOffset = metaData("resume");
if ( !resumeOffset.isEmpty() )
{
llOffset = resumeOffset.toLongLong();
kDebug(7102) << "got offset from metadata : " << llOffset;
}
if( !ftpOpenCommand("retr", url.path(), '?', ERR_CANNOT_OPEN_FOR_READING, llOffset) )
{
kWarning(7102) << "Can't open for reading";
return statusServerError;
}
// Read the size from the response string
if(m_size == UnknownSize)
{
const char* psz = strrchr( ftpResponse(4), '(' );
if(psz) m_size = charToLongLong(psz+1);
if (!m_size) m_size = UnknownSize;
}
// Send the mime-type...
if (iCopyFile == -1) {
StatusCode status = ftpSendMimeType(iError, url);
if (status != statusSuccess) {
return status;
}
}
KIO::filesize_t bytesLeft = 0;
if ( m_size != UnknownSize ) {
bytesLeft = m_size - llOffset;
totalSize( m_size ); // emit the total size...
}
kDebug(7102) << "starting with offset=" << llOffset;
KIO::fileoffset_t processed_size = llOffset;
QByteArray array;
char buffer[maximumIpcSize];
// start with small data chunks in case of a slow data source (modem)
// - unfortunately this has a negative impact on performance for large
// - files - so we will increase the block size after a while ...
int iBlockSize = initialIpcSize;
int iBufferCur = 0;
while(m_size == UnknownSize || bytesLeft > 0)
{ // let the buffer size grow if the file is larger 64kByte ...
if(processed_size-llOffset > 1024 * 64)
iBlockSize = maximumIpcSize;
// read the data and detect EOF or error ...
if(iBlockSize+iBufferCur > (int)sizeof(buffer))
iBlockSize = sizeof(buffer) - iBufferCur;
if (m_data->bytesAvailable() == 0)
m_data->waitForReadyRead((readTimeout() * 1000));
int n = m_data->read( buffer+iBufferCur, iBlockSize );
if(n <= 0)
{ // this is how we detect EOF in case of unknown size
if( m_size == UnknownSize && n == 0 )
break;
// unexpected eof. Happens when the daemon gets killed.
iError = ERR_COULD_NOT_READ;
return statusServerError;
}
processed_size += n;
// collect very small data chunks in buffer before processing ...
if(m_size != UnknownSize)
{
bytesLeft -= n;
iBufferCur += n;
if(iBufferCur < minimumMimeSize && bytesLeft > 0)
{
processedSize( processed_size );
continue;
}
n = iBufferCur;
iBufferCur = 0;
}
// write output file or pass to data pump ...
if(iCopyFile == -1)
{
array = QByteArray::fromRawData(buffer, n);
data( array );
array.clear();
}
else if( (iError = WriteToFile(iCopyFile, buffer, n)) != 0)
return statusClientError; // client side error
processedSize( processed_size );
}
kDebug(7102) << "done";
if(iCopyFile == -1) // must signal EOF to data pump ...
data(array); // array is empty and must be empty!
processedSize( m_size == UnknownSize ? processed_size : m_size );
return statusSuccess;
}
#if 0
void Ftp::mimetype( const QUrl& url )
{
if( !ftpOpenConnection(loginImplicit) )
return;
if ( !ftpOpenCommand( "retr", url.path(), 'I', ERR_CANNOT_OPEN_FOR_READING, 0 ) ) {
kWarning(7102) << "Can't open for reading";
return;
}
char buffer[ 2048 ];
QByteArray array;
// Get one chunk of data only and send it, KIO::Job will determine the
// mimetype from it using KMimeMagic
int n = m_data->read( buffer, 2048 );
array.setRawData(buffer, n);
data( array );
array.resetRawData(buffer, n);
kDebug(7102) << "aborting";
ftpAbortTransfer();
kDebug(7102) << "finished";
finished();
kDebug(7102) << "after finished";
}
void Ftp::ftpAbortTransfer()
{
// RFC 959, page 34-35
// IAC (interpret as command) = 255 ; IP (interrupt process) = 254
// DM = 242 (data mark)
char msg[4];
// 1. User system inserts the Telnet "Interrupt Process" (IP) signal
// in the Telnet stream.
msg[0] = (char) 255; //IAC
msg[1] = (char) 254; //IP
(void) send(sControl, msg, 2, 0);
// 2. User system sends the Telnet "Sync" signal.
msg[0] = (char) 255; //IAC
msg[1] = (char) 242; //DM
if (send(sControl, msg, 2, MSG_OOB) != 2)
; // error...
// Send ABOR
kDebug(7102) << "send ABOR";
QCString buf = "ABOR\r\n";
if ( KSocks::self()->write( sControl, buf.data(), buf.length() ) <= 0 ) {
error( ERR_COULD_NOT_WRITE, QString() );
return;
}
//
kDebug(7102) << "read resp";
if ( readresp() != '2' )
{
error( ERR_COULD_NOT_READ, QString() );
return;
}
kDebug(7102) << "close sockets";
closeSockets();
}
#endif
//===============================================================================
// public: put upload file to server
// helper: ftpPut called from put() and copy()
//===============================================================================
void Ftp::put(const QUrl& url, int permissions, KIO::JobFlags flags)
{
kDebug(7102) << url;
int iError = 0; // iError gets status
const StatusCode cs = ftpPut(iError, -1, url, permissions, flags);
ftpCloseCommand(); // must close command!
if (cs == statusSuccess) {
finished();
return;
}
if (iError) { // can have only server side errs
error(iError, url.path());
}
}
Ftp::StatusCode Ftp::ftpPut(int& iError, int iCopyFile, const QUrl& dest_url,
int permissions, KIO::JobFlags flags)
{
if( !ftpOpenConnection(loginImplicit) )
return statusServerError;
// Don't use mark partial over anonymous FTP.
// My incoming dir allows put but not rename...
bool bMarkPartial;
if (m_user.isEmpty () || m_user == FTP_LOGIN)
bMarkPartial = false;
else
bMarkPartial = config()->readEntry("MarkPartial", true);
QString dest_orig = dest_url.path();
QString dest_part( dest_orig );
dest_part += ".part";
if ( ftpSize( dest_orig, 'I' ) )
{
if ( m_size == 0 )
{ // delete files with zero size
QByteArray cmd = "DELE ";
cmd += remoteEncoding()->encode(dest_orig);
if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
{
iError = ERR_CANNOT_DELETE_PARTIAL;
return statusServerError;
}
}
else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
{
iError = ERR_FILE_ALREADY_EXIST;
return statusServerError;
}
else if ( bMarkPartial )
{ // when using mark partial, append .part extension
if ( !ftpRename( dest_orig, dest_part, KIO::Overwrite ) )
{
iError = ERR_CANNOT_RENAME_PARTIAL;
return statusServerError;
}
}
// Don't chmod an existing file
permissions = -1;
}
else if ( bMarkPartial && ftpSize( dest_part, 'I' ) )
{ // file with extension .part exists
if ( m_size == 0 )
{ // delete files with zero size
QByteArray cmd = "DELE ";
cmd += remoteEncoding()->encode(dest_part);
if ( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
{
iError = ERR_CANNOT_DELETE_PARTIAL;
return statusServerError;
}
}
else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
{
flags |= canResume (m_size) ? KIO::Resume : KIO::DefaultFlags;
if (!(flags & KIO::Resume))
{
iError = ERR_FILE_ALREADY_EXIST;
return statusServerError;
}
}
}
else
m_size = 0;
QString dest;
// if we are using marking of partial downloads -> add .part extension
if ( bMarkPartial ) {
kDebug(7102) << "Adding .part extension to " << dest_orig;
dest = dest_part;
} else
dest = dest_orig;
KIO::fileoffset_t offset = 0;
// set the mode according to offset
if( (flags & KIO::Resume) && m_size > 0 )
{
offset = m_size;
if(iCopyFile != -1)
{
if( KDE_lseek(iCopyFile, offset, SEEK_SET) < 0 )
{
iError = ERR_CANNOT_RESUME;
return statusClientError;
}
}
}
if (! ftpOpenCommand( "stor", dest, '?', ERR_COULD_NOT_WRITE, offset ) )
return statusServerError;
kDebug(7102) << "ftpPut: starting with offset=" << offset;
KIO::fileoffset_t processed_size = offset;
QByteArray buffer;
int result;
int iBlockSize = initialIpcSize;
// Loop until we got 'dataEnd'
do
{
if(iCopyFile == -1)
{
dataReq(); // Request for data
result = readData( buffer );
}
else
{ // let the buffer size grow if the file is larger 64kByte ...
if(processed_size-offset > 1024 * 64)
iBlockSize = maximumIpcSize;
buffer.resize(iBlockSize);
result = ::read(iCopyFile, buffer.data(), buffer.size());
if(result < 0)
iError = ERR_COULD_NOT_WRITE;
else
buffer.resize(result);
}
if (result > 0)
{
m_data->write( buffer );
while (m_data->bytesToWrite() && m_data->waitForBytesWritten()) {}
processed_size += result;
processedSize (processed_size);
}
}
while ( result > 0 );
if (result != 0) // error
{
ftpCloseCommand(); // don't care about errors
kDebug(7102) << "Error during 'put'. Aborting.";
if (bMarkPartial)
{
// Remove if smaller than minimum size
if ( ftpSize( dest, 'I' ) &&
( processed_size < config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE) ) )
{
QByteArray cmd = "DELE ";
cmd += remoteEncoding()->encode(dest);
(void) ftpSendCmd( cmd );
}
}
return statusServerError;
}
if ( !ftpCloseCommand() )
{
iError = ERR_COULD_NOT_WRITE;
return statusServerError;
}
// after full download rename the file back to original name
if ( bMarkPartial )
{
kDebug(7102) << "renaming dest (" << dest << ") back to dest_orig (" << dest_orig << ")";
if ( !ftpRename( dest, dest_orig, KIO::Overwrite ) )
{
iError = ERR_CANNOT_RENAME_PARTIAL;
return statusServerError;
}
}
// set final permissions
if ( permissions != -1 )
{
if ( m_user == FTP_LOGIN )
kDebug(7102) << "Trying to chmod over anonymous FTP ???";
// chmod the file we just put
if ( ! ftpChmod( dest_orig, permissions ) )
{
// To be tested
//if ( m_user != FTP_LOGIN )
// warning( i18n( "Could not change permissions for\n%1" ).arg( dest_orig ) );
}
}
return statusSuccess;
}
/** Use the SIZE command to get the file size.
Warning : the size depends on the transfer mode, hence the second arg. */
bool Ftp::ftpSize( const QString & path, char mode )
{
m_size = UnknownSize;
if( !ftpDataMode(mode) )
return false;
QByteArray buf;
buf = "SIZE ";
buf += remoteEncoding()->encode(path);
if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
return false;
// skip leading "213 " (response code)
QByteArray psz (ftpResponse(4));
if(psz.isEmpty())
return false;
bool ok = false;
m_size = psz.trimmed().toLongLong(&ok);
if (!ok) m_size = UnknownSize;
return true;
}
bool Ftp::ftpFileExists(const QString& path)
{
QByteArray buf;
buf = "SIZE ";
buf += remoteEncoding()->encode(path);
if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
return false;
// skip leading "213 " (response code)
const char* psz = ftpResponse(4);
return psz != 0;
}
// Today the differences between ASCII and BINARY are limited to
// CR or CR/LF line terminators. Many servers ignore ASCII (like
// win2003 -or- vsftp with default config). In the early days of
// computing, when even text-files had structure, this stuff was
// more important.
// Theoretically "list" could return different results in ASCII
// and BINARY mode. But again, most servers ignore ASCII here.
bool Ftp::ftpDataMode(char cMode)
{
if(cMode == '?') cMode = m_bTextMode ? 'A' : 'I';
else if(cMode == 'a') cMode = 'A';
else if(cMode != 'A') cMode = 'I';
kDebug(7102) << "want" << cMode << "has" << m_cDataMode;
if(m_cDataMode == cMode)
return true;
QByteArray buf = "TYPE ";
buf += cMode;
if( !ftpSendCmd(buf) || (m_iRespType != 2) )
return false;
m_cDataMode = cMode;
return true;
}
bool Ftp::ftpFolder(const QString& path, bool bReportError)
{
QString newPath = path;
int iLen = newPath.length();
if(iLen > 1 && newPath[iLen-1] == '/') newPath.truncate(iLen-1);
//kDebug(7102) << "want" << newPath << "has" << m_currentPath;
if(m_currentPath == newPath)
return true;
QByteArray tmp = "cwd ";
tmp += remoteEncoding()->encode(newPath);
if( !ftpSendCmd(tmp) )
return false; // connection failure
if(m_iRespType != 2)
{
if(bReportError)
error(ERR_CANNOT_ENTER_DIRECTORY, path);
return false; // not a folder
}
m_currentPath = newPath;
return true;
}
//===============================================================================
// public: copy don't use kio data pump if one side is a local file
// helper: ftpCopyPut called from copy() on upload
// helper: ftpCopyGet called from copy() on download
//===============================================================================
void Ftp::copy( const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags )
{
int iError = 0;
int iCopyFile = -1;
StatusCode cs = statusSuccess;
bool bSrcLocal = src.isLocalFile();
bool bDestLocal = dest.isLocalFile();
QString sCopyFile;
if(bSrcLocal && !bDestLocal) // File -> Ftp
{
sCopyFile = src.toLocalFile();
kDebug(7102) << "local file" << sCopyFile << "-> ftp" << dest.path();
cs = ftpCopyPut(iError, iCopyFile, sCopyFile, dest, permissions, flags);
if( cs == statusServerError) sCopyFile = dest.toString();
}
else if(!bSrcLocal && bDestLocal) // Ftp -> File
{
sCopyFile = dest.toLocalFile();
kDebug(7102) << "ftp" << src.path() << "-> local file" << sCopyFile;
cs = ftpCopyGet(iError, iCopyFile, sCopyFile, src, permissions, flags);
if( cs == statusServerError ) sCopyFile = src.toString();
}
else {
error( ERR_UNSUPPORTED_ACTION, QString() );
return;
}
// perform clean-ups and report error (if any)
if(iCopyFile != -1)
::close(iCopyFile);
ftpCloseCommand(); // must close command!
if(iError)
error(iError, sCopyFile);
else
finished();
}
Ftp::StatusCode Ftp::ftpCopyPut(int& iError, int& iCopyFile, const QString &sCopyFile,
const QUrl& url, int permissions, KIO::JobFlags flags)
{
// check if source is ok ...
KDE_struct_stat buff;
bool bSrcExists = (KDE::stat( sCopyFile, &buff ) != -1);
if(bSrcExists)
{ if(S_ISDIR(buff.st_mode))
{
iError = ERR_IS_DIRECTORY;
return statusClientError;
}
}
else
{
iError = ERR_DOES_NOT_EXIST;
return statusClientError;
}
iCopyFile = KDE::open( sCopyFile, O_RDONLY );
if(iCopyFile == -1)
{
iError = ERR_CANNOT_OPEN_FOR_READING;
return statusClientError;
}
// delegate the real work (iError gets status) ...
totalSize(buff.st_size);
#ifdef ENABLE_CAN_RESUME
return ftpPut(iError, iCopyFile, url, permissions, flags & ~KIO::Resume);
#else
return ftpPut(iError, iCopyFile, url, permissions, flags | KIO::Resume);
#endif
}
Ftp::StatusCode Ftp::ftpCopyGet(int& iError, int& iCopyFile, const QString &sCopyFile,
const QUrl& url, int permissions, KIO::JobFlags flags)
{
// check if destination is ok ...
KDE_struct_stat buff;
const bool bDestExists = (KDE::stat( sCopyFile, &buff ) != -1);
if(bDestExists)
{ if(S_ISDIR(buff.st_mode))
{
iError = ERR_IS_DIRECTORY;
return statusClientError;
}
if(!(flags & KIO::Overwrite))
{
iError = ERR_FILE_ALREADY_EXIST;
return statusClientError;
}
}
// do we have a ".part" file?
const QString sPart = sCopyFile + QLatin1String(".part");
bool bResume = false;
const bool bPartExists = (KDE::stat( sPart, &buff ) != -1);
const bool bMarkPartial = config()->readEntry("MarkPartial", true);
const QString dest = bMarkPartial ? sPart : sCopyFile;
if (bMarkPartial && bPartExists && buff.st_size > 0)
{ // must not be a folder! please fix a similar bug in kio_file!!
if(S_ISDIR(buff.st_mode))
{
iError = ERR_DIR_ALREADY_EXIST;
return statusClientError; // client side error
}
//doesn't work for copy? -> design flaw?
#ifdef ENABLE_CAN_RESUME
bResume = canResume( buff.st_size );
#else
bResume = true;
#endif
}
if (bPartExists && !bResume) // get rid of an unwanted ".part" file
QFile::remove(sPart);
// WABA: Make sure that we keep writing permissions ourselves,
// otherwise we can be in for a surprise on NFS.
mode_t initialMode;
if (permissions != -1)
initialMode = permissions | S_IWUSR;
else
initialMode = 0666;
// open the output file ...
KIO::fileoffset_t hCopyOffset = 0;
if (bResume) {
iCopyFile = KDE::open( sPart, O_RDWR ); // append if resuming
hCopyOffset = KDE_lseek(iCopyFile, 0, SEEK_END);
if(hCopyOffset < 0)
{
iError = ERR_CANNOT_RESUME;
return statusClientError; // client side error
}
kDebug(7102) << "resuming at " << hCopyOffset;
}
else {
iCopyFile = KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
}
if(iCopyFile == -1)
{
kDebug(7102) << "### COULD NOT WRITE " << sCopyFile;
iError = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED
: ERR_CANNOT_OPEN_FOR_WRITING;
return statusClientError;
}
// delegate the real work (iError gets status) ...
StatusCode iRes = ftpGet(iError, iCopyFile, url, hCopyOffset);
if( ::close(iCopyFile) && iRes == statusSuccess )
{
iError = ERR_COULD_NOT_WRITE;
iRes = statusClientError;
}
iCopyFile = -1;
// handle renaming or deletion of a partial file ...
if(bMarkPartial)
{
if(iRes == statusSuccess)
{ // rename ".part" on success
if ( KDE::rename( sPart, sCopyFile ) )
{
// If rename fails, try removing the destination first if it exists.
if (!bDestExists || !(QFile::remove(sCopyFile) && KDE::rename(sPart, sCopyFile) == 0)) {
kDebug(7102) << "cannot rename " << sPart << " to " << sCopyFile;
iError = ERR_CANNOT_RENAME_PARTIAL;
iRes = statusClientError;
}
}
}
else if(KDE::stat( sPart, &buff ) == 0)
{ // should a very small ".part" be deleted?
int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
if (buff.st_size < size)
QFile::remove(sPart);
}
}
return iRes;
}
Ftp::StatusCode Ftp::ftpSendMimeType(int& iError, const QUrl& url)
{
const int totalSize = ((m_size == UnknownSize || m_size > 1024) ? 1024 : m_size);
QByteArray buffer(totalSize, '\0');
while (true) {
// Wait for content to be available...
if (m_data->bytesAvailable() == 0 && !m_data->waitForReadyRead((readTimeout() * 1000))) {
iError = ERR_COULD_NOT_READ;
return statusServerError;
}
const int bytesRead = m_data->peek(buffer.data(), totalSize);
// If we got a -1, it must be an error so return an error.
if (bytesRead == -1) {
iError = ERR_COULD_NOT_READ;
return statusServerError;
}
// If m_size is unknown, peek returns 0 (0 sized file ??), or peek returns size
// equal to the size we want, then break.
if (bytesRead == 0 || bytesRead == totalSize || m_size == UnknownSize) {
break;
}
}
if (!buffer.isEmpty()) {
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFileNameAndData(url.path(), buffer);
kDebug(7102) << "Emitting mimetype" << mime.name();
mimeType(mime.name()); // emit the mime type...
}
return statusSuccess;
}
void Ftp::proxyAuthentication(const QNetworkProxy& proxy, QAuthenticator* authenticator)
{
Q_UNUSED(proxy);
kDebug(7102) << "Authenticator received -- realm:" << authenticator->realm() << "user:"
<< authenticator->user();
AuthInfo info;
info.url = m_proxyURL;
info.realmValue = authenticator->realm();
info.verifyPath = true; //### whatever
info.username = authenticator->user();
const bool haveCachedCredentials = checkCachedAuthentication(info);
// if m_socketProxyAuth is a valid pointer then authentication has been attempted before,
// and it was not successful. see below and saveProxyAuthenticationForSocket().
if (!haveCachedCredentials || m_socketProxyAuth) {
// Save authentication info if the connection succeeds. We need to disconnect
// this after saving the auth data (or an error) so we won't save garbage afterwards!
connect(m_control, SIGNAL(connected()), this, SLOT(saveProxyAuthentication()));
//### fillPromptInfo(&info);
info.prompt = i18n("You need to supply a username and a password for "
"the proxy server listed below before you are allowed "
"to access any sites.");
info.keepPassword = true;
info.commentLabel = i18n("Proxy:");
info.comment = i18n("<b>%1</b> at <b>%2</b>", info.realmValue, m_proxyURL.host());
const bool dataEntered = openPasswordDialog(info, i18n("Proxy Authentication Failed."));
if (!dataEntered) {
kDebug(7102) << "looks like the user canceled proxy authentication.";
error(ERR_USER_CANCELED, m_proxyURL.host());
return;
}
}
authenticator->setUser(info.username);
authenticator->setPassword(info.password);
authenticator->setOption(QLatin1String("keepalive"), info.keepPassword);
if (m_socketProxyAuth) {
*m_socketProxyAuth = *authenticator;
} else {
m_socketProxyAuth = new QAuthenticator(*authenticator);
}
m_proxyURL.setUserName(info.username);
m_proxyURL.setPassword(info.password);
}
void Ftp::saveProxyAuthentication()
{
kDebug(7102);
disconnect(m_control, SIGNAL(connected()), this, SLOT(saveProxyAuthentication()));
Q_ASSERT(m_socketProxyAuth);
if (m_socketProxyAuth) {
kDebug(7102) << "-- realm:" << m_socketProxyAuth->realm() << "user:" << m_socketProxyAuth->user();
KIO::AuthInfo a;
a.verifyPath = true;
a.url = m_proxyURL;
a.realmValue = m_socketProxyAuth->realm();
a.username = m_socketProxyAuth->user();
a.password = m_socketProxyAuth->password();
a.keepPassword = m_socketProxyAuth->option(QLatin1String("keepalive")).toBool();
cacheAuthentication(a);
}
delete m_socketProxyAuth;
m_socketProxyAuth = 0;
}
void Ftp::fixupEntryName(FtpEntry* e)
{
Q_ASSERT(e);
if (e->type == S_IFDIR) {
if (!ftpFolder(e->name, false)) {
QString name (e->name.trimmed());
if (ftpFolder(name, false)) {
e->name = name;
kDebug(7102) << "fixing up directory name from" << e->name << "to" << name;
} else {
int index = 0;
while (e->name.at(index).isSpace()) {
index++;
name = e->name.mid(index);
if (ftpFolder(name, false)) {
kDebug(7102) << "fixing up directory name from" << e->name << "to" << name;
e->name = name;
break;
}
}
}
}
} else {
if (!ftpFileExists(e->name)) {
QString name (e->name.trimmed());
if (ftpFileExists(name)) {
e->name = name;
kDebug(7102) << "fixing up filename from" << e->name << "to" << name;
} else {
int index = 0;
while (e->name.at(index).isSpace()) {
index++;
name = e->name.mid(index);
if (ftpFileExists(name)) {
kDebug(7102) << "fixing up filename from" << e->name << "to" << name;
e->name = name;
break;
}
}
}
}
}
}
diff --git a/kioslave/http/httpauthentication.cpp b/kioslave/http/httpauthentication.cpp
index 4edbd310d3..f34329c70f 100644
--- a/kioslave/http/httpauthentication.cpp
+++ b/kioslave/http/httpauthentication.cpp
@@ -1,927 +1,927 @@
/* This file is part of the KDE libraries
Copyright (C) 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
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 "httpauthentication.h"
#if HAVE_LIBGSSAPI
#if GSSAPI_MIT
#include <gssapi/gssapi.h>
#else
#include <gssapi.h>
#endif /* GSSAPI_MIT */
// Catch uncompatible crap (BR86019)
#if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
#include <gssapi/gssapi_generic.h>
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif
#endif /* HAVE_LIBGSSAPI */
#include <krandom.h>
#include <kdebug.h>
#include <klocale.h>
#include <kconfiggroup.h>
#include <kio/authinfo.h>
#include <misc/kntlm/kntlm.h>
#include <QtCore/QTextCodec>
#include <QtCore/QCryptographicHash>
static bool isWhiteSpace(char ch)
{
return (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f');
}
static bool isWhiteSpaceOrComma(char ch)
{
return (ch == ',' || isWhiteSpace(ch));
}
static bool containsScheme(const char input[], int start, int end)
{
// skip any comma or white space
while (start < end && isWhiteSpaceOrComma(input[start])) {
start++;
}
while (start < end) {
if (isWhiteSpace(input[start])) {
return true;
}
start++;
}
return false;
}
// keys on even indexes, values on odd indexes. Reduces code expansion for the templated
// alternatives.
// If "ba" starts with empty content it will be removed from ba to simplify later calls
static QList<QByteArray> parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0)
{
QList<QByteArray> values;
const char *b = ba.constData();
int len = ba.count();
int start = 0, end = 0, pos = 0, pos2 = 0;
// parse scheme
while (start < len && isWhiteSpaceOrComma(b[start])) {
start++;
}
end = start;
while (end < len && !isWhiteSpace(b[end])) {
end++;
}
// drop empty stuff from the given string, it would have to be skipped over and over again
if (start != 0) {
ba = ba.mid(start);
end -= start;
len -= start;
start = 0;
b = ba.constData();
}
Q_ASSERT(scheme);
*scheme = ba.left(end);
while (end < len) {
start = end;
while (end < len && b[end] != '=') {
end++;
}
pos = end; // save the end position
while (end - 1 > start && isWhiteSpace(b[end - 1])) { // trim whitespace
end--;
}
pos2 = start;
while (pos2 < end && isWhiteSpace(b[pos2])) { // skip whitespace
pos2++;
}
if (containsScheme(b, start, end) || (b[pos2] == ',' && b[pos] != '=' && pos == len)) {
if (nextAuth) {
*nextAuth = QByteArray (b + start);
}
break; // break on start of next scheme.
}
while (start < len && isWhiteSpaceOrComma(b[start])) {
start++;
}
values.append(QByteArray (b + start, end - start));
end = pos; // restore the end position
if (end == len) {
break;
}
// parse value
start = end + 1; //skip '='
while (start < len && isWhiteSpace(b[start])) {
start++;
}
if (b[start] == '"') {
//quoted string
bool hasBs = false;
bool hasErr = false;
end = ++start;
while (end < len) {
if (b[end] == '\\') {
end++;
if (end + 1 >= len) {
hasErr = true;
break;
} else {
hasBs = true;
end++;
}
} else if (b[end] == '"') {
break;
} else {
end++;
}
}
if (hasErr || (end == len)) {
// remove the key we already inserted
kDebug(7113) << "error in quoted text for key" << values.last();
values.removeLast();
break;
}
QByteArray value = QByteArray(b + start, end - start);
if (hasBs) {
// skip over the next character, it might be an escaped backslash
int i = -1;
while ( (i = value.indexOf('\\', i + 1)) >= 0 ) {
value.remove(i, 1);
}
}
values.append(value);
end++;
} else {
//unquoted string
end = start;
while (end < len && b[end] != ',' && !isWhiteSpace(b[end])) {
end++;
}
values.append(QByteArray(b + start, end - start));
}
//the quoted string has ended, but only a comma ends a key-value pair
while (end < len && isWhiteSpace(b[end])) {
end++;
}
// garbage, here should be end or field delimiter (comma)
if (end < len && b[end] != ',') {
kDebug(7113) << "unexpected character" << b[end] << "found in WWW-authentication header where token boundary (,) was expected";
break;
}
}
// ensure every key has a value
// WARNING: Do not remove the > 1 check or parsing a Type 1 NTLM
// authentication challenge will surely fail.
if (values.count() > 1 && values.count() % 2) {
values.removeLast();
}
return values;
}
static QByteArray valueForKey(const QList<QByteArray> &ba, const QByteArray &key)
{
for (int i = 0, count = ba.count(); (i + 1) < count; i += 2) {
if (ba[i] == key) {
return ba[i + 1];
}
}
return QByteArray();
}
KAbstractHttpAuthentication::KAbstractHttpAuthentication(KConfigGroup *config)
:m_config(config), m_finalAuthStage(false)
{
reset();
}
KAbstractHttpAuthentication::~KAbstractHttpAuthentication()
{
}
QByteArray KAbstractHttpAuthentication::bestOffer(const QList<QByteArray> &offers)
{
// choose the most secure auth scheme offered
QByteArray negotiateOffer;
QByteArray digestOffer;
QByteArray ntlmOffer;
QByteArray basicOffer;
Q_FOREACH (const QByteArray &offer, offers) {
const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
#if HAVE_LIBGSSAPI
if (scheme == "negotiate") { // krazy:exclude=strings
negotiateOffer = offer;
} else
#endif
if (scheme == "digest") { // krazy:exclude=strings
digestOffer = offer;
} else if (scheme == "ntlm") { // krazy:exclude=strings
ntlmOffer = offer;
} else if (scheme == "basic") { // krazy:exclude=strings
basicOffer = offer;
}
}
if (!negotiateOffer.isEmpty()) {
return negotiateOffer;
}
if (!digestOffer.isEmpty()) {
return digestOffer;
}
if (!ntlmOffer.isEmpty()) {
return ntlmOffer;
}
return basicOffer; //empty or not...
}
KAbstractHttpAuthentication *KAbstractHttpAuthentication::newAuth(const QByteArray &offer, KConfigGroup* config)
{
const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
#if HAVE_LIBGSSAPI
if (scheme == "negotiate") { // krazy:exclude=strings
return new KHttpNegotiateAuthentication(config);
} else
#endif
if (scheme == "digest") { // krazy:exclude=strings
return new KHttpDigestAuthentication();
} else if (scheme == "ntlm") { // krazy:exclude=strings
return new KHttpNtlmAuthentication(config);
} else if (scheme == "basic") { // krazy:exclude=strings
return new KHttpBasicAuthentication();
}
return 0;
}
QList< QByteArray > KAbstractHttpAuthentication::splitOffers(const QList< QByteArray >& offers)
{
// first detect if one entry may contain multiple offers
QList<QByteArray> alloffers;
foreach(QByteArray offer, offers) {
QByteArray scheme, cont;
parseChallenge(offer, &scheme, &cont);
while (!cont.isEmpty()) {
offer.chop(cont.length());
alloffers << offer;
offer = cont;
cont.clear();
parseChallenge(offer, &scheme, &cont);
}
alloffers << offer;
}
return alloffers;
}
void KAbstractHttpAuthentication::reset()
{
m_scheme.clear();
m_challenge.clear();
m_challengeText.clear();
m_resource.clear();
m_httpMethod.clear();
m_isError = false;
m_needCredentials = true;
m_forceKeepAlive = false;
m_forceDisconnect = false;
m_keepPassword = false;
m_headerFragment.clear();
m_username.clear();
m_password.clear();
}
void KAbstractHttpAuthentication::setChallenge(const QByteArray &c, const QUrl &resource,
const QByteArray &httpMethod)
{
reset();
m_challengeText = c.trimmed();
m_challenge = parseChallenge(m_challengeText, &m_scheme);
Q_ASSERT(m_scheme.toLower() == scheme().toLower());
m_resource = resource;
m_httpMethod = httpMethod.trimmed();
}
QString KAbstractHttpAuthentication::realm() const
{
const QByteArray realm = valueForKey(m_challenge, "realm");
// TODO: Find out what this is supposed to address. The site mentioned below does not exist.
if (KLocale::global()->language().contains(QLatin1String("ru"))) {
//for sites like lib.homelinux.org
return QTextCodec::codecForName("CP1251")->toUnicode(realm);
}
return QString::fromLatin1(realm.constData(), realm.length());
}
void KAbstractHttpAuthentication::authInfoBoilerplate(KIO::AuthInfo *a) const
{
a->url = m_resource;
a->username = m_username;
a->password = m_password;
a->verifyPath = supportsPathMatching();
a->realmValue = realm();
a->digestInfo = QLatin1String(authDataToCache());
a->keepPassword = m_keepPassword;
}
void KAbstractHttpAuthentication::generateResponseCommon(const QString &user, const QString &password)
{
if (m_scheme.isEmpty() || m_httpMethod.isEmpty()) {
m_isError = true;
return;
}
if (m_needCredentials) {
m_username = user;
m_password = password;
}
m_isError = false;
m_forceKeepAlive = false;
m_forceDisconnect = false;
m_finalAuthStage = true;
}
QByteArray KHttpBasicAuthentication::scheme() const
{
return "Basic";
}
void KHttpBasicAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
}
void KHttpBasicAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
m_headerFragment = "Basic ";
m_headerFragment += QByteArray(m_username.toLatin1() + ':' + m_password.toLatin1()).toBase64();
m_headerFragment += "\r\n";
}
QByteArray KHttpDigestAuthentication::scheme() const
{
return "Digest";
}
void KHttpDigestAuthentication::setChallenge(const QByteArray &c, const QUrl &resource,
const QByteArray &httpMethod)
{
QString oldUsername;
QString oldPassword;
if (valueForKey(m_challenge, "stale").toLower() == "true") {
// stale nonce: the auth failure that triggered this round of authentication is an artifact
// of digest authentication. the credentials are probably still good, so keep them.
oldUsername = m_username;
oldPassword = m_password;
}
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
// keep credentials *and* don't ask for new ones
m_needCredentials = false;
m_username = oldUsername;
m_password = oldPassword;
}
}
void KHttpDigestAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
}
struct DigestAuthInfo
{
QByteArray nc;
QByteArray qop;
QByteArray realm;
QByteArray nonce;
QByteArray method;
QByteArray cnonce;
QByteArray username;
QByteArray password;
QList<KUrl> digestURIs;
QByteArray algorithm;
QByteArray entityBody;
};
//calculateResponse() from the original HTTPProtocol
static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
{
QCryptographicHash md(QCryptographicHash::Md5);
QByteArray HA1;
QByteArray HA2;
// Calculate H(A1)
QByteArray authStr = info.username;
authStr += ':';
authStr += info.realm;
authStr += ':';
authStr += info.password;
md.addData( authStr );
if ( info.algorithm.toLower() == "md5-sess" )
{
authStr = md.result().toHex();
authStr += ':';
authStr += info.nonce;
authStr += ':';
authStr += info.cnonce;
md.reset();
md.addData( authStr );
}
HA1 = md.result().toHex();
kDebug(7113) << "A1 => " << HA1;
// Calcualte H(A2)
authStr = info.method;
authStr += ':';
authStr += resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
if ( info.qop == "auth-int" )
{
authStr += ':';
md.reset();
md.addData(info.entityBody);
authStr += md.result().toHex();
}
md.reset();
md.addData( authStr );
HA2 = md.result().toHex();
kDebug(7113) << "A2 => " << HA2;
// Calcualte the response.
authStr = HA1;
authStr += ':';
authStr += info.nonce;
authStr += ':';
if ( !info.qop.isEmpty() )
{
authStr += info.nc;
authStr += ':';
authStr += info.cnonce;
authStr += ':';
authStr += info.qop;
authStr += ':';
}
authStr += HA2;
md.reset();
md.addData( authStr );
const QByteArray response = md.result().toHex();
kDebug(7113) << "Response =>" << response;
return response;
}
void KHttpDigestAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
// magic starts here (this part is slightly modified from the original in HTTPProtocol)
DigestAuthInfo info;
info.username = m_username.toLatin1(); //### charset breakage
info.password = m_password.toLatin1(); //###
// info.entityBody = p; // FIXME: send digest of data for POST action ??
info.realm = "";
info.nonce = "";
info.qop = "";
// cnonce is recommended to contain about 64 bits of entropy
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
info.cnonce = m_nonce;
#else
info.cnonce = KRandom::randomString(16).toLatin1();
#endif
// HACK: Should be fixed according to RFC 2617 section 3.2.2
info.nc = "00000001";
// Set the method used...
info.method = m_httpMethod;
// Parse the Digest response....
info.realm = valueForKey(m_challenge, "realm");
info.algorithm = valueForKey(m_challenge, "algorithm");
if (info.algorithm.isEmpty()) {
info.algorithm = valueForKey(m_challenge, "algorith");
}
if (info.algorithm.isEmpty()) {
info.algorithm = "MD5";
}
Q_FOREACH (const QByteArray &path, valueForKey(m_challenge, "domain").split(' ')) {
KUrl u(m_resource, QString::fromLatin1(path));
if (u.isValid()) {
info.digestURIs.append(u);
}
}
info.nonce = valueForKey(m_challenge, "nonce");
QByteArray opaque = valueForKey(m_challenge, "opaque");
info.qop = valueForKey(m_challenge, "qop");
// NOTE: Since we do not have access to the entity body, we cannot support
// the "auth-int" qop value ; so if the server returns a comma separated
// list of qop values, prefer "auth".See RFC 2617 sec 3.2.2 for the details.
// If "auth" is not present or it is set to "auth-int" only, then we simply
// print a warning message and disregard the qop option altogether.
if (info.qop.contains(',')) {
const QList<QByteArray> values = info.qop.split(',');
if (info.qop.contains("auth"))
info.qop = "auth";
else {
kWarning(7113) << "Unsupported digest authentication qop parameters:" << values;
info.qop.clear();
}
} else if (info.qop == "auth-int") {
kWarning(7113) << "Unsupported digest authentication qop parameter:" << info.qop;
info.qop.clear();
}
if (info.realm.isEmpty() || info.nonce.isEmpty()) {
// ### proper error return
m_isError = true;
return;
}
// If the "domain" attribute was not specified and the current response code
// is authentication needed, add the current request url to the list over which
// this credential can be automatically applied.
if (info.digestURIs.isEmpty() /*###&& (m_request.responseCode == 401 || m_request.responseCode == 407)*/)
info.digestURIs.append (m_resource);
else
{
// Verify whether or not we should send a cached credential to the
// server based on the stored "domain" attribute...
bool send = true;
// Determine the path of the request url...
QString requestPath = m_resource.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
if (requestPath.isEmpty())
requestPath = QLatin1Char('/');
Q_FOREACH (const KUrl &u, info.digestURIs)
{
send &= (m_resource.scheme().toLower() == u.scheme().toLower());
send &= (m_resource.host().toLower() == u.host().toLower());
if (m_resource.port() > 0 && u.port() > 0)
send &= (m_resource.port() == u.port());
QString digestPath = u.directory (KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
if (digestPath.isEmpty())
digestPath = QLatin1Char('/');
send &= (requestPath.startsWith(digestPath));
if (send)
break;
}
if (!send) {
m_isError = true;
return;
}
}
kDebug(7113) << "RESULT OF PARSING:";
kDebug(7113) << " algorithm: " << info.algorithm;
kDebug(7113) << " realm: " << info.realm;
kDebug(7113) << " nonce: " << info.nonce;
kDebug(7113) << " opaque: " << opaque;
kDebug(7113) << " qop: " << info.qop;
// Calculate the response...
const QByteArray response = calculateResponse(info, m_resource);
QByteArray auth = "Digest username=\"";
auth += info.username;
auth += "\", realm=\"";
auth += info.realm;
auth += "\"";
auth += ", nonce=\"";
auth += info.nonce;
auth += "\", uri=\"";
auth += m_resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
if (!info.algorithm.isEmpty()) {
auth += "\", algorithm=";
auth += info.algorithm;
}
if ( !info.qop.isEmpty() )
{
auth += ", qop=";
auth += info.qop;
auth += ", cnonce=\"";
auth += info.cnonce;
auth += "\", nc=";
auth += info.nc;
}
auth += ", response=\"";
auth += response;
if ( !opaque.isEmpty() )
{
auth += "\", opaque=\"";
auth += opaque;
}
auth += "\"\r\n";
// magic ends here
// note that auth already contains \r\n
m_headerFragment = auth;
}
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
void KHttpDigestAuthentication::setDigestNonceValue(const QByteArray& nonce)
{
m_nonce = nonce;
}
#endif
QByteArray KHttpNtlmAuthentication::scheme() const
{
return "NTLM";
}
void KHttpNtlmAuthentication::setChallenge(const QByteArray &c, const QUrl &resource,
const QByteArray &httpMethod)
{
QString oldUsername, oldPassword;
if (!m_finalAuthStage && !m_username.isEmpty() && !m_password.isEmpty()) {
oldUsername = m_username;
oldPassword = m_password;
}
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
m_username = oldUsername;
m_password = oldPassword;
}
// The type 1 message we're going to send needs no credentials;
// they come later in the type 3 message.
m_needCredentials = m_challenge.isEmpty();
}
void KHttpNtlmAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
// Every auth scheme is supposed to supply a realm according to the RFCs. Of course this doesn't
// prevent Microsoft from not doing it... Dummy value!
// we don't have the username yet which may (may!) contain a domain, so we really have no choice
ai->realmValue = QLatin1String("NTLM");
}
void KHttpNtlmAuthentication::generateResponse(const QString &_user, const QString &password)
{
generateResponseCommon(_user, password);
if (m_isError) {
return;
}
QByteArray buf;
if (m_challenge.isEmpty()) {
m_finalAuthStage = false;
// first, send type 1 message (with empty domain, workstation..., but it still works)
if (!KNTLM::getNegotiate(buf)) {
kWarning(7113) << "Error while constructing Type 1 NTLM authentication request";
m_isError = true;
return;
}
} else {
m_finalAuthStage = true;
// we've (hopefully) received a valid type 2 message: send type 3 message as last step
QString user, domain;
if (m_username.contains(QLatin1Char('\\'))) {
domain = m_username.section(QLatin1Char('\\'), 0, 0);
user = m_username.section(QLatin1Char('\\'), 1);
} else {
user = m_username;
}
m_forceKeepAlive = true;
const QByteArray challenge = QByteArray::fromBase64(m_challenge[0]);
KNTLM::AuthFlags flags = KNTLM::Add_LM;
if (!m_config || !m_config->readEntry("EnableNTLMv2Auth", false)) {
flags |= KNTLM::Force_V1;
}
if (!KNTLM::getAuth(buf, challenge, user, m_password, domain, QLatin1String("WORKSTATION"), flags)) {
kWarning(7113) << "Error while constructing Type 3 NTLM authentication request";
m_isError = true;
return;
}
}
m_headerFragment = "NTLM ";
m_headerFragment += buf.toBase64();
m_headerFragment += "\r\n";
return;
}
//////////////////////////
#if HAVE_LIBGSSAPI
// just an error message formatter
static QByteArray gssError(int major_status, int minor_status)
{
OM_uint32 new_status;
OM_uint32 msg_ctx = 0;
gss_buffer_desc major_string;
gss_buffer_desc minor_string;
OM_uint32 ret;
QByteArray errorstr;
do {
ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string);
errorstr += (const char *)major_string.value;
errorstr += ' ';
ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string);
errorstr += (const char *)minor_string.value;
errorstr += ' ';
} while (!GSS_ERROR(ret) && msg_ctx != 0);
return errorstr;
}
QByteArray KHttpNegotiateAuthentication::scheme() const
{
return "Negotiate";
}
void KHttpNegotiateAuthentication::setChallenge(const QByteArray &c, const QUrl &resource,
const QByteArray &httpMethod)
{
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
// GSSAPI knows how to get the credentials on its own
m_needCredentials = false;
}
void KHttpNegotiateAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
//### does GSSAPI supply anything realm-like? dummy value for now.
ai->realmValue = QLatin1String("Negotiate");
}
void KHttpNegotiateAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
OM_uint32 major_status, minor_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_name_t server;
gss_ctx_id_t ctx;
gss_OID mech_oid;
static gss_OID_desc krb5_oid_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
static gss_OID_desc spnego_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"};
gss_OID_set mech_set;
gss_OID tmp_oid;
ctx = GSS_C_NO_CONTEXT;
mech_oid = &krb5_oid_desc;
// see whether we can use the SPNEGO mechanism
major_status = gss_indicate_mechs(&minor_status, &mech_set);
if (GSS_ERROR(major_status)) {
kDebug(7113) << "gss_indicate_mechs failed: " << gssError(major_status, minor_status);
} else {
for (uint i = 0; i < mech_set->count; i++) {
tmp_oid = &mech_set->elements[i];
if (tmp_oid->length == spnego_oid_desc.length &&
!memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) {
kDebug(7113) << "found SPNEGO mech";
mech_oid = &spnego_oid_desc;
break;
}
}
gss_release_oid_set(&minor_status, &mech_set);
}
// the service name is "HTTP/f.q.d.n"
QByteArray servicename = "HTTP@";
- servicename += m_resource.host().toAscii();
+ servicename += m_resource.host().toLatin1();
input_token.value = (void *)servicename.data();
input_token.length = servicename.length() + 1;
major_status = gss_import_name(&minor_status, &input_token,
GSS_C_NT_HOSTBASED_SERVICE, &server);
input_token.value = NULL;
input_token.length = 0;
if (GSS_ERROR(major_status)) {
kDebug(7113) << "gss_import_name failed: " << gssError(major_status, minor_status);
m_isError = true;
return;
}
OM_uint32 req_flags;
if (m_config && m_config->readEntry("DelegateCredentialsOn", false))
req_flags = GSS_C_DELEG_FLAG;
else
req_flags = 0;
// GSSAPI knows how to get the credentials its own way, so don't ask for any
major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
&ctx, server, mech_oid,
req_flags, GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
GSS_C_NO_BUFFER, NULL, &output_token,
NULL, NULL);
if (GSS_ERROR(major_status) || (output_token.length == 0)) {
kDebug(7113) << "gss_init_sec_context failed: " << gssError(major_status, minor_status);
gss_release_name(&minor_status, &server);
if (ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
ctx = GSS_C_NO_CONTEXT;
}
m_isError = true;
return;
}
m_headerFragment = "Negotiate ";
m_headerFragment += QByteArray::fromRawData(static_cast<const char *>(output_token.value),
output_token.length).toBase64();
m_headerFragment += "\r\n";
// free everything
gss_release_name(&minor_status, &server);
if (ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
ctx = GSS_C_NO_CONTEXT;
}
gss_release_buffer(&minor_status, &output_token);
}
#endif // HAVE_LIBGSSAPI
diff --git a/kioslave/http/parsinghelpers.cpp b/kioslave/http/parsinghelpers.cpp
index 5388056c63..6af51bc631 100644
--- a/kioslave/http/parsinghelpers.cpp
+++ b/kioslave/http/parsinghelpers.cpp
@@ -1,597 +1,597 @@
/* This file is part of the KDE libraries
Copyright (C) 2008 Andreas Hartmetz <ahartmetz@gmail.com>
Copyright (C) 2010,2011 Rolf Eike Beer <kde@opensource.sf-tec.de>
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 <ctype.h>
#include <QDir>
#include <QMap>
#include <QTextCodec>
#include <QUrl>
#include <kdebug.h>
// Advance *pos beyond spaces / tabs
static void skipSpace(const char input[], int *pos, int end)
{
int idx = *pos;
while (idx < end && (input[idx] == ' ' || input[idx] == '\t')) {
idx++;
}
*pos = idx;
return;
}
// Advance *pos to start of next line while being forgiving about line endings.
// Return false if the end of the header has been reached, true otherwise.
static bool nextLine(const char input[], int *pos, int end)
{
int idx = *pos;
while (idx < end && input[idx] != '\r' && input[idx] != '\n') {
idx++;
}
int rCount = 0;
int nCount = 0;
while (idx < end && qMax(rCount, nCount) < 2 && (input[idx] == '\r' || input[idx] == '\n')) {
input[idx] == '\r' ? rCount++ : nCount++;
idx++;
}
if (idx < end && qMax(rCount, nCount) == 2 && qMin(rCount, nCount) == 1) {
// if just one of the others is missing eat it too.
// this ensures that conforming headers using the proper
// \r\n sequence (and also \n\r) will be parsed correctly.
if ((rCount == 1 && input[idx] == '\r') || (nCount == 1 && input[idx] == '\n')) {
idx++;
}
}
*pos = idx;
return idx < end && rCount < 2 && nCount < 2;
}
// QByteArray::fromPercentEncoding() does not notify us about encoding errors so we need
// to check here if this is valid at all.
static bool isValidPercentEncoding(const QByteArray &data)
{
int i = 0;
const int last = data.length() - 1;
const char *d = data.constData();
while ( (i = data.indexOf('%', i)) != -1) {
if ( i >= last - 2 )
return false;
if ( ! isxdigit(d[i + 1]) )
return false;
if ( ! isxdigit(d[i + 2]) )
return false;
i++;
}
return true;
}
QByteArray TokenIterator::next()
{
QPair<int, int> token = m_tokens[m_currentToken++];
//fromRawData brings some speed advantage but also the requirement to keep the text buffer
//around. this together with implicit sharing (you don't know where copies end up)
//is dangerous!
//return QByteArray::fromRawData(&m_buffer[token.first], token.second - token.first);
return QByteArray(&m_buffer[token.first], token.second - token.first);
}
QByteArray TokenIterator::current() const
{
QPair<int, int> token = m_tokens[m_currentToken - 1];
//return QByteArray::fromRawData(&m_buffer[token.first], token.second - token.first);
return QByteArray(&m_buffer[token.first], token.second - token.first);
}
QList<QByteArray> TokenIterator::all() const
{
QList<QByteArray> ret;
for (int i = 0; i < m_tokens.count(); i++) {
QPair<int, int> token = m_tokens[i];
ret.append(QByteArray(&m_buffer[token.first], token.second - token.first));
}
return ret;
}
HeaderTokenizer::HeaderTokenizer(char *buffer)
: m_buffer(buffer)
{
// add information about available headers and whether they have one or multiple,
// comma-separated values.
//The following response header fields are from RFC 2616 unless otherwise specified.
//Hint: search the web for e.g. 'http "accept-ranges header"' to find information about
//a header field.
static const HeaderFieldTemplate headerFieldTemplates[] = {
{"accept-ranges", false},
{"age", false},
{"cache-control", true},
{"connection", true},
{"content-disposition", false}, //is multi-valued in a way, but with ";" separator!
{"content-encoding", true},
{"content-language", true},
{"content-length", false},
{"content-location", false},
{"content-md5", false},
{"content-type", false},
{"date", false},
{"dav", true}, //RFC 2518
{"etag", false},
{"expires", false},
{"keep-alive", true}, //RFC 2068
{"last-modified", false},
{"link", false}, //RFC 2068, multi-valued with ";" separator
{"location", false},
{"p3p", true}, // http://www.w3.org/TR/P3P/
{"pragma", true},
{"proxy-authenticate", false}, //complicated multi-valuedness: quoted commas don't separate
//multiple values. we handle this at a higher level.
{"proxy-connection", true}, //inofficial but well-known; to avoid misunderstandings
//when using "connection" when talking to a proxy.
{"refresh", false}, //not sure, only found some mailing list posts mentioning it
{"set-cookie", false}, //RFC 2109; the multi-valuedness seems to be usually achieved
//by sending several instances of this field as opposed to
//usually comma-separated lists with maybe multiple instances.
{"transfer-encoding", true},
{"upgrade", true},
{"warning", true},
{"www-authenticate", false} //see proxy-authenticate
};
for (uint i = 0; i < sizeof(headerFieldTemplates) / sizeof(HeaderFieldTemplate); i++) {
const HeaderFieldTemplate &ft = headerFieldTemplates[i];
insert(QByteArray(ft.name), HeaderField(ft.isMultiValued));
}
}
int HeaderTokenizer::tokenize(int begin, int end)
{
char *buf = m_buffer; //keep line length in check :/
int idx = begin;
int startIdx = begin; //multi-purpose start of current token
bool multiValuedEndedWithComma = false; //did the last multi-valued line end with a comma?
QByteArray headerKey;
do {
if (buf[idx] == ' ' || buf [idx] == '\t') {
// line continuation; preserve startIdx except (see below)
if (headerKey.isEmpty()) {
continue;
}
// turn CR/LF into spaces for later parsing convenience
int backIdx = idx - 1;
while (backIdx >= begin && (buf[backIdx] == '\r' || buf[backIdx] == '\n')) {
buf[backIdx--] = ' ';
}
// multiple values, comma-separated: add new value or continue previous?
if (operator[](headerKey).isMultiValued) {
if (multiValuedEndedWithComma) {
// start new value; this is almost like no line continuation
skipSpace(buf, &idx, end);
startIdx = idx;
} else {
// continue previous value; this is tricky. unit tests to the rescue!
if (operator[](headerKey).beginEnd.last().first == startIdx) {
// remove entry, it will be re-added because already idx != startIdx
operator[](headerKey).beginEnd.removeLast();
} else {
// no comma, no entry: the prev line was whitespace only - start new value
skipSpace(buf, &idx, end);
startIdx = idx;
}
}
}
} else {
// new field
startIdx = idx;
// also make sure that there is at least one char after the colon
while (idx < (end - 1) && buf[idx] != ':' && buf[idx] != '\r' && buf[idx] != '\n') {
buf[idx] = tolower(buf[idx]);
idx++;
}
if (buf[idx] != ':') {
//malformed line: no colon
headerKey.clear();
continue;
}
headerKey = QByteArray(&buf[startIdx], idx - startIdx);
if (!contains(headerKey)) {
//we don't recognize this header line
headerKey.clear();
continue;
}
// skip colon & leading whitespace
idx++;
skipSpace(buf, &idx, end);
startIdx = idx;
}
// we have the name/key of the field, now parse the value
if (!operator[](headerKey).isMultiValued) {
// scan to end of line
while (idx < end && buf[idx] != '\r' && buf[idx] != '\n') {
idx++;
}
if (!operator[](headerKey).beginEnd.isEmpty()) {
// there already is an entry; are we just in a line continuation?
if (operator[](headerKey).beginEnd.last().first == startIdx) {
// line continuation: delete previous entry and later insert a new, longer one.
operator[](headerKey).beginEnd.removeLast();
}
}
operator[](headerKey).beginEnd.append(QPair<int, int>(startIdx, idx));
} else {
// comma-separated list
while (true) {
//skip one value
while (idx < end && buf[idx] != '\r' && buf[idx] != '\n' && buf[idx] != ',') {
idx++;
}
if (idx != startIdx) {
operator[](headerKey).beginEnd.append(QPair<int, int>(startIdx, idx));
}
multiValuedEndedWithComma = buf[idx] == ',';
//skip comma(s) and leading whitespace, if any respectively
while (idx < end && buf[idx] == ',') {
idx++;
}
skipSpace(buf, &idx, end);
//next value or end-of-line / end of header?
if (buf[idx] >= end || buf[idx] == '\r' || buf[idx] == '\n') {
break;
}
//next value
startIdx = idx;
}
}
} while (nextLine(buf, &idx, end));
return idx;
}
TokenIterator HeaderTokenizer::iterator(const char *key) const
{
QByteArray keyBa = QByteArray::fromRawData(key, strlen(key));
if (contains(keyBa)) {
return TokenIterator(value(keyBa).beginEnd, m_buffer);
} else {
return TokenIterator(m_nullTokens, m_buffer);
}
}
static void skipLWS(const QString &str, int &pos)
{
while (pos < str.length() && (str[pos] == QLatin1Char(' ') || str[pos] == QLatin1Char('\t'))) {
++pos;
}
}
// keep the common ending, this allows the compiler to join them
static const char typeSpecials[] = "{}*'%()<>@,;:\\\"/[]?=";
static const char attrSpecials[] = "'%()<>@,;:\\\"/[]?=";
static const char valueSpecials[] = "()<>@,;:\\\"/[]?=";
static bool specialChar(const QChar &ch, const char *specials)
{
// WORKAROUND: According to RFC 2616, any character other than ascii
// characters should NOT be allowed in unquoted content-disposition file
// names. However, since none of the major browsers follow this rule, we do
// the same thing here and allow all printable unicode characters. See
// https://bugs.kde.org/show_bug.cgi?id=261223 for the detials.
if (!ch.isPrint()) {
return true;
}
for (int i = qstrlen(specials) - 1; i >= 0; i--) {
if (ch == QLatin1Char(specials[i])) {
return true;
}
}
return false;
}
/**
* read and parse the input until the given terminator
* @param str input string to parse
* @param term terminator
* @param pos position marker in the input string
* @param specials characters forbidden in this section
* @return the next section or an empty string if it was invalid
*
* Extracts token-like input until terminator char or EOL.
* Also skips over the terminator.
*
* pos is correctly incremented even if this functions returns
* an empty string so this can be used to skip over invalid
* parts and continue.
*/
static QString extractUntil(const QString &str, QChar term, int &pos, const char *specials)
{
QString out;
skipLWS(str, pos);
bool valid = true;
while (pos < str.length() && (str[pos] != term)) {
out += str[pos];
valid = (valid && !specialChar(str[pos], specials));
++pos;
}
if (pos < str.length()) { // Stopped due to finding term
++pos;
}
if (!valid) {
return QString();
}
// Remove trailing linear whitespace...
while (out.endsWith(QLatin1Char(' ')) || out.endsWith(QLatin1Char('\t'))) {
out.chop(1);
}
if (out.contains(QLatin1Char(' '))) {
out.clear();
}
return out;
}
// As above, but also handles quotes..
// pos is set to -1 on parse error
static QString extractMaybeQuotedUntil(const QString &str, int &pos)
{
const QChar term = QLatin1Char(';');
skipLWS(str, pos);
// Are we quoted?
if (pos < str.length() && str[pos] == QLatin1Char('"')) {
QString out;
// Skip the quote...
++pos;
// when quoted we also need an end-quote
bool endquote = false;
// Parse until trailing quote...
while (pos < str.length()) {
if (str[pos] == QLatin1Char('\\') && pos + 1 < str.length()) {
// quoted-pair = "\" CHAR
out += str[pos + 1];
pos += 2; // Skip both...
} else if (str[pos] == QLatin1Char('"')) {
++pos;
endquote = true;
break;
} else if (!str[pos].isPrint()) { // Don't allow CTL's RFC 2616 sec 2.2
break;
} else {
out += str[pos];
++pos;
}
}
if (!endquote) {
pos = -1;
return QString();
}
// Skip until term..
while (pos < str.length() && (str[pos] != term)) {
if ((str[pos] != QLatin1Char(' ')) && (str[pos] != QLatin1Char('\t'))) {
pos = -1;
return QString();
}
++pos;
}
if (pos < str.length()) { // Stopped due to finding term
++pos;
}
return out;
} else {
return extractUntil(str, term, pos, valueSpecials);
}
}
static QMap<QString, QString> contentDispositionParserInternal(const QString &disposition)
{
kDebug(7113) << "disposition: " << disposition;
int pos = 0;
const QString strDisposition = extractUntil(disposition, QLatin1Char(';'), pos, typeSpecials).toLower();
QMap<QString, QString> parameters;
QMap<QString, QString> contparams; // all parameters that contain continuations
QMap<QString, QString> encparams; // all parameters that have character encoding
// the type is invalid, the complete header is junk
if (strDisposition.isEmpty()) {
return parameters;
}
parameters.insert(QLatin1String("type"), strDisposition);
while (pos < disposition.length()) {
QString key = extractUntil(disposition, QLatin1Char('='), pos, attrSpecials).toLower();
if (key.isEmpty()) {
// parse error in this key: do not parse more, but add up
// everything we already got
kDebug(7113) << "parse error in key, abort parsing";
break;
}
QString val;
if (key.endsWith(QLatin1Char('*'))) {
val = extractUntil(disposition, QLatin1Char(';'), pos, valueSpecials);
} else {
val = extractMaybeQuotedUntil(disposition, pos);
}
if (val.isEmpty()) {
if (pos == -1) {
kDebug(7113) << "parse error in value, abort parsing";
break;
}
continue;
}
const int spos = key.indexOf(QLatin1Char('*'));
if (spos == key.length() - 1) {
key.chop(1);
encparams.insert(key, val);
} else if (spos >= 0) {
contparams.insert(key, val);
} else if (parameters.contains(key)) {
kDebug(7113) << "duplicate key" << key << "found, ignoring everything more";
parameters.remove(key);
return parameters;
} else {
parameters.insert(key, val);
}
}
QMap<QString, QString>::iterator i = contparams.begin();
while (i != contparams.end()) {
QString key = i.key();
int spos = key.indexOf(QLatin1Char('*'));
bool hasencoding = false;
if (key.at(spos + 1) != QLatin1Char('0')) {
++i;
continue;
}
// no leading zeros allowed, so delete the junk
int klen = key.length();
if (klen > spos + 2) {
// nothing but continuations and encodings may insert * into parameter name
if ((klen > spos + 3) || ((klen == spos + 3) && (key.at(spos + 2) != QLatin1Char('*')))) {
kDebug(7113) << "removing invalid key " << key << "with val" << i.value() << key.at(spos + 2);
i = contparams.erase(i);
continue;
}
hasencoding = true;
}
int seqnum = 1;
QMap<QString, QString>::iterator partsi;
// we do not need to care about encoding specifications: only the first
// part is allowed to have one
QString val = i.value();
key.chop(hasencoding ? 2 : 1);
while ((partsi = contparams.find(key + QString::number(seqnum))) != contparams.end()) {
val += partsi.value();
contparams.erase(partsi);
}
i = contparams.erase(i);
key.chop(1);
if (hasencoding) {
encparams.insert(key, val);
} else {
if (parameters.contains(key)) {
kDebug(7113) << "duplicate key" << key << "found, ignoring everything more";
parameters.remove(key);
return parameters;
}
parameters.insert(key, val);
}
}
for (QMap<QString, QString>::iterator i = encparams.begin(); i != encparams.end(); ++i) {
QString val = i.value();
// RfC 2231 encoded character set in filename
int spos = val.indexOf(QLatin1Char('\''));
if (spos == -1) {
continue;
}
int npos = val.indexOf(QLatin1Char('\''), spos + 1);
if (npos == -1) {
continue;
}
const QString charset = val.left(spos);
const QString lang = val.mid(spos + 1, npos - spos - 1);
- const QByteArray encodedVal = val.mid(npos + 1).toAscii();
+ const QByteArray encodedVal = val.mid(npos + 1).toLatin1();
if ( ! isValidPercentEncoding(encodedVal) )
continue;
const QByteArray rawval = QByteArray::fromPercentEncoding(encodedVal);
if (charset.isEmpty() || (charset == QLatin1String("us-ascii"))) {
bool valid = true;
for (int j = rawval.length() - 1; (j >= 0) && valid; j--) {
valid = (rawval.at(j) >= 32);
}
if (!valid)
continue;
- val = QString::fromAscii(rawval.constData());
+ val = QString::fromLatin1(rawval.constData());
} else {
- QTextCodec *codec = QTextCodec::codecForName(charset.toAscii());
+ QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1());
if (!codec)
continue;
val = codec->toUnicode(rawval);
}
parameters.insert(i.key(), val);
}
return parameters;
}
static QMap<QString, QString> contentDispositionParser(const QString &disposition)
{
QMap<QString, QString> parameters = contentDispositionParserInternal(disposition);
const QLatin1String fn("filename");
if (parameters.contains(fn)) {
// Content-Disposition is not allowed to dictate directory
// path, thus we extract the filename only.
const QString val = QDir::toNativeSeparators(parameters[fn]);
int slpos = val.lastIndexOf(QDir::separator());
if (slpos > -1) {
parameters.insert(fn, val.mid(slpos + 1));
}
}
return parameters;
}
diff --git a/kpty/kpty.cpp b/kpty/kpty.cpp
index 0baf1cabd9..7a416b060c 100644
--- a/kpty/kpty.cpp
+++ b/kpty/kpty.cpp
@@ -1,707 +1,707 @@
/*
This file is part of the KDE libraries
Copyright (C) 2002 Waldo Bastian <bastian@kde.org>
Copyright (C) 2002-2003,2007-2008 Oswald Buddenhagen <ossi@kde.org>
Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org>
Author Adriaan de Groot <groot@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.
*/
#include "kpty_p.h"
#include <config-prefix.h>
#ifdef __sgi
#define __svr4__
#endif
#ifdef __osf__
#define _OSF_SOURCE
#include <float.h>
#endif
#ifdef _AIX
#define _ALL_SOURCE
#endif
// __USE_XOPEN isn't defined by default in ICC
// (needed for ptsname(), grantpt() and unlockpt())
#ifdef __INTEL_COMPILER
# ifndef __USE_XOPEN
# define __USE_XOPEN
# endif
#endif
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <grp.h>
#if HAVE_PTY_H
# include <pty.h>
#endif
#if HAVE_LIBUTIL_H
# include <libutil.h>
#elif HAVE_UTIL_H
# include <util.h>
#endif
#if HAVE_UTEMPTER
extern "C" {
# include <utempter.h>
}
#else
# include <utmp.h>
# if HAVE_UTMPX
# include <utmpx.h>
# endif
# if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
# define _PATH_UTMPX _UTMPX_FILE
# endif
# if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
# define _PATH_WTMPX _WTMPX_FILE
# endif
#endif
/* for HP-UX (some versions) the extern C is needed, and for other
platforms it doesn't hurt */
extern "C" {
#include <termios.h>
#if HAVE_TERMIO_H
# include <termio.h> // struct winsize on some systems
#endif
}
#if defined (_HPUX_SOURCE)
# define _TERMIOS_INCLUDED
# include <bsdtty.h>
#endif
#if HAVE_SYS_STROPTS_H
# include <sys/stropts.h> // Defines I_PUSH
# define _NEW_TTY_CTRL
#endif
#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
# define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
#else
# if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__) || defined(__sun)
# define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
# else
# define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
# endif
#endif
#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
# define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
#else
# if defined(_HPUX_SOURCE) || defined(__CYGWIN__) || defined(__sun)
# define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
# else
# define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
# endif
#endif
#include <kdebug.h>
#include <kde_file.h>
#include <QtCore/Q_PID>
#define TTY_GROUP "tty"
#ifndef PATH_MAX
# ifdef MAXPATHLEN
# define PATH_MAX MAXPATHLEN
# else
# define PATH_MAX 1024
# endif
#endif
///////////////////////
// private functions //
///////////////////////
//////////////////
// private data //
//////////////////
KPtyPrivate::KPtyPrivate(KPty* parent) :
masterFd(-1), slaveFd(-1), ownMaster(true), q_ptr(parent)
{
}
KPtyPrivate::~KPtyPrivate()
{
}
#if ! HAVE_OPENPTY
bool KPtyPrivate::chownpty(bool grant)
{
return !QProcess::execute(QFile::decodeName(CMAKE_INSTALL_PREFIX "/" LIBEXEC_INSTALL_DIR "/kgrantpty"),
QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd));
}
#endif
/////////////////////////////
// public member functions //
/////////////////////////////
KPty::KPty() :
d_ptr(new KPtyPrivate(this))
{
}
KPty::KPty(KPtyPrivate *d) :
d_ptr(d)
{
d_ptr->q_ptr = this;
}
KPty::~KPty()
{
close();
delete d_ptr;
}
bool KPty::open()
{
Q_D(KPty);
if (d->masterFd >= 0)
return true;
d->ownMaster = true;
QByteArray ptyName;
// Find a master pty that we can open ////////////////////////////////
// Because not all the pty animals are created equal, they want to
// be opened by several different methods.
// We try, as we know them, one by one.
#if HAVE_OPENPTY
char ptsn[PATH_MAX];
if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0))
{
d->masterFd = -1;
d->slaveFd = -1;
kWarning(175) << "Can't open a pseudo teletype";
return false;
}
d->ttyName = ptsn;
#else
#if HAVE__GETPTY // irix
char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0);
if (ptsn) {
d->ttyName = ptsn;
goto grantedpt;
}
#elif HAVE_PTSNAME || defined(TIOCGPTN)
#if HAVE_POSIX_OPENPT
d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY);
#elif HAVE_GETPT
d->masterFd = ::getpt();
#elif defined(PTM_DEVICE)
d->masterFd = KDE_open(PTM_DEVICE, O_RDWR|O_NOCTTY);
#else
# error No method to open a PTY master detected.
#endif
if (d->masterFd >= 0)
{
#if HAVE_PTSNAME
char *ptsn = ptsname(d->masterFd);
if (ptsn) {
d->ttyName = ptsn;
#else
int ptyno;
if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
char buf[32];
sprintf(buf, "/dev/pts/%d", ptyno);
d->ttyName = buf;
#endif
#if HAVE_GRANTPT
if (!grantpt(d->masterFd))
goto grantedpt;
#else
goto gotpty;
#endif
}
::close(d->masterFd);
d->masterFd = -1;
}
#endif // HAVE_PTSNAME || TIOCGPTN
// Linux device names, FIXME: Trouble on other systems?
for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++)
{
for (const char* s4 = "0123456789abcdef"; *s4; s4++)
{
- ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii();
- d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii();
+ ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toLatin1();
+ d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toLatin1();
d->masterFd = KDE_open(ptyName.data(), O_RDWR);
if (d->masterFd >= 0)
{
#ifdef Q_OS_SOLARIS
/* Need to check the process group of the pty.
* If it exists, then the slave pty is in use,
* and we need to get another one.
*/
int pgrp_rtn;
if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
::close(d->masterFd);
d->masterFd = -1;
continue;
}
#endif /* Q_OS_SOLARIS */
if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
{
if (!geteuid())
{
struct group* p = getgrnam(TTY_GROUP);
if (!p)
p = getgrnam("wheel");
gid_t gid = p ? p->gr_gid : getgid ();
chown(d->ttyName.data(), getuid(), gid);
chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
}
goto gotpty;
}
::close(d->masterFd);
d->masterFd = -1;
}
}
}
kWarning(175) << "Can't open a pseudo teletype";
return false;
gotpty:
KDE_struct_stat st;
if (KDE_stat(d->ttyName.data(), &st))
return false; // this just cannot happen ... *cough* Yeah right, I just
// had it happen when pty #349 was allocated. I guess
// there was some sort of leak? I only had a few open.
if (((st.st_uid != getuid()) ||
(st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
!d->chownpty(true))
{
kWarning(175)
<< "chownpty failed for device " << ptyName << "::" << d->ttyName
<< "\nThis means the communication can be eavesdropped." << endl;
}
grantedpt:
#ifdef HAVE_REVOKE
revoke(d->ttyName.data());
#endif
#ifdef HAVE_UNLOCKPT
unlockpt(d->masterFd);
#elif defined(TIOCSPTLCK)
int flag = 0;
ioctl(d->masterFd, TIOCSPTLCK, &flag);
#endif
d->slaveFd = KDE_open(d->ttyName.data(), O_RDWR | O_NOCTTY);
if (d->slaveFd < 0)
{
kWarning(175) << "Can't open slave pseudo teletype";
::close(d->masterFd);
d->masterFd = -1;
return false;
}
#if (defined(__svr4__) || defined(__sgi__) || defined(Q_OS_SOLARIS))
// Solaris uses STREAMS for terminal handling. It is possible
// for the pty handling modules to be left off the stream; in that
// case push them on. ioctl(fd, I_FIND, ...) is documented to return
// 1 if the module is on the stream already.
{
static const char *pt = "ptem";
static const char *ld = "ldterm";
if (ioctl(d->slaveFd, I_FIND, pt) == 0)
ioctl(d->slaveFd, I_PUSH, pt);
if (ioctl(d->slaveFd, I_FIND, ld) == 0)
ioctl(d->slaveFd, I_PUSH, ld);
}
#endif
#endif /* HAVE_OPENPTY */
fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
return true;
}
bool KPty::open(int fd)
{
#if !HAVE_PTSNAME && !defined(TIOCGPTN)
kWarning(175) << "Unsupported attempt to open pty with fd" << fd;
return false;
#else
Q_D(KPty);
if (d->masterFd >= 0) {
kWarning(175) << "Attempting to open an already open pty";
return false;
}
d->ownMaster = false;
# if HAVE_PTSNAME
char *ptsn = ptsname(fd);
if (ptsn) {
d->ttyName = ptsn;
# else
int ptyno;
if (!ioctl(fd, TIOCGPTN, &ptyno)) {
char buf[32];
sprintf(buf, "/dev/pts/%d", ptyno);
d->ttyName = buf;
# endif
} else {
kWarning(175) << "Failed to determine pty slave device for fd" << fd;
return false;
}
d->masterFd = fd;
if (!openSlave()) {
d->masterFd = -1;
return false;
}
return true;
#endif
}
void KPty::closeSlave()
{
Q_D(KPty);
if (d->slaveFd < 0)
return;
::close(d->slaveFd);
d->slaveFd = -1;
}
bool KPty::openSlave()
{
Q_D(KPty);
if (d->slaveFd >= 0)
return true;
if (d->masterFd < 0) {
kWarning(175) << "Attempting to open pty slave while master is closed";
return false;
}
d->slaveFd = KDE_open(d->ttyName.data(), O_RDWR | O_NOCTTY);
if (d->slaveFd < 0) {
kWarning(175) << "Can't open slave pseudo teletype";
return false;
}
fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
return true;
}
void KPty::close()
{
Q_D(KPty);
if (d->masterFd < 0)
return;
closeSlave();
if (d->ownMaster) {
#if ! HAVE_OPENPTY
// don't bother resetting unix98 pty, it will go away after closing master anyway.
if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
if (!geteuid()) {
struct stat st;
if (!stat(d->ttyName.data(), &st)) {
chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
}
} else {
fcntl(d->masterFd, F_SETFD, 0);
d->chownpty(false);
}
}
#endif
::close(d->masterFd);
}
d->masterFd = -1;
}
void KPty::setCTty()
{
Q_D(KPty);
// Setup job control //////////////////////////////////
// Become session leader, process group leader,
// and get rid of the old controlling terminal.
setsid();
// make our slave pty the new controlling terminal.
#ifdef TIOCSCTTY
ioctl(d->slaveFd, TIOCSCTTY, 0);
#else
// __svr4__ hack: the first tty opened after setsid() becomes controlling tty
::close(KDE_open(d->ttyName, O_WRONLY, 0));
#endif
// make our new process group the foreground group on the pty
int pgrp = getpid();
#if defined(_POSIX_VERSION) || defined(__svr4__)
tcsetpgrp(d->slaveFd, pgrp);
#elif defined(TIOCSPGRP)
ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp);
#endif
}
void KPty::login(const char *user, const char *remotehost)
{
#if HAVE_UTEMPTER
Q_D(KPty);
addToUtmp(d->ttyName, remotehost, d->masterFd);
Q_UNUSED(user);
#else
# if HAVE_UTMPX
struct utmpx l_struct;
# else
struct utmp l_struct;
# endif
memset(&l_struct, 0, sizeof(l_struct));
// note: strncpy without terminators _is_ correct here. man 4 utmp
if (user)
strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name));
if (remotehost) {
strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host));
# if HAVE_STRUCT_UTMP_UT_SYSLEN
l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host));
# endif
}
# ifndef __GLIBC__
Q_D(KPty);
const char *str_ptr = d->ttyName.data();
if (!memcmp(str_ptr, "/dev/", 5))
str_ptr += 5;
strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
# if HAVE_STRUCT_UTMP_UT_ID
strncpy(l_struct.ut_id,
str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id),
sizeof(l_struct.ut_id));
# endif
# endif
# if HAVE_UTMPX
gettimeofday(&l_struct.ut_tv, 0);
# else
l_struct.ut_time = time(0);
# endif
# if HAVE_LOGIN
# if HAVE_LOGINX
::loginx(&l_struct);
# else
::login(&l_struct);
# endif
# else
# if HAVE_STRUCT_UTMP_UT_TYPE
l_struct.ut_type = USER_PROCESS;
# endif
# if HAVE_STRUCT_UTMP_UT_PID
l_struct.ut_pid = getpid();
# if HAVE_STRUCT_UTMP_UT_SESSION
l_struct.ut_session = getsid(0);
# endif
# endif
# if HAVE_UTMPX
utmpxname(_PATH_UTMPX);
setutxent();
pututxline(&l_struct);
endutxent();
updwtmpx(_PATH_WTMPX, &l_struct);
# else
utmpname(_PATH_UTMP);
setutent();
pututline(&l_struct);
endutent();
updwtmp(_PATH_WTMP, &l_struct);
# endif
# endif
#endif
}
void KPty::logout()
{
#if HAVE_UTEMPTER
Q_D(KPty);
removeLineFromUtmp(d->ttyName, d->masterFd);
#else
Q_D(KPty);
const char *str_ptr = d->ttyName.data();
if (!memcmp(str_ptr, "/dev/", 5))
str_ptr += 5;
# ifdef __GLIBC__
else {
const char *sl_ptr = strrchr(str_ptr, '/');
if (sl_ptr)
str_ptr = sl_ptr + 1;
}
# endif
# if HAVE_LOGIN
# if HAVE_LOGINX
::logoutx(str_ptr, 0, DEAD_PROCESS);
# else
::logout(str_ptr);
# endif
# else
# if HAVE_UTMPX
struct utmpx l_struct, *ut;
# else
struct utmp l_struct, *ut;
# endif
memset(&l_struct, 0, sizeof(l_struct));
strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
# if HAVE_UTMPX
utmpxname(_PATH_UTMPX);
setutxent();
if ((ut = getutxline(&l_struct))) {
# else
utmpname(_PATH_UTMP);
setutent();
if ((ut = getutline(&l_struct))) {
# endif
memset(ut->ut_name, 0, sizeof(*ut->ut_name));
memset(ut->ut_host, 0, sizeof(*ut->ut_host));
# if HAVE_STRUCT_UTMP_UT_SYSLEN
ut->ut_syslen = 0;
# endif
# if HAVE_STRUCT_UTMP_UT_TYPE
ut->ut_type = DEAD_PROCESS;
# endif
# if HAVE_UTMPX
gettimeofday(&(ut->ut_tv), 0);
pututxline(ut);
}
endutxent();
# else
ut->ut_time = time(0);
pututline(ut);
}
endutent();
# endif
# endif
#endif
}
bool KPty::tcGetAttr(struct ::termios *ttmode) const
{
Q_D(const KPty);
#ifdef Q_OS_SOLARIS
if (_tcgetattr(d->slaveFd, ttmode) == 0) return true;
#endif
return _tcgetattr(d->masterFd, ttmode) == 0;
}
bool KPty::tcSetAttr(struct ::termios *ttmode)
{
Q_D(KPty);
#ifdef Q_OS_SOLARIS
if (_tcsetattr(d->slaveFd, ttmode) == 0) return true;
#endif
return _tcsetattr(d->masterFd, ttmode) == 0;
}
bool KPty::setWinSize(int lines, int columns)
{
Q_D(KPty);
struct winsize winSize;
memset(&winSize, 0, sizeof(winSize));
winSize.ws_row = (unsigned short)lines;
winSize.ws_col = (unsigned short)columns;
return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0;
}
bool KPty::setEcho(bool echo)
{
struct ::termios ttmode;
if (!tcGetAttr(&ttmode))
return false;
if (!echo)
ttmode.c_lflag &= ~ECHO;
else
ttmode.c_lflag |= ECHO;
return tcSetAttr(&ttmode);
}
const char *KPty::ttyName() const
{
Q_D(const KPty);
return d->ttyName.data();
}
int KPty::masterFd() const
{
Q_D(const KPty);
return d->masterFd;
}
int KPty::slaveFd() const
{
Q_D(const KPty);
return d->slaveFd;
}
diff --git a/kutils/kcmultidialog.cpp b/kutils/kcmultidialog.cpp
index 7623333b3d..25e50317c0 100644
--- a/kutils/kcmultidialog.cpp
+++ b/kutils/kcmultidialog.cpp
@@ -1,507 +1,507 @@
/*
Copyright (c) 2000 Matthias Elter <elter@kde.org>
Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
Copyright (c) 2003,2006 Matthias Kretz <kretz@kde.org>
Copyright (c) 2004 Frans Englich <frans.englich@telia.com>
Copyright (c) 2006 Tobias Koenig <tokoe@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.
*/
#include "kcmultidialog.h"
#include "kcmultidialog_p.h"
#include <QtCore/QStringList>
#include <QtCore/QProcess>
#include <QtCore/QUrl>
#include <QDesktopServices>
#include <kcoreauthorized.h>
#include <kguiitem.h>
#include <klocalizedstring.h>
#include <kpagewidgetmodel.h>
#include <kpushbutton.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include "kauthaction.h"
#include "kauthobjectdecorator.h"
#include "kcolorscheme.h"
#include "kcmoduleproxy.h"
bool KCMultiDialogPrivate::resolveChanges(KCModuleProxy *currentProxy)
{
Q_Q(KCMultiDialog);
if( !currentProxy || !currentProxy->changed() ) {
return true;
}
// Let the user decide
const int queryUser = KMessageBox::warningYesNoCancel(
q,
i18n("The settings of the current module have changed.\n"
"Do you want to apply the changes or discard them?"),
i18n("Apply Settings"),
KStandardGuiItem::apply(),
KStandardGuiItem::discard(),
KStandardGuiItem::cancel());
switch (queryUser) {
case KMessageBox::Yes:
return moduleSave(currentProxy);
case KMessageBox::No:
currentProxy->load();
return true;
case KMessageBox::Cancel:
return false;
default:
Q_ASSERT(false);
return false;
}
}
void KCMultiDialogPrivate::_k_slotCurrentPageChanged( KPageWidgetItem *current, KPageWidgetItem *previous )
{
Q_Q(KCMultiDialog);
kDebug(710);
q->blockSignals(true);
q->setCurrentPage(previous);
KCModuleProxy *previousModule = 0;
for ( int i = 0; i < modules.count(); ++i ) {
if ( modules[ i ].item == previous ) {
previousModule = modules[ i ].kcm;
break;
}
}
if( resolveChanges(previousModule) ) {
q->setCurrentPage(current);
}
q->blockSignals(false);
// We need to get the state of the now active module
_k_clientChanged();
}
void KCMultiDialogPrivate::_k_clientChanged()
{
Q_Q(KCMultiDialog);
kDebug(710);
// Get the current module
KCModuleProxy *activeModule = 0;
for ( int i = 0; i < modules.count(); ++i ) {
if ( modules[ i ].item == q->currentPage() ) {
activeModule = modules[ i ].kcm;
break;
}
}
bool change = false;
if (activeModule) {
change = activeModule->changed();
if (q->button(KDialog::Apply)) {
q->disconnect(q, SIGNAL(applyClicked()), q, SLOT(slotApplyClicked()));
q->disconnect(q->button(KDialog::Apply), SIGNAL(authorized(KAuth::Action)), q, SLOT(slotApplyClicked()));
q->button(KDialog::Apply)->setEnabled(change);
}
if (q->button(KDialog::Ok)) {
q->disconnect(q, SIGNAL(okClicked()), q, SLOT(slotOkClicked()));
q->disconnect(q->button(KDialog::Ok), SIGNAL(authorized(KAuth::Action)), q, SLOT(slotOkClicked()));
}
if (activeModule->realModule()->needsAuthorization()) {
if (q->button(KDialog::Apply)) {
KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(q->button(KDialog::Apply));
decorator->setAuthAction(activeModule->realModule()->authAction());
activeModule->realModule()->authAction().setParentWidget(activeModule->realModule());
q->connect(decorator, SIGNAL(authorized(KAuth::Action)), SLOT(slotApplyClicked()));
}
if (q->button(KDialog::Ok)) {
KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(q->button(KDialog::Ok));
decorator->setAuthAction(activeModule->realModule()->authAction());
activeModule->realModule()->authAction().setParentWidget(activeModule->realModule());
q->connect(decorator, SIGNAL(authorized(KAuth::Action)), SLOT(slotOkClicked()));
}
} else {
if (q->button(KDialog::Apply)) {
q->connect(q, SIGNAL(applyClicked()), SLOT(slotApplyClicked()));
q->button(KDialog::Apply)->findChild<KAuth::ObjectDecorator*>()->deleteLater();
}
if (q->button(KDialog::Ok)) {
q->connect(q, SIGNAL(okClicked()), SLOT(slotOkClicked()));
q->button(KDialog::Ok)->findChild<KAuth::ObjectDecorator*>()->deleteLater();
}
}
}
if (q->button(KDialog::Reset)) {
q->button(KDialog::Reset)->setEnabled(change);
}
if (q->button(KDialog::Apply)) {
q->button(KDialog::Apply)->setEnabled(change);
}
if (activeModule) {
q->enableButton(KDialog::Help, activeModule->buttons() & KCModule::Help);
q->enableButton(KDialog::Default, activeModule->buttons() & KCModule::Default);
}
}
void KCMultiDialogPrivate::_k_updateHeader(bool use, const QString &message)
{
Q_Q(KCMultiDialog);
KPageWidgetItem *item = q->currentPage();
KCModuleProxy *kcm = qobject_cast<KCModuleProxy*>(item->widget());
if (use) {
item->setHeader( "<b>"+kcm->moduleInfo().comment() + "</b><br><i>" +
message + "</i>" );
item->setIcon( KDE::icon( kcm->moduleInfo().icon(), QStringList() << "dialog-warning" ) );
} else {
item->setHeader( kcm->moduleInfo().comment() );
item->setIcon( KDE::icon( kcm->moduleInfo().icon() ) );
}
}
void KCMultiDialogPrivate::_k_dialogClosed()
{
kDebug(710) ;
/**
* If we don't delete them, the DBUS registration stays, and trying to load the KCMs
* in other situations will lead to "module already loaded in Foo," while to the user
* doesn't appear so(the dialog is hidden)
*/
for ( int i = 0; i < modules.count(); ++i )
modules[ i ].kcm->deleteClient();
}
void KCMultiDialogPrivate::init()
{
Q_Q(KCMultiDialog);
q->setFaceType(KPageDialog::Auto);
q->setCaption(i18n("Configure"));
q->setButtons(KDialog::Help | KDialog::Default |KDialog::Cancel | KDialog::Apply | KDialog::Ok | KDialog::Reset);
q->setModal(false);
q->connect(q, SIGNAL(finished()), SLOT(_k_dialogClosed()));
q->connect(q, SIGNAL(applyClicked()), SLOT(slotApplyClicked()));
q->connect(q, SIGNAL(okClicked()), SLOT(slotOkClicked()));
q->connect(q, SIGNAL(defaultClicked()), SLOT(slotDefaultClicked()));
q->connect(q, SIGNAL(helpClicked()), SLOT(slotHelpClicked()));
q->connect(q, SIGNAL(user1Clicked()), SLOT(slotUser1Clicked()));
q->connect(q, SIGNAL(resetClicked()), SLOT(slotUser1Clicked()));
q->connect(q, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)),
SLOT(_k_slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*)));
q->setInitialSize(QSize(800, 550));
}
KCMultiDialog::KCMultiDialog( QWidget *parent )
: KPageDialog(*new KCMultiDialogPrivate, NULL, parent)
{
d_func()->init();
}
-KCMultiDialog::KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WFlags flags)
+KCMultiDialog::KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
: KPageDialog(*new KCMultiDialogPrivate, pageWidget, parent, flags)
{
d_func()->init();
}
-KCMultiDialog::KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WFlags flags)
+KCMultiDialog::KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
: KPageDialog(dd, pageWidget, parent, flags)
{
d_func()->init();
}
KCMultiDialog::~KCMultiDialog()
{
}
void KCMultiDialog::slotDefaultClicked()
{
Q_D(KCMultiDialog);
const KPageWidgetItem *item = currentPage();
if ( !item )
return;
for ( int i = 0; i < d->modules.count(); ++i ) {
if ( d->modules[ i ].item == item ) {
d->modules[ i ].kcm->defaults();
d->_k_clientChanged();
return;
}
}
}
void KCMultiDialog::slotUser1Clicked()
{
const KPageWidgetItem *item = currentPage();
if ( !item )
return;
Q_D(KCMultiDialog);
for ( int i = 0; i < d->modules.count(); ++i ) {
if ( d->modules[ i ].item == item ) {
d->modules[ i ].kcm->load();
d->_k_clientChanged();
return;
}
}
}
bool KCMultiDialogPrivate::moduleSave(KCModuleProxy *module)
{
if( !module ) {
return false;
}
module->save();
return true;
}
void KCMultiDialogPrivate::apply()
{
Q_Q(KCMultiDialog);
QStringList updatedComponents;
foreach (const CreatedModule &module, modules) {
KCModuleProxy *proxy = module.kcm;
if (proxy->changed()) {
proxy->save();
/**
* Add name of the components the kcm belongs to the list
* of updated components.
*/
const QStringList componentNames = module.componentNames;
foreach (const QString &componentName, module.componentNames) {
if (!updatedComponents.contains(componentName)) {
updatedComponents.append(componentName);
}
}
}
}
// Send the configCommitted signal for every updated component.
foreach (const QString &name, updatedComponents) {
emit q->configCommitted(name.toLatin1());
}
emit q->configCommitted();
}
void KCMultiDialog::slotApplyClicked()
{
setButtonFocus( Apply );
d_func()->apply();
}
void KCMultiDialog::slotOkClicked()
{
setButtonFocus( Ok );
d_func()->apply();
accept();
}
void KCMultiDialog::slotHelpClicked()
{
const KPageWidgetItem *item = currentPage();
if ( !item )
return;
Q_D(KCMultiDialog);
QString docPath;
for ( int i = 0; i < d->modules.count(); ++i ) {
if ( d->modules[ i ].item == item ) {
docPath = d->modules[ i ].kcm->moduleInfo().docPath();
break;
}
}
QUrl docUrl = QUrl("help:/").resolved(QUrl::fromUserInput(docPath)); // same code as in KHelpClient::invokeHelp
if ( docUrl.scheme() == "help" || docUrl.scheme() == "man" || docUrl.scheme() == "info" ) {
QProcess::startDetached("khelpcenter", QStringList() << docUrl.toString());
} else {
QDesktopServices::openUrl(docUrl);
}
}
KPageWidgetItem* KCMultiDialog::addModule( const QString& path, const QStringList& args )
{
QString complete = path;
if ( !path.endsWith( QLatin1String(".desktop") ) )
complete += ".desktop";
KService::Ptr service = KService::serviceByStorageId( complete );
return addModule( KCModuleInfo( service ), 0, args );
}
KPageWidgetItem* KCMultiDialog::addModule( const KCModuleInfo& moduleInfo,
KPageWidgetItem *parentItem, const QStringList& args )
{
if ( !moduleInfo.service() )
return 0;
//KAuthorized::authorizeControlModule( moduleInfo.service()->menuId() ) is
//checked in noDisplay already
if ( moduleInfo.service()->noDisplay() )
return 0;
KCModuleProxy *kcm = new KCModuleProxy(moduleInfo, 0, args);
kDebug(710) << moduleInfo.moduleName();
KPageWidgetItem *item = new KPageWidgetItem(kcm, moduleInfo.moduleName());
if (kcm->useRootOnlyMessage()) {
item->setHeader( "<b>"+moduleInfo.comment() + "</b><br><i>" + kcm->rootOnlyMessage() + "</i>" );
item->setIcon( KDE::icon( moduleInfo.icon(), QStringList() << "dialog-warning" ) );
} else {
item->setHeader( moduleInfo.comment() );
item->setIcon( KDE::icon( moduleInfo.icon() ) );
}
item->setProperty("_k_weight", moduleInfo.weight());
bool updateCurrentPage = false;
const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(pageWidget()->model());
Q_ASSERT(model);
if (parentItem) {
const QModelIndex parentIndex = model->index(parentItem);
const int siblingCount = model->rowCount(parentIndex);
int row = 0;
for (; row < siblingCount; ++row) {
KPageWidgetItem *siblingItem = model->item(parentIndex.child(row, 0));
if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
// the item we found is heavier than the new module
kDebug(710) << "adding KCM " << item->name() << " before " << siblingItem->name();
insertPage(siblingItem, item);
break;
}
}
if (row >= siblingCount) {
// the new module is either the first or the heaviest item
kDebug(710) << "adding KCM " << item->name() << " with parent " << parentItem->name();
addSubPage(parentItem, item);
}
} else {
const int siblingCount = model->rowCount();
int row = 0;
for (; row < siblingCount; ++row) {
KPageWidgetItem *siblingItem = model->item(model->index(row, 0));
if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
// the item we found is heavier than the new module
kDebug(710) << "adding KCM " << item->name() << " before " << siblingItem->name();
insertPage(siblingItem, item);
if ( siblingItem == currentPage() )
updateCurrentPage = true;
break;
}
}
if (row == siblingCount) {
// the new module is either the first or the heaviest item
kDebug(710) << "adding KCM " << item->name() << " at the top level";
addPage(item);
}
}
connect(kcm, SIGNAL(changed(bool)), this, SLOT(_k_clientChanged()));
connect(kcm->realModule(), SIGNAL(rootOnlyMessageChanged(bool,QString)), this, SLOT(_k_updateHeader(bool,QString)));
Q_D(KCMultiDialog);
KCMultiDialogPrivate::CreatedModule cm;
cm.kcm = kcm;
cm.item = item;
cm.componentNames = moduleInfo.service()->property( "X-KDE-ParentComponents" ).toStringList();
d->modules.append( cm );
if ( d->modules.count() == 1 || updateCurrentPage )
{
setCurrentPage( item );
d->_k_clientChanged();
}
return item;
}
void KCMultiDialog::clear()
{
Q_D(KCMultiDialog);
kDebug( 710 ) ;
for ( int i = 0; i < d->modules.count(); ++i ) {
removePage( d->modules[ i ].item );
delete d->modules[ i ].kcm;
}
d->modules.clear();
d->_k_clientChanged();
}
void KCMultiDialog::setButtons(ButtonCodes buttonMask)
{
KPageDialog::setButtons(buttonMask);
// Set Auto-Default mode ( KDE Bug #211187 )
if (buttonMask & KDialog::Ok) {
button(KDialog::Ok)->setAutoDefault(true);
}
if (buttonMask & KDialog::Apply) {
button(KDialog::Apply)->setAutoDefault(true);
}
if (buttonMask & KDialog::Default) {
button(KDialog::Default)->setAutoDefault(true);
}
if (buttonMask & KDialog::Reset) {
button(KDialog::Reset)->setAutoDefault(true);
}
if (buttonMask & KDialog::Cancel) {
button(KDialog::Cancel)->setAutoDefault(true);
}
if (buttonMask & KDialog::Help) {
button(KDialog::Help)->setAutoDefault(true);
}
// Old Reset Button
enableButton(KDialog::User1, false);
enableButton(KDialog::Reset, false);
enableButton(KDialog::Apply, false);
}
#include "moc_kcmultidialog.cpp"
diff --git a/kutils/kcmultidialog.h b/kutils/kcmultidialog.h
index 1b3ff15876..bd6a7e4310 100644
--- a/kutils/kcmultidialog.h
+++ b/kutils/kcmultidialog.h
@@ -1,185 +1,185 @@
/*
Copyright (c) 2000 Matthias Elter <elter@kde.org>
Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
Copyright (c) 2003,2006 Matthias Kretz <kretz@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 KCMULTIDIALOG_H
#define KCMULTIDIALOG_H
#include <kcmoduleinfo.h>
#include <kpagedialog.h>
class KCMultiDialogPrivate;
/**
* @short A class that offers a KPageDialog containing arbitrary
* KControl Modules.
*
* @author Matthias Elter <elter@kde.org>, Daniel Molkentin <molkentin@kde.org>
*/
class KCMUTILS_EXPORT KCMultiDialog : public KPageDialog
{
Q_OBJECT
Q_DECLARE_PRIVATE(KCMultiDialog)
public:
/**
* Constructs a new KCMultiDialog
*
* @param parent The parent widget
**/
KCMultiDialog( QWidget *parent = 0 );
/**
* Destructor
**/
virtual ~KCMultiDialog();
/**
* Add a module.
*
* The module is added according to its KCModuleInfo::weight(). The weight determines where in the list
* the module will appear. Lighter modules on top, heavier modules at the bottom.
*
* @param module Specify the name of the module that is to be added
* to the list of modules the dialog will show.
*
* @param args The arguments that should be given to the KCModule when it is created
*
* @returns The @see KPageWidgetItem associated with the new dialog page.
**/
KPageWidgetItem* addModule( const QString& module, const QStringList&
args = QStringList() );
/**
* Add a module.
*
* The module is added according to its KCModuleInfo::weight(). The weight determines where in the list
* the module will appear. Lighter modules on top, heavier modules at the bottom.
*
* @param moduleinfo Pass a KCModuleInfo object which will be
* used for creating the module. It will be added
* to the list of modules the dialog will show.
*
* @param parent The @see KPageWidgetItem that should appear as parents
* in the tree view or a 0 pointer if there is no parent.
*
* @param args The arguments that should be given to the KCModule when it is created
**/
KPageWidgetItem* addModule( const KCModuleInfo& moduleinfo, KPageWidgetItem *parent = 0,
const QStringList& args = QStringList() );
/**
* Removes all modules from the dialog.
*/
void clear();
/**
* Reimplemented for internal reasons.
*/
void setButtons(ButtonCodes buttonMask);
Q_SIGNALS:
/**
* Emitted after all KCModules have been told to save their configuration.
*
* The applyClicked and okClicked signals are emitted before the
* configuration is saved.
*/
void configCommitted();
/**
* Emitted after the KCModules have been told to save their configuration.
* It is emitted once for every instance the KCMs that were changed belong
* to.
*
* You can make use of this if you have more than one component in your
* application. componentName tells you the instance that has to reload its
* configuration.
*
* The applyClicked and okClicked signals are emitted before the
* configuration is saved.
*
* @param componentName The name of the instance that needs to reload its
* configuration.
*/
void configCommitted( const QByteArray & componentName );
protected:
/**
* This constructor can be used by subclasses to provide a custom KPageWidget.
*/
- KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WFlags flags = 0);
- KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WFlags flags = 0);
+ KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags = 0);
+ KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags = 0);
protected Q_SLOTS:
/**
* This slot is called when the user presses the "Default" Button.
* You can reimplement it if needed.
*
* @note Make sure you call the original implementation.
**/
void slotDefaultClicked();
/**
* This slot is called when the user presses the "Reset" Button.
* You can reimplement it if needed.
*
* @note Make sure you call the original implementation.
*/
void slotUser1Clicked();
/**
* This slot is called when the user presses the "Apply" Button.
* You can reimplement it if needed.
*
* @note Make sure you call the original implementation.
**/
void slotApplyClicked();
/**
* This slot is called when the user presses the "OK" Button.
* You can reimplement it if needed.
*
* @note Make sure you call the original implementation.
**/
void slotOkClicked();
/**
* This slot is called when the user presses the "Help" Button.
* It reads the X-DocPath field of the currently selected KControl
* module's .desktop file to find the path to the documentation,
* which it then attempts to load.
*
* You can reimplement this slot if needed.
*
* @note Make sure you call the original implementation.
**/
void slotHelpClicked();
private:
Q_PRIVATE_SLOT(d_func(), void _k_slotCurrentPageChanged(KPageWidgetItem *, KPageWidgetItem *))
Q_PRIVATE_SLOT(d_func(), void _k_clientChanged())
Q_PRIVATE_SLOT(d_func(), void _k_dialogClosed())
Q_PRIVATE_SLOT(d_func(), void _k_updateHeader(bool use, const QString &message))
};
#endif
diff --git a/plasma/private/remoteservice.cpp b/plasma/private/remoteservice.cpp
index 210f3d574a..e9944f6199 100644
--- a/plasma/private/remoteservice.cpp
+++ b/plasma/private/remoteservice.cpp
@@ -1,317 +1,317 @@
/*
* Copyright © 2009 Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "remoteservice_p.h"
#include "../remote/accessmanager.h"
#include "../remote/authorizationinterface.h"
#include "../remote/authorizationmanager.h"
#include "../remote/clientpinrequest.h"
#include "../service.h"
#include "authorizationmanager_p.h"
#include "joliemessagehelper_p.h"
#include "remoteservicejob_p.h"
#include "service_p.h"
#include <QtCore/QBuffer>
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QUuid>
#include <QtJolie/Client>
#include <QtJolie/Message>
#include <QtJolie/PendingReply>
#include <QtJolie/PendingCall>
#include <QtJolie/PendingCallWatcher>
#include <QtJolie/Value>
#include <kdebug.h>
#include <qurl.h>
#include <qurlpathinfo.h>
namespace Plasma
{
RemoteService::RemoteService(QObject* parent)
: Service(parent),
m_client(0),
m_ready(false),
m_busy(false)
{
}
RemoteService::RemoteService(QObject* parent, const QUrl &location)
: Service(parent),
m_location(location),
m_client(0),
m_ready(false),
m_busy(false)
{
if (AuthorizationManager::self()->d->myCredentials.isValid()) {
setLocation(location);
} else {
connect(AuthorizationManager::self(), SIGNAL(readyForRemoteAccess()),
this, SLOT(slotReadyForRemoteAccess()));
}
}
RemoteService::~RemoteService()
{
delete m_client;
}
void RemoteService::slotReadyForRemoteAccess()
{
#ifndef NDEBUG
kDebug() << "AuthorizationManager is now ready for remote access!";
#endif
setLocation(m_location);
}
static QByteArray resourcePathFromUrl(const QUrl& location)
{
QUrlPathInfo pi(location);
return pi.path(QUrlPathInfo::StripTrailingSlash).remove(0, 1).toUtf8();
}
void RemoteService::setLocation(const QUrl &location)
{
#ifndef NDEBUG
kDebug() << "Setting RemoteService location to " << location;
#endif
m_uuid = QUuid::createUuid().toString();
Credentials identity = AuthorizationManager::self()->d->myCredentials;
if (!identity.canSign()) {
#ifndef NDEBUG
kDebug() << "we can't sign? how did we get here?";
#endif
return;
}
if (m_client && (m_location != location)) {
delete m_client;
m_client = new Jolie::Client(location.host(), location.port());
}
if (!m_client) {
m_client = new Jolie::Client(location.host(), location.port());
}
m_location = location;
QByteArray identityByteArray;
QDataStream stream(&identityByteArray, QIODevice::WriteOnly);
stream << identity.toPublicCredentials();
Jolie::Message getOpDesc(resourcePathFromUrl(location), "startConnection");
Jolie::Value data;
data.children(JolieMessage::Field::IDENTITY) << Jolie::Value(identityByteArray);
- data.children(JolieMessage::Field::UUID) << Jolie::Value(m_uuid.toAscii());
+ data.children(JolieMessage::Field::UUID) << Jolie::Value(m_uuid.toLatin1());
getOpDesc.setData(data);
Jolie::PendingCall pendingReply = m_client->asyncCall(getOpDesc);
Jolie::PendingCallWatcher *watcher = new Jolie::PendingCallWatcher(pendingReply, this);
connect(watcher, SIGNAL(finished(Jolie::PendingCallWatcher*)),
this, SLOT(callCompleted(Jolie::PendingCallWatcher*)));
}
QString RemoteService::location() const
{
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
return m_location.toString();
#else
return m_location.toDisplayString();
#endif
}
bool RemoteService::isReady() const
{
return m_ready;
}
bool RemoteService::isBusy() const
{
return m_busy;
}
void RemoteService::callCompleted(Jolie::PendingCallWatcher *watcher)
{
Jolie::PendingReply reply = *watcher;
Jolie::Message response = reply.reply();
if (response.operationName() == "startConnection") {
#ifndef NDEBUG
kDebug() << "Started connection: fetching .operations";
#endif
m_token = JolieMessage::field(JolieMessage::Field::TOKEN, response);
Jolie::Message getOpDesc(resourcePathFromUrl(m_location), "getOperations");
//TODO: async
Jolie::PendingCall pendingReply = m_client->asyncCall(signMessage(getOpDesc));
Jolie::PendingCallWatcher *watcher = new Jolie::PendingCallWatcher(pendingReply, this);
connect(watcher, SIGNAL(finished(Jolie::PendingCallWatcher*)),
this, SLOT(callCompleted(Jolie::PendingCallWatcher*)));
} else if (response.operationName() == "getOperations") {
if (response.fault().name() == JolieMessage::Error::REQUIREPIN) {
#ifndef NDEBUG
kDebug() << "pin required, request auth interface";
#endif
ClientPinRequest *request = new ClientPinRequest(this);
connect(request, SIGNAL(changed(Plasma::ClientPinRequest*)),
this, SLOT(slotGotPin(Plasma::ClientPinRequest*)));
AuthorizationManager::self()->d->authorizationInterface->clientPinRequest(*request);
} else {
#ifndef NDEBUG
kDebug() << "RemoteService is now ready for use!";
#endif
m_operationsScheme = JolieMessage::field(JolieMessage::Field::OPERATIONSDESCRIPTION, response);
m_token = JolieMessage::field(JolieMessage::Field::TOKEN, response);
m_ready = true;
setName(location());
//if there's stuff in the queue, let it continue.
slotFinished();
}
} else if (response.operationName() == "getEnabledOperations") {
//TODO: optimize.
m_token = JolieMessage::field(JolieMessage::Field::TOKEN, response);
QByteArray enabledOperations = JolieMessage::field(JolieMessage::Field::ENABLEDOPERATIONS, response);
QDataStream in(&enabledOperations, QIODevice::ReadOnly);
QStringList enabledOperationsList;
in >> enabledOperationsList;
foreach (const QString &operation, operationNames()) {
if (enabledOperationsList.contains(operation) && !isOperationEnabled(operation)) {
#ifndef NDEBUG
kDebug() << "yeah, we're enabling the operation with the name " << operation;
#endif
setOperationEnabled(operation, true);
} else if (!enabledOperationsList.contains(operation) && isOperationEnabled(operation)) {
#ifndef NDEBUG
kDebug() << "we're disabling the operation with the name " << operation;
#endif
setOperationEnabled(operation, false);
}
}
//if there's stuff in the queue, let it continue.
m_busy = false;
slotFinished();
} else {
#ifndef NDEBUG
kDebug() << "How did we end up here?";
#endif
}
}
void RemoteService::slotGotPin(Plasma::ClientPinRequest *request)
{
Jolie::Message getOpDesc(resourcePathFromUrl(m_location), "getOperations");
Jolie::Value value;
value.children(JolieMessage::Field::PARAMETERS) << Jolie::Value(QByteArray());
if (!request->pin().isEmpty()) {
- value.children(JolieMessage::Field::PIN) << Jolie::Value(request->pin().toAscii());
+ value.children(JolieMessage::Field::PIN) << Jolie::Value(request->pin().toLatin1());
}
getOpDesc.setData(value);
//TODO: async
Jolie::PendingCall pendingReply = m_client->asyncCall(signMessage(getOpDesc));
Jolie::PendingCallWatcher *watcher = new Jolie::PendingCallWatcher(pendingReply, this);
connect(watcher, SIGNAL(finished(Jolie::PendingCallWatcher*)),
this, SLOT(callCompleted(Jolie::PendingCallWatcher*)));
}
void RemoteService::registerOperationsScheme()
{
QBuffer buffer(&m_operationsScheme);
buffer.open(QBuffer::ReadWrite);
setOperationsScheme(&buffer);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(slotUpdateEnabledOperations()));
//FIXME: get some sensible interval depending on the connection speed. This is kind of stupid.
timer->start(2000);
}
void RemoteService::slotUpdateEnabledOperations()
{
//TODO: maybe push the get enabled operations also on the queue?
if (!m_busy) {
m_busy = true;
Jolie::Message getOpDesc(resourcePathFromUrl(m_location), "getEnabledOperations");
Jolie::PendingCall pendingReply = m_client->asyncCall(signMessage(getOpDesc));
Jolie::PendingCallWatcher *watcher = new Jolie::PendingCallWatcher(pendingReply, this);
connect(watcher, SIGNAL(finished(Jolie::PendingCallWatcher*)),
this, SLOT(callCompleted(Jolie::PendingCallWatcher*)));
} else {
#ifndef NDEBUG
kDebug() << "We would like to update enabled operations, but are still busy so let's wait for now.";
#endif
}
}
ServiceJob* RemoteService::createJob(const QString& operation,
QHash<QString,QVariant>& parameters)
{
if (!m_ready) {
#ifndef NDEBUG
kDebug() << "Use of this service hasn't checked for the serviceReady signal, which it should.";
#endif
}
ServiceJob *job = new RemoteServiceJob(m_location, destination(), operation, parameters, m_token, this);
connect(job, SIGNAL(finished(KJob*)), this, SLOT(slotFinished()));
return job;
}
void RemoteService::slotFinished()
{
if (!m_queue.isEmpty()) {
#ifndef NDEBUG
kDebug() << "Job finished, there are still service jobs in queue, starting next in queue.";
#endif
ServiceJob *job = m_queue.dequeue();
QTimer::singleShot(0, job, SLOT(slotStart()));
}
}
Jolie::Message RemoteService::signMessage(const Jolie::Message &message) const
{
Jolie::Message response(message);
Credentials identity = AuthorizationManager::self()->d->myCredentials;
if (!identity.isValid()) {
#ifndef NDEBUG
kDebug() << "We don't have our identity yet, just drop this message";
#endif
return response;
}
Jolie::Value data = response.data();
- data.children(JolieMessage::Field::IDENTITYID) << Jolie::Value(identity.id().toAscii());
+ data.children(JolieMessage::Field::IDENTITYID) << Jolie::Value(identity.id().toLatin1());
data.children(JolieMessage::Field::TOKEN) << Jolie::Value(m_token);
- data.children(JolieMessage::Field::UUID) << Jolie::Value(m_uuid.toAscii());
+ data.children(JolieMessage::Field::UUID) << Jolie::Value(m_uuid.toLatin1());
response.setData(data);
data.children(JolieMessage::Field::SIGNATURE) <<
Jolie::Value(identity.signMessage(JolieMessage::payload(response)));
response.setData(data);
return response;
}
} //namespace Plasma
#include "moc_remoteservice_p.cpp"
diff --git a/plasma/private/remoteservicejob.cpp b/plasma/private/remoteservicejob.cpp
index dfd314b1b4..11a229fde1 100644
--- a/plasma/private/remoteservicejob.cpp
+++ b/plasma/private/remoteservicejob.cpp
@@ -1,183 +1,183 @@
/*
* Copyright © 2009 Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "remoteservicejob_p.h"
#include <kconfiggroup.h>
#include <kurl.h>
#include <QtCore/QBuffer>
#include <QtJolie/Client>
#include <QtJolie/Message>
#include <QtJolie/PendingCallWatcher>
#include <QtJolie/PendingReply>
#include "../servicejob.h"
#include "../remote/accessmanager.h"
#include "../remote/authorizationmanager.h"
#include "authorizationmanager_p.h"
#include "remoteservice_p.h"
#include "servicejob_p.h"
#include "joliemessagehelper_p.h"
#include <qtimer.h>
namespace Plasma
{
RemoteServiceJob::RemoteServiceJob(KUrl location,
const QString& destination,
const QString& operation,
QHash<QString,QVariant>& parameters,
QByteArray initialToken,
RemoteService* parent)
: ServiceJob(destination, operation, parameters, parent),
m_token(initialToken),
m_location(location),
m_service(parent),
m_delayedDesc(0)
{
}
RemoteServiceJob::~RemoteServiceJob()
{
delete m_delayedDesc;
m_delayedDesc = 0;
}
void RemoteServiceJob::start()
{
QTimer::singleShot(30000, this, SLOT(timeout()));
if (m_service->m_busy || !m_service->m_ready) {
//enqueue and wait
m_service->m_queue.enqueue(this);
#ifndef NDEBUG
kDebug() << "already busy... enqueue, queue contains " << m_service->m_queue.count();
#endif
return;
}
// the service is now busy ... with us!
m_service->m_busy = true;
// while waiting in the queue, our validity may have changed; it's all async
// so don't assume anything
checkValidity();
if (error()) {
emitResult();
return;
}
//serialize the parameters
QByteArray params;
QBuffer buffer(&params);
buffer.open(QIODevice::WriteOnly);
QDataStream out(&buffer);
out << parameters();
Jolie::Message message(m_location.path(KUrl::RemoveTrailingSlash).remove(0, 1).toUtf8(),
"startOperationCall");
Jolie::Value data;
- data.children(JolieMessage::Field::OPERATION) << (Jolie::Value(operationName().toAscii()));
+ data.children(JolieMessage::Field::OPERATION) << (Jolie::Value(operationName().toLatin1()));
data.children(JolieMessage::Field::PARAMETERS) << Jolie::Value(params);
- data.children(JolieMessage::Field::DESTINATION) << Jolie::Value(destination().toAscii());
+ data.children(JolieMessage::Field::DESTINATION) << Jolie::Value(destination().toLatin1());
message.setData(data);
Jolie::Client *client = m_service->m_client;
Jolie::PendingCall pendingReply = client->asyncCall(m_service->signMessage(message));
Jolie::PendingCallWatcher *watcher = new Jolie::PendingCallWatcher(pendingReply, this);
connect(watcher, SIGNAL(finished(Jolie::PendingCallWatcher*)),
this, SLOT(callCompleted(Jolie::PendingCallWatcher*)));
}
void RemoteServiceJob::setDelayedDescription(const KConfigGroup &desc)
{
if (!m_delayedDesc) {
m_delayedDesc = new KConfigGroup(desc);
} else {
*m_delayedDesc = desc;
}
}
void RemoteServiceJob::checkValidity()
{
if (!m_service->isOperationEnabled(operationName())) {
setError(-1);
setErrorText(i18n("Job no longer valid, operation is not enabled."));
} else if (m_delayedDesc) {
d->parameters = m_service->parametersFromDescription(*m_delayedDesc);
} else {
KConfigGroup description = m_service->operationDescription(operationName());
QHashIterator<QString, QVariant> param(parameters());
while (param.hasNext()) {
param.next();
if (!description.hasKey(param.key())) {
setError(-1);
setErrorText(i18n("Job no longer valid, invalid parameters."));
break;
}
}
}
delete m_delayedDesc;
m_delayedDesc = 0;
}
void RemoteServiceJob::callCompleted(Jolie::PendingCallWatcher *watcher)
{
m_service->m_busy = false;
Jolie::PendingReply reply = *watcher;
Jolie::Message response = reply.reply();
//TODO:async
if (response.fault().isValid()) {
#ifndef NDEBUG
kDebug() << "fault: " << response.fault().name();
#endif
setError(-1);
setErrorText(JolieMessage::errorMessage(response.fault().name()));
emitResult();
return;
}
m_service->m_token = JolieMessage::field(JolieMessage::Field::TOKEN, response);
QVariant variantResult;
QByteArray byteArrayResult = JolieMessage::field(JolieMessage::Field::RESULT, response);
QBuffer buffer(&byteArrayResult);
buffer.open(QIODevice::ReadOnly);
QDataStream in(&buffer);
in >> variantResult;
setResult(variantResult);
}
void RemoteServiceJob::timeout()
{
m_service->m_busy = false;
#ifndef NDEBUG
kDebug() << "Service job timed out.";
#endif
setError(-1);
setErrorText(i18n("Timeout."));
m_service->m_queue.removeAll(this);
emitResult();
}
} // namespace Plasma
diff --git a/plasma/private/serviceprovider.cpp b/plasma/private/serviceprovider.cpp
index c7fb5cc70d..65b63d4bbc 100644
--- a/plasma/private/serviceprovider.cpp
+++ b/plasma/private/serviceprovider.cpp
@@ -1,534 +1,534 @@
/*
* Copyright © 2009 Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serviceprovider_p.h"
#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <QtCore/QUuid>
#include "authorizationrule_p.h"
#include "authorizationmanager_p.h"
#include "joliemessagehelper_p.h"
#include "config-plasma.h"
#include <plasma/remote/authorizationinterface.h>
#include <plasma/remote/authorizationmanager.h>
#include <plasma/remote/authorizationrule.h>
#include <plasma/remote/credentials.h>
#include <plasma/service.h>
#include <plasma/servicejob.h>
#include <plasma/private/servicejob_p.h>
#include <QtJolie/Server>
#if ENABLE_REMOTE_WIDGETS
#include <QtCrypto>
#endif
#include <kdebug.h>
#include <qstandardpaths.h>
namespace Plasma
{
ServiceProvider::ServiceProvider(const QString &name, Service *service)
: Jolie::AbstractAdaptor(service),
m_service(service)
{
connect(service, SIGNAL(finished(Plasma::ServiceJob*)),
this, SLOT(operationCompleted(Plasma::ServiceJob*)));
m_providerName = name;
AuthorizationManager::self()->d->server->registerAdaptor(m_providerName.toUtf8(), this);
#ifndef NDEBUG
kDebug() << "registered service provider " << m_providerName;
#endif
}
ServiceProvider::~ServiceProvider()
{
AuthorizationManager::self()->d->server->unregisterAdaptor(m_providerName.toUtf8());
}
void ServiceProvider::startOperationCall(Jolie::Message message)
{
#ifndef NDEBUG
kDebug() << "starting operation call";
#endif
KConfigGroup description =
m_service->operationDescription(QString(JolieMessage::field(JolieMessage::Field::OPERATION, message)));
//deserialize the parameters
QByteArray parametersByteArray;
parametersByteArray = JolieMessage::field(JolieMessage::Field::PARAMETERS, message);
#ifndef NDEBUG
kDebug() << "parameters byte array: " << parametersByteArray.toBase64();
#endif
QBuffer buffer(&parametersByteArray);
buffer.open(QIODevice::ReadOnly);
QDataStream in(&buffer);
QMap<QString, QVariant> parameters;
in >> parameters;
if (!description.isValid()) {
#ifndef NDEBUG
kDebug() << "invalid description.";
#endif
}
#ifndef NDEBUG
kDebug() << "====PARAMETERS====";
#endif
//write the parameters into the operation description
QMap<QString, QVariant>::const_iterator it = parameters.constBegin();
QMap<QString, QVariant>::const_iterator itEnd = parameters.constEnd();
for ( ; it != itEnd; ++it) {
const QString key = it.key();
const QVariant value = it.value();
#ifndef NDEBUG
kDebug() << "key = " << key << ", value = " << value;
#endif
description.writeEntry(key, value);
}
m_service->setDestination(JolieMessage::field(JolieMessage::Field::DESTINATION, message));
ServiceJob *job = m_service->startOperationCall(description);
QString identityID = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
job->d->identity = AuthorizationManager::self()->d->getCredentials(identityID);
#ifndef NDEBUG
kDebug() << "adding into messagemap:" << ((QObject*)job);
#endif
m_messageMap[job] = message;
}
void ServiceProvider::sendOperations(Jolie::Message message)
{
#ifndef NDEBUG
kDebug() << "send operations.";
#endif
//kDebug() << printJolieMessage(message);
Jolie::Message response(message.resourcePath(), message.operationName(), message.id());
//FIXME: this is duplicated from Plasma::Service
QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "plasma/services/" + m_service->name() +
".operations");
if (path.isEmpty()) {
#ifndef NDEBUG
kDebug() << "Cannot find operations description:" << m_service->name() << ".operations";
#endif
response.setFault(Jolie::Fault("NoOperationsDescription"));
} else {
#ifndef NDEBUG
kDebug() << "file = " << path;
#endif
QFile file(path);
file.open(QIODevice::ReadOnly);
Jolie::Value value;
value.children(JolieMessage::Field::OPERATIONSDESCRIPTION) << Jolie::Value(file.readAll());
file.close();
response.setData(value);
}
QByteArray id = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
QByteArray uuid = JolieMessage::field(JolieMessage::Field::UUID, message);
response = appendToken(response, id, uuid);
#ifndef NDEBUG
kDebug() << "caller = " << id.toBase64();
#endif
//hack around the not yet async service adaptor api in qtjolie
if (m_descriptorMap.contains(id + uuid)) {
#ifndef NDEBUG
kDebug() << "descriptor found, sending message";
#endif
AuthorizationManager::self()->d->server->sendReply(
m_descriptorMap.value(id + uuid), response);
} else {
#ifndef NDEBUG
kDebug() << "no valid entry in descriptormap.";
#endif
}
}
void ServiceProvider::sendEnabledOperations(Jolie::Message message)
{
#ifndef NDEBUG
kDebug() << "send enabled operations.";
#endif
Jolie::Message response(message.resourcePath(), message.operationName(), message.id());
QStringList enabledOperationsList;
foreach (const QString &operation, m_service->operationNames()) {
if (m_service->isOperationEnabled(operation)) {
enabledOperationsList << operation;
}
}
#ifndef NDEBUG
kDebug() << "enabled operations: " << enabledOperationsList;
#endif
QByteArray enabledOperationsArray;
QDataStream out(&enabledOperationsArray, QIODevice::WriteOnly);
out << enabledOperationsList;
Jolie::Value value;
value.children(JolieMessage::Field::ENABLEDOPERATIONS) << Jolie::Value(enabledOperationsArray);
response.setData(value);
QByteArray id = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
QByteArray uuid = JolieMessage::field(JolieMessage::Field::UUID, message);
response = appendToken(response, id, uuid);
#ifndef NDEBUG
kDebug() << "caller = " << id.toBase64();
#endif
//hack around the not yet async service adaptor api in qtjolie
if (m_descriptorMap.contains(id + uuid)) {
#ifndef NDEBUG
kDebug() << "descriptor found, sending message";
#endif
AuthorizationManager::self()->d->server->sendReply(
m_descriptorMap.value(id + uuid), response);
} else {
#ifndef NDEBUG
kDebug() << "no valid entry in descriptormap.";
#endif
}
}
QString ServiceProvider::resourceName() const
{
return m_providerName;
}
void ServiceProvider::relay(Jolie::Server *server, int descriptor,
const Jolie::Message &message)
{
Q_UNUSED(server)
if (message.operationName() == "startConnection") {
#ifndef NDEBUG
kDebug() << "reset token";
#endif
//add the identity
Credentials identity;
QByteArray identityByteArray = JolieMessage::field(JolieMessage::Field::IDENTITY, message);
QDataStream stream(&identityByteArray, QIODevice::ReadOnly);
stream >> identity;
AuthorizationManager::self()->d->addCredentials(identity);
Jolie::Message response(message.resourcePath(), message.operationName(), message.id());
QByteArray uuid = JolieMessage::field(JolieMessage::Field::UUID, message);
- response = appendToken(response, identity.id().toAscii(), uuid);
+ response = appendToken(response, identity.id().toLatin1(), uuid);
AuthorizationManager::self()->d->server->sendReply(descriptor, response);
return;
}
if (JolieMessage::field(JolieMessage::Field::TOKEN, message).isEmpty()) {
Jolie::Message response(message.resourcePath(), message.operationName(), message.id());
response.setFault(Jolie::Fault(JolieMessage::Error::INVALIDTOKEN));
AuthorizationManager::self()->d->server->sendReply(descriptor, response);
return;
}
//m_descriptor = descriptor;
QByteArray id = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
QByteArray uuid = JolieMessage::field(JolieMessage::Field::UUID, message);
m_descriptorMap[id + uuid] = descriptor;
authorize(message, m_tokens[id + uuid]);
}
void ServiceProvider::operationCompleted(Plasma::ServiceJob *job)
{
#ifndef NDEBUG
kDebug() << "operation completed.";
#endif
if (!m_messageMap.contains(job)) {
#ifndef NDEBUG
kDebug() << "service not in map!";
#endif
return;
}
#ifndef NDEBUG
kDebug() << "found message in message map!";
#endif
Jolie::Message message = m_messageMap.take(job);
Jolie::Message response(message.resourcePath(), message.operationName(), message.id());
QVariant variantResult = job->result();
#ifndef NDEBUG
kDebug() << "got a result: " << variantResult;
#endif
QByteArray byteArrayResult;
QBuffer buffer(&byteArrayResult);
buffer.open(QIODevice::WriteOnly);
QDataStream out(&buffer);
out << variantResult;
Jolie::Value data;
data.children(JolieMessage::Field::RESULT) << Jolie::Value(byteArrayResult);
response.setData(data);
if (job->error()) {
- response.setFault(Jolie::Fault(job->errorString().toAscii()));
+ response.setFault(Jolie::Fault(job->errorString().toLatin1()));
}
QByteArray id = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
QByteArray uuid = JolieMessage::field(JolieMessage::Field::UUID, message);
response = appendToken(response, id, uuid);
//hack around the not yet async service adaptor api in qtjolie
if (m_descriptorMap.contains(id + uuid)) {
#ifndef NDEBUG
kDebug() << "descriptor found, sending message";
#endif
AuthorizationManager::self()->d->server->sendReply(
m_descriptorMap.value(id + uuid), response);
} else {
#ifndef NDEBUG
kDebug() << "no valid entry in descriptormap.";
#endif
}
}
void ServiceProvider::ruleChanged(Plasma::AuthorizationRule *rule)
{
int i = 0;
foreach (const Jolie::Message &message, m_messagesPendingAuthorization) {
QByteArray id = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
//Credentials identity = AuthorizationManager::self()->d->getCredentials(id);
bool matches = rule->d->matches(message.resourcePath(), id);
if (matches && rule->policy() == AuthorizationRule::PinRequired &&
JolieMessage::field(JolieMessage::Field::PIN, message) != rule->pin()) {
#ifndef NDEBUG
kDebug() << "we need a pin";
#endif
authorizationFailed(message, JolieMessage::Error::REQUIREPIN);
m_messagesPendingAuthorization.removeAt(i);
return;
/**
} else if (matches && rule->policy() == AuthorizationRule::PinRequired) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is freely accessable for verified caller.";
#endif
rule->setPolicy(AuthorizationRule::Allow);
authorizationSuccess(message);
//TODO: it might be nicer to do a removeAll once Jolie::Message implements ==
m_messagesPendingAuthorization.removeAt(i);
return;
*/
} else if (matches && rule->policy() == AuthorizationRule::Allow) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is freely accessable for verified caller.";
#endif
authorizationSuccess(message);
//TODO: it might be nicer to do a removeAll once Jolie::Message implements ==
m_messagesPendingAuthorization.removeAt(i);
return;
} else if (matches && rule->policy() == AuthorizationRule::Deny) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is never accessable for verified caller.";
#endif
authorizationFailed(message, JolieMessage::Error::ACCESSDENIED);
m_messagesPendingAuthorization.removeAt(i);
return;
} else {
i++;
}
}
}
Jolie::Message ServiceProvider::appendToken(Jolie::Message message,
const QByteArray &caller,
const QByteArray &uuid)
{
- m_tokens[caller + uuid] = QUuid::createUuid().toString().toAscii();
+ m_tokens[caller + uuid] = QUuid::createUuid().toString().toLatin1();
//kDebug() << "setting token: " << m_tokens[caller + uuid].toBase64()
//<< " for caller: " << caller.toBase64()
//<< " with uuid caller: " << uuid.toBase64();
Jolie::Value data = message.data();
data.children(JolieMessage::Field::TOKEN) << Jolie::Value(m_tokens[caller + uuid]);
message.setData(data);
return message;
}
void ServiceProvider::authorize(const Jolie::Message &message, const QByteArray &validToken)
{
#ifndef NDEBUG
kDebug() << "VALIDATING MESSAGE:";
#endif
//kDebug() << JolieMessage::print(message);
//Authorization step 1: is the service accessable to all callers? In that case we can skip the
//verification of the signature
#ifndef NDEBUG
kDebug() << "STEP1";
#endif
AuthorizationRule *rule =
AuthorizationManager::self()->d->matchingRule(message.resourcePath(), Credentials());
if (rule && rule->policy() == AuthorizationRule::Allow) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is freely accessable.";
#endif
authorizationSuccess(message);
return;
} else if (rule && rule->policy() == AuthorizationRule::Deny) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is never accessable.";
#endif
authorizationFailed(message, JolieMessage::Error::ACCESSDENIED);
return;
}
//Authorization step 2: see if the token matches. If it doesn't we can't safely identify the
//caller and are finished.
#ifndef NDEBUG
kDebug() << "STEP2";
#endif
if (JolieMessage::field(JolieMessage::Field::TOKEN, message) != validToken && !validToken.isEmpty()) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Message token doesn't match.";
#endif
#ifndef NDEBUG
kDebug() << "expected: " << validToken.toBase64();
#endif
authorizationFailed(message, JolieMessage::Error::INVALIDTOKEN);
return;
}
QByteArray payload = JolieMessage::payload(message);
QByteArray signature = JolieMessage::field(JolieMessage::Field::SIGNATURE, message);
Credentials identity = AuthorizationManager::self()->d->getCredentials(
JolieMessage::field(JolieMessage::Field::IDENTITYID, message));
if (!identity.isValid()) {
#ifndef NDEBUG
kDebug() << "no identity";
#endif
authorizationFailed(message, JolieMessage::Error::INVALIDTOKEN);
return;
}
#ifndef NDEBUG
kDebug() << "STEP3";
#endif
//Authorization step 3: see if we have the key and can validate the signature. If we can't,
//either the public key has changed, or somebody is doing something nasty, and we're finished.
if ((!identity.isValidSignature(signature, payload))) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: signature invalid.";
#endif
authorizationFailed(message, JolieMessage::Error::ACCESSDENIED);
return;
}
#ifndef NDEBUG
kDebug() << "STEP4";
#endif
//Authorization step 4: if we have a valid signature, see if we've got a matching rule
rule = AuthorizationManager::self()->d->matchingRule(message.resourcePath(), identity);
if (rule && rule->policy() == AuthorizationRule::PinRequired) {
#ifndef NDEBUG
kDebug() << "we expect a pin!";
#endif
QByteArray pin = JolieMessage::field(JolieMessage::Field::PIN, message);
if (rule->pin() == QString(pin)) {
authorizationSuccess(message);
rule->setPolicy(AuthorizationRule::Allow);
} else {
authorizationFailed(message, JolieMessage::Error::ACCESSDENIED);
AuthorizationManager::self()->d->rules.removeAll(rule);
delete rule;
}
} else if (rule && rule->policy() == AuthorizationRule::Allow) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is freely accessable for validated sender.";
#endif
authorizationSuccess(message);
return;
} else if (rule && rule->policy() == AuthorizationRule::Deny) {
#ifndef NDEBUG
kDebug() << "AUTHORIZATION: Service is not accessable for validated sender.";
#endif
authorizationFailed(message, JolieMessage::Error::ACCESSDENIED);
return;
} else {
//- let the shell set the rule matching this request:
#ifndef NDEBUG
kDebug() << "STEP6";
#endif
#ifndef NDEBUG
kDebug() << "leave it up to the authorization interface";
#endif
m_messagesPendingAuthorization << message;
AuthorizationRule *newRule =
new AuthorizationRule(QString(message.resourcePath()), identity.id());
connect(newRule, SIGNAL(changed(Plasma::AuthorizationRule*)), this,
SLOT(ruleChanged(Plasma::AuthorizationRule*)));
AuthorizationManager::self()->d->rules.append(newRule);
AuthorizationManager::self()->d->authorizationInterface->authorizationRequest(*newRule);
}
}
void ServiceProvider::authorizationSuccess(const Jolie::Message &message)
{
#ifndef NDEBUG
kDebug() << "message with operationName " << message.operationName() << " allowed!";
#endif
//would be lovely if this kind of stuff could be autogenerated code from xml like in dbus
//adaptors
if (message.operationName() == "getOperations") {
sendOperations(message);
} else if (message.operationName() == "getEnabledOperations") {
sendEnabledOperations(message);
} else if (message.operationName() == "startOperationCall") {
startOperationCall(message);
}
}
void ServiceProvider::authorizationFailed(const Jolie::Message &message, const QByteArray &error)
{
#ifndef NDEBUG
kDebug() << "message with operationName " << message.operationName() << " NOT allowed!";
#endif
Jolie::Message response(message.resourcePath(), message.operationName(), message.id());
response.setFault(Jolie::Fault(error));
QByteArray id = JolieMessage::field(JolieMessage::Field::IDENTITYID, message);
QByteArray uuid = JolieMessage::field(JolieMessage::Field::UUID, message);
AuthorizationManager::self()->d->server->sendReply(
m_descriptorMap.value(id + uuid), response);
return;
}
} //namespace Plasma
#include "moc_serviceprovider_p.cpp"
diff --git a/plasma/remote/credentials.cpp b/plasma/remote/credentials.cpp
index 663d1bce5f..50702ef5a7 100644
--- a/plasma/remote/credentials.cpp
+++ b/plasma/remote/credentials.cpp
@@ -1,305 +1,305 @@
/*
* Copyright © 2009 Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "credentials.h"
#include "config-plasma.h"
#include <QCryptographicHash>
#include <QObject>
#if ENABLE_REMOTE_WIDGETS
#include <QtCrypto>
#endif
#include <kdebug.h>
#include <kstandarddirs.h>
#include "authorizationmanager.h"
#define REQUIRED_FEATURES "rsa,sha1,pkey"
namespace Plasma {
class CredentialsPrivate {
public:
CredentialsPrivate()
{
}
CredentialsPrivate(const QString &id, const QString &name,
const QString &pemKey, bool isPrivateKey)
: id(id),
name(name)
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return;
}
if (isPrivateKey) {
privateKey = QCA::PrivateKey::fromPEM(pemKey);
publicKey = privateKey.toPublicKey();
} else {
publicKey = QCA::PublicKey::fromPEM(pemKey);
}
#endif
}
~CredentialsPrivate()
{
}
QString id;
QString name;
#if ENABLE_REMOTE_WIDGETS
QCA::PublicKey publicKey;
QCA::PrivateKey privateKey;
#endif
};
Credentials::Credentials(const QString &id, const QString &name,
const QString &key, bool isPrivateKey)
: d(new CredentialsPrivate(id, name, key, isPrivateKey))
{
}
Credentials::Credentials()
: d(new CredentialsPrivate())
{
}
Credentials::Credentials(const Credentials &other)
: d(new CredentialsPrivate())
{
*d = *other.d;
}
Credentials::~Credentials()
{
delete d;
}
Credentials &Credentials::operator=(const Credentials &other)
{
*d = *other.d;
return *this;
}
Credentials Credentials::createCredentials(const QString &name)
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return Credentials();
}
QCA::KeyGenerator generator;
QCA::PrivateKey key = generator.createRSA(2048);
QString pemKey(key.toPublicKey().toPEM());
- QString id = QCryptographicHash::hash(pemKey.toAscii(), QCryptographicHash::Sha1);
+ QString id = QCryptographicHash::hash(pemKey.toLatin1(), QCryptographicHash::Sha1);
return Credentials(id, name, key.toPEM(), true);
#else
return Credentials();
#endif
}
TrustLevel Credentials::trustLevel() const
{
/**
QString pemFile = KStandardDirs::locate("trustedkeys", id());
if (!pemFile.isEmpty()) {
QCA::PublicKey pubKey = QCA::PublicKey::fromPEMFile(pemFile);
if (pubKey == d->publicKey) {
return true;
}
}
*/
//Trust no one ;)
return UnknownTrusted;
}
bool Credentials::isValid() const
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return false;
}
if (d->publicKey.isNull()) {
return false;
} else {
- QString id = QCryptographicHash::hash(d->publicKey.toPEM().toAscii(), QCryptographicHash::Sha1);
+ QString id = QCryptographicHash::hash(d->publicKey.toPEM().toLatin1(), QCryptographicHash::Sha1);
return (id == d->id);
}
#else
#ifndef NDEBUG
kDebug() << "libplasma is compiled without support for remote widgets. Key invalid.";
#endif
return false;
#endif
}
QString Credentials::name() const
{
return d->name;
}
QString Credentials::id() const
{
return d->id;
}
bool Credentials::isValidSignature(const QByteArray &signature, const QByteArray &payload)
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return false;
}
if (d->publicKey.canVerify()) {
if (!isValid()) {
#ifndef NDEBUG
kDebug() << "Key is null?";
#endif
}
QCA::PublicKey publicKey = QCA::PublicKey::fromPEM(d->publicKey.toPEM());
publicKey.startVerify( QCA::EMSA3_MD5 );
publicKey.update(payload);
return ( publicKey.validSignature( signature ) );
} else {
#ifndef NDEBUG
kDebug() << "Can't verify?";
#endif
return false;
}
#else
return false;
#endif
}
bool Credentials::canSign() const
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return false;
}
return d->privateKey.canSign();
#else
return false;
#endif
}
QByteArray Credentials::signMessage(const QByteArray &message)
{
#if ENABLE_REMOTE_WIDGETS
if(!QCA::isSupported(REQUIRED_FEATURES)) {
#ifndef NDEBUG
kDebug() << "RSA not supported";
#endif
return QByteArray();
} else if (canSign()) {
//QCA::PrivateKey privateKey = QCA::PrivateKey::fromPEM(d->privateKey.toPEM());
d->privateKey.startSign( QCA::EMSA3_MD5 );
d->privateKey.update( message );
QByteArray signature = d->privateKey.signature();
return signature;
} else {
return QByteArray();
}
#else
return QByteArray();
#endif
}
Credentials Credentials::toPublicCredentials() const
{
#if ENABLE_REMOTE_WIDGETS
Credentials result(*this);
result.d->privateKey = QCA::PrivateKey();
return result;
#else
return Credentials();
#endif
}
QDataStream &operator<<(QDataStream &out, const Credentials &myObj)
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return out;
}
QString privateKeyPem;
QString publicKeyPem;
if (!myObj.d->privateKey.isNull()) {
privateKeyPem = myObj.d->privateKey.toPEM();
}
if (!myObj.d->publicKey.isNull()) {
publicKeyPem = myObj.d->publicKey.toPEM();
}
out << 1 << myObj.d->id << myObj.d->name << privateKeyPem << publicKeyPem;
#endif
return out;
}
QDataStream &operator>>(QDataStream &in, Credentials &myObj)
{
#if ENABLE_REMOTE_WIDGETS
if (!QCA::isSupported(REQUIRED_FEATURES)) {
kWarning() << "QCA doesn't support " << REQUIRED_FEATURES;
return in;
}
QString privateKeyString;
QString publicKeyString;
uint version;
in >> version >> myObj.d->id >> myObj.d->name >> privateKeyString >> publicKeyString;
QCA::ConvertResult conversionResult;
if (!privateKeyString.isEmpty()) {
myObj.d->privateKey = QCA::PrivateKey::fromPEM(privateKeyString,
QByteArray(), &conversionResult);
}
if (!publicKeyString.isEmpty()) {
myObj.d->publicKey = QCA::PublicKey::fromPEM(publicKeyString, &conversionResult);
}
if (conversionResult != QCA::ConvertGood) {
#ifndef NDEBUG
kDebug() << "Unsuccessfull conversion of key?";
#endif
}
#endif
return in;
}
}
diff --git a/plasma/remote/signing.cpp b/plasma/remote/signing.cpp
index 4c85049efe..25a915ec06 100644
--- a/plasma/remote/signing.cpp
+++ b/plasma/remote/signing.cpp
@@ -1,662 +1,662 @@
/*
* Copyright (C) 2010 by Diego '[Po]lentino' Casella <polentino911@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "signing.h"
#include "signing_p.h"
#include <gpgme.h>
#include <gpgme++/gpgmefw.h>
#include <gpgme++/global.h>
#include <gpgme++/context.h>
#include <gpgme++/error.h>
#include <gpgme++/engineinfo.h>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <gpgme++/keygenerationresult.h>
#include <gpgme++/importresult.h>
#include <gpgme++/data.h>
#include <QtCore/QDir>
#include <QtCore/QStringList>
#include <kdirwatch.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kuser.h>
#include "plasma/package.h"
#include <cstdio> // FILE
namespace Plasma
{
SigningPrivate::SigningPrivate(Signing *auth, const QString &path)
: q(auth),
m_keystorePath(path)
{
GpgME::initializeLibrary();
GpgME::Error error = GpgME::checkEngine(GpgME::OpenPGP);
if (error) {
#ifndef NDEBUG
kDebug() << "OpenPGP engine not found: authentication will not work.";
#endif
return;
}
m_gpgContext = GpgME::Context::createForProtocol(GpgME::OpenPGP);
m_gpgContext->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS);
m_keystorePath = path;
if (m_keystorePath.isEmpty()) {
// From the gpgme doc: if the homeDirectory() is null, it means we are using the standard dir,
// that is "/home/$USER/.gnupg/ ; so let's retrieve it.
KUser user;
m_keystorePath.append(user.homeDir()).append("/.gnupg/");
} else {
- error = m_gpgContext->setEngineHomeDirectory(m_keystorePath.toAscii().data());
+ error = m_gpgContext->setEngineHomeDirectory(m_keystorePath.toLatin1().data());
if (error) {
#ifndef NDEBUG
kDebug() << "Failed setting custom gpg keystore directory: using default.";
#endif
}
}
m_keystoreDir = new KDirWatch();
m_keystoreDir->addDir(m_keystorePath);
m_keystoreDir->addDir(ultimateKeyStoragePath());
m_keystoreDir->startScan(true);
// Start watching the keystore and the dir with the kde keys, and notify for changed
q->connect(m_keystoreDir, SIGNAL(created(const QString &)), q, SLOT(processKeystore(const QString &)));
q->connect(m_keystoreDir, SIGNAL(dirty(const QString &)), q, SLOT(keyAdded(const QString &)));
q->connect(m_keystoreDir, SIGNAL(deleted(const QString &)), q, SLOT(keyRemoved(const QString &)));
}
SigningPrivate::~SigningPrivate()
{
delete m_keystoreDir;
}
QString SigningPrivate::ultimateKeyStoragePath() const
{
return KStandardDirs::installPath("data") + "plasmakeys/";
}
void SigningPrivate::registerUltimateTrustKeys()
{
QSet<QByteArray> tmp;
keys[UltimatelyTrusted] = tmp;
if (!m_gpgContext) {
#ifndef NDEBUG
kDebug() << "GPGME context not valid: please re-initialize the library.";
#endif
return;
}
QString path = ultimateKeyStoragePath();
QDir dir(path);
if (!dir.exists() || path.isEmpty()) {
#ifndef NDEBUG
kDebug() << "Directory with KDE keys not found: aborting";
#endif
return;
}
const QStringList keyFiles = dir.entryList(QDir::Files); // QDir::Files is rendundant in this stage
// Foreach file found, open it and import the key into the gpg keyring.
// First avoid firing multiple entryWritten() signals
m_keystoreDir->stopScan();
foreach (QString keyFile, keyFiles) {
FILE *fp;
- fp = fopen(keyFile.toAscii().data(), "r");
+ fp = fopen(keyFile.toLatin1().data(), "r");
GpgME::Data data(fp);
GpgME::ImportResult iRes = m_gpgContext->importKeys(data);
if (iRes.error()) {
#ifndef NDEBUG
kDebug() << "Error while importing the key located at: " << keyFile;
#endif
#ifndef NDEBUG
kDebug() << " The error is:" << iRes.error().asString() << "; Skipping.";
#endif
continue;
}
// The first fingerprint listed is the one we need to save
tmp.insert(iRes.import(0).fingerprint());
}
keys[UltimatelyTrusted] = tmp;
// Restore scanning folders
m_keystoreDir->startScan(true, true);
}
void SigningPrivate::splitKeysByTrustLevel()
{
if (!m_gpgContext) {
#ifndef NDEBUG
kDebug() << "GPGME context not valid: please re-initialize the library.";
#endif
return;
}
QSet<QByteArray> temp = keys[UltimatelyTrusted];
keys.clear();
keys[UltimatelyTrusted] = temp;
// Splitting the keys by their trust level is a boring task, since we have to distinguish
// `which key has been signed with an other given key` :P
//
// Loop 1: import and load the KDE keys, already done in registerUltimateTrustKeys()
//
// Loop 2: load the user keyring (private keys only), and loop for:
// - a: a key not yet expired;
// - b: a key not already present in the keys[UltimatelyTrusted];
//
// Loop 3: load the user keyring, and loop for:
// - a: a key not yet expired;
// - b: a key not already present in the keys[UltimatelyTrusted];
// - c: a key not yet in keys[SelfTrusted]
//
// After Loop 3, the tmp object contains the remaining keys not yet processed.
//
// Loop 4: foreach key not yet classified, inspect their signatures and:
// - a: if contains a key from keys[UltimatelyTrusted], save it in keys[FullyTrusted];
// - b: if contains a key from keys[SelfTrusted], save it in keys[UserTrusted];
// - c: if the signature is unknown, let's save it in keys[UnknownTrusted].
QSet<QByteArray> tmp;
GpgME::Error error = m_gpgContext->startKeyListing("", true);
while (!error) { // Loop 2
GpgME::Key key = m_gpgContext->nextKey(error);
if (error) {
break;
}
QByteArray data(key.subkey(0).fingerprint());
// If the key is disabled, expired, invalid or revoked, put it in the untrusted list
if (key.isDisabled() || key.isExpired() || key.isInvalid() || key.isRevoked()) {
keys[CompletelyUntrusted].insert(data);
} else if (!keys[UltimatelyTrusted].contains(data)) {
// Ensure we are not processing twice the trusted KDE keys
// The keys is new, valid and private: save it !
keys[SelfTrusted].insert(data);
}
}
GpgME::KeyListResult lRes = m_gpgContext->endKeyListing();
}
Plasma::TrustLevel SigningPrivate::addKeyToCache(const QByteArray &fingerprint)
{
if (!m_gpgContext) {
#ifndef NDEBUG
kDebug() << "GPGME context not valid: please re-initialize the library.";
#endif
return UnknownTrusted;
}
GpgME::Error error;
GpgME::Key key = m_gpgContext->key(fingerprint.data(), error);
if (error) {
keys[UnknownTrusted].insert(fingerprint);
return UnknownTrusted;
}
if (keys[UltimatelyTrusted].contains(fingerprint)) {
return UltimatelyTrusted;
} else if (keys[SelfTrusted].contains(fingerprint)) {
return SelfTrusted;
}
// If the key is disabled, expired, invalid or revoked, put it in the untrusted list
if (key.isDisabled() || key.isExpired() || key.isInvalid() || key.isRevoked()) {
keys[CompletelyUntrusted].insert(fingerprint);
return CompletelyUntrusted;
}
for (unsigned int i = 0; i < key.numUserIDs(); ++i) {
foreach (const GpgME::UserID::Signature &signature, key.userID(i).signatures()) {
if (keys[UltimatelyTrusted].contains(signature.signerKeyID())) {
// if the unknown key has a signer that is a kde key, let's trust it
keys[FullyTrusted].insert(fingerprint);
return FullyTrusted;
} else if (keys[SelfTrusted].contains(signature.signerKeyID())) {
// if the unknown key has a signer that is a user key, let's trust it
keys[UserTrusted].insert(fingerprint);
return UserTrusted;
}
}
}
// We didn't stored the unknown key in the previous loop, which means that we
// don't know the hey al all.
keys[UnknownTrusted].insert(fingerprint);
return UnknownTrusted;
}
#ifndef NDEBUG
void SigningPrivate::dumpKeysToDebug()
{
kDebug() << "UltimatelyTrusted = " << keys[UltimatelyTrusted];
kDebug() << "FullyTrusted = " << keys[FullyTrusted];
kDebug() << "SelfTrusted = " << keys[SelfTrusted];
kDebug() << "UserTrusted = " << keys[UserTrusted];
kDebug() << "UnknownTrusted = " << keys[UnknownTrusted];
kDebug() << "CompletelyUntrusted = " << keys[CompletelyUntrusted];
}
#endif
QStringList SigningPrivate::keysID(const bool returnPrivate) const
{
QStringList result;
if (!m_gpgContext) {
#ifndef NDEBUG
kDebug() << "GPGME context not valid: please re-initialize the library.";
#endif
return result;
}
GpgME::Error error = m_gpgContext->startKeyListing("", returnPrivate);
while (!error) {
GpgME::Key k = m_gpgContext->nextKey(error);
if (error) {
break;
}
result.append(k.subkey(0).fingerprint());
}
GpgME::KeyListResult lRes = m_gpgContext->endKeyListing();
if (lRes.error()) {
#ifndef NDEBUG
kDebug() << "Error while ending the keyListing operation: " << lRes.error().asString();
#endif
}
return result;
}
void SigningPrivate::processKeystore(const QString &path)
{
if (path != m_keystorePath) {
registerUltimateTrustKeys();
return;
}
QSet<QByteArray> oldValues;
oldValues += keys[UnverifiableTrust];
oldValues += keys[CompletelyUntrusted];
oldValues += keys[UnknownTrusted];
oldValues += keys[SelfTrusted];
oldValues += keys[FullyTrusted];
oldValues += keys[UltimatelyTrusted];
splitKeysByTrustLevel();
QSet<QByteArray> newValues;
newValues += keys[UnverifiableTrust];
newValues += keys[CompletelyUntrusted];
newValues += keys[UnknownTrusted];
newValues += keys[SelfTrusted];
newValues += keys[FullyTrusted];
newValues += keys[UltimatelyTrusted];
QString result;
bool keystoreIncreased = (newValues.size() >= oldValues.size());
if (keystoreIncreased) {
foreach (QByteArray value, newValues) {
if (!oldValues.contains(value)) {
// Found the key added
result.append(value);
break;
}
}
} else {
foreach (QByteArray value, oldValues) {
if (!newValues.contains(value)) {
// Found the removed key
result.append(value);
break;
}
}
}
if (!result.isEmpty()) {
if (keystoreIncreased) {
emit q->keyAdded(result);
} else {
emit q->keyRemoved(result);
}
}
}
void SigningPrivate::keyAdded(const QString &path)
{
if (path == m_keystorePath) {
// we don't worry about keys added to the key store path,
// just the ultimate store dir
return;
}
// Avoid firing multiple signals by kdirwatch instances
m_keystoreDir->stopScan();
FILE *fp;
- fp = fopen(path.toAscii().data(), "r");
+ fp = fopen(path.toLatin1().data(), "r");
GpgME::Data data(fp);
GpgME::ImportResult iRes = m_gpgContext->importKeys(data);
bool alreadyInMap = false;
// Ensure we don't already have the key
foreach (QByteArray sec, keys[UltimatelyTrusted]) {
if (strcmp(sec.data(), iRes.import(0).fingerprint())) {
alreadyInMap = true;
break;
}
}
if (!alreadyInMap) {
keys[UltimatelyTrusted] << QByteArray(iRes.import(0).fingerprint());
splitKeysByTrustLevel();
}
// Restore scanning folders
m_keystoreDir->startScan(true, true);
QString result(iRes.import(0).fingerprint());
emit(q->keyAdded(result));
}
void SigningPrivate::keyRemoved(const QString &path)
{
// Avoid firing multiple signals by kdirwatch instances
m_keystoreDir->stopScan();
if (path == m_keystorePath) {
// Restore scanning folders
m_keystoreDir->startScan(true, true);
return;
}
QSet<QByteArray> oldKeys = keys[UltimatelyTrusted];
registerUltimateTrustKeys();
QSet<QByteArray> newkeys = keys[UltimatelyTrusted];
QString result;
foreach (QByteArray key, oldKeys) {
if (!newkeys.contains(key)) {
// We found the missing key :)
result.append(key);
break;
}
}
GpgME::Error error = m_gpgContext->startKeyListing("");
while (!error) {
GpgME::Key k = m_gpgContext->nextKey(error);
if (error) {
break;
}
if (result.contains(k.subkey(0).fingerprint())) {
error = m_gpgContext->startKeyDeletion(k, true); // GG
if (error) {
#ifndef NDEBUG
kDebug() << "Can't delete key with fingerprint: " << result;
#endif
m_gpgContext->endKeyListing();
return;
}
break;
}
}
GpgME::KeyListResult lRes = m_gpgContext->endKeyListing();
if (lRes.error()) {
#ifndef NDEBUG
kDebug() << "Error while ending the keyListing operation: " << lRes.error().asString();
#endif
}
splitKeysByTrustLevel();
// Restore scanning folders
m_keystoreDir->startScan(true, true);
emit(q->keyRemoved(result));
}
QStringList SigningPrivate::signersOf(const QString id) const
{
QStringList result;
GpgME::Error error;
- GpgME::Key key = m_gpgContext->key(id.toAscii().data(), error);
+ GpgME::Key key = m_gpgContext->key(id.toLatin1().data(), error);
if (!error) {
for (unsigned int i = 0; i < key.numUserIDs(); ++i) {
foreach (const GpgME::UserID::Signature &signature, key.userID(i).signatures()) {
QString sig(signature.signerKeyID());
if (!result.contains(sig) && id != sig) {
result.append(sig);
}
}
}
}
//kDebug() << "Hey, the key " << id << " has been signed with " << result;
return result;
}
Signing::Signing(const QString &keystorePath)
: QObject(),
d(new SigningPrivate(this, keystorePath))
{
d->registerUltimateTrustKeys();
d->splitKeysByTrustLevel();
}
Signing::~Signing()
{
delete d;
}
QStringList Signing::keysByTrustLevel(TrustLevel trustLevel) const
{
if (trustLevel == UnverifiableTrust) {
return QStringList();
}
QSet<QByteArray> s = d->keys[trustLevel];
QStringList tmp;
foreach (const QByteArray &sa, s) {
tmp.append(sa);
}
return tmp;
}
TrustLevel Signing::trustLevelOf(const QString &keyID) const
{
if (keyID.isEmpty()) {
return Plasma::UnverifiableTrust;
}
for (int i = (int)Plasma::UnverifiableTrust; i <= (int)Plasma::UltimatelyTrusted; ++i) {
QSet<QByteArray> tmp = d->keys[(Plasma::TrustLevel)i];
foreach (QByteArray key, tmp) {
- if (key.contains(keyID.toAscii().data())) {
+ if (key.contains(keyID.toLatin1().data())) {
return (Plasma::TrustLevel)i;
}
}
}
- return d->addKeyToCache(keyID.toAscii());
+ return d->addKeyToCache(keyID.toLatin1());
}
QString Signing::signerOf(const Package &package) const
{
const QString contents = package.path() + "contents.hash";
if (!QFile::exists(contents)) {
#ifndef NDEBUG
kDebug() << "not contents hash for package at" << package.path();
#endif
return QString();
}
QFile file(contents);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
#ifndef NDEBUG
kDebug() << "could not open contents.hash file for reading" << contents;
#endif
return QString();
}
const QByteArray hash = file.read(10 * 1024);
const QString actualHash = package.contentsHash();
if (actualHash != hash) {
#ifndef NDEBUG
kDebug() << "contents.hash does not match contents of package" << package.path();
#endif
return QString();
}
return d->verifySignature(contents, QString());
}
QString Signing::signerOf(const QUrl &package, const QUrl &signature) const
{
#ifndef NDEBUG
kDebug() << "Checking existence of " << package;
#endif
#ifndef NDEBUG
kDebug() << "Checking existence of " << signature;
#endif
if (!package.isLocalFile() || (!signature.isEmpty() && !signature.isLocalFile())) {
#ifndef NDEBUG
kDebug() << "Remote urls not yet supported. FIXME.";
#endif
return QString();
}
return d->verifySignature(package.path(), signature.path());
}
QString SigningPrivate::verifySignature(const QString &filePath, const QString &signature)
{
if (!QFile::exists(filePath)) {
#ifndef NDEBUG
kDebug() << "Package" << filePath << "does not exist: signature verification aborted.";
#endif
return QString();
}
const QString signaturePath = signature.isEmpty() ? filePath + (".sig") : signature;
if (!QFile::exists(signaturePath)) {
#ifndef NDEBUG
kDebug() << "Signature" << signaturePath << "does not exist: signature verification aborted.";
#endif
return QString();
}
//kDebug() << "Cheking if " << filePath << " and " << signaturePath << " matches";
FILE *pFile = fopen(filePath.toLocal8Bit().data(), "r");
if (!pFile) {
#ifndef NDEBUG
kDebug() << "failed to open file" << filePath;
#endif
return QString();
}
FILE *pSig = fopen(signaturePath.toLocal8Bit().data(), "r");
if (!pSig) {
#ifndef NDEBUG
kDebug() << "failed to open package file" << signaturePath;
#endif
fclose(pFile);
return QString();
}
GpgME::Data file(pFile);
GpgME::Data sig(pSig);
GpgME::VerificationResult vRes = m_gpgContext->verifyDetachedSignature(sig, file);
QString rv;
if (!vRes.error()) {
//kDebug() << "got" << vRes.signatures().size() << "signatures out" << vRes.error().asString();
foreach (GpgME::Signature sig, vRes.signatures()) {
if (sig.fingerprint()) {
rv = sig.fingerprint();
break;
}
}
//kDebug() << "message " << filePath << " and signature " << signaturePath << "matched! The fingerprint of the signer is: " << rv;
}
fclose(pFile);
fclose(pSig);
return rv;
}
QString Signing::keyStorePath() const
{
return d->m_keystorePath;
}
QString Signing::descriptiveString(const QString &keyID) const
{
if (keyID.isEmpty()) {
return QString();
}
if (!d->m_gpgContext) {
#ifndef NDEBUG
kDebug() << "GPGME context not valid: please re-initialize the library.";
#endif
return QString();
}
GpgME::Error error;
- GpgME::Key key = d->m_gpgContext->key(keyID.toAscii().data(), error);
+ GpgME::Key key = d->m_gpgContext->key(keyID.toLatin1().data(), error);
if (error) {
return QString();
}
return key.userID(0).id();
}
}
#include "moc_signing.cpp"
diff --git a/staging/kde4support/src/kdecore/k3resolverstandardworkers.cpp b/staging/kde4support/src/kdecore/k3resolverstandardworkers.cpp
index 8b166ac8a9..bd4f0f7ea1 100644
--- a/staging/kde4support/src/kdecore/k3resolverstandardworkers.cpp
+++ b/staging/kde4support/src/kdecore/k3resolverstandardworkers.cpp
@@ -1,1046 +1,1046 @@
/* -*- C++ -*-
* Copyright (C) 2003,2004 Thiago Macieira <thiago@kde.org>
*
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "k3resolverstandardworkers_p.h"
#include <config-network.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <QFile>
#include <QList>
#include <QMutex>
#include <QTextStream>
#include <QThread>
#ifdef Q_OS_WIN
#include <winsock2.h>
#endif
#include "kdebug.h"
#include "kglobal.h"
#include "kcomponentdata.h"
#include "kstandarddirs.h"
#include "k3resolver.h"
#include "k3socketaddress.h"
struct hostent;
struct addrinfo;
using namespace KNetwork;
using namespace KNetwork::Internal;
static bool hasIPv6()
{
#ifdef Q_OS_WIN
extern void KNetwork_initSocket();
KNetwork_initSocket();
#endif
#ifdef AF_INET6
if (!qgetenv("KDE_NO_IPV6").isEmpty())
return false;
# ifdef Q_OS_WIN
SOCKET s = ::socket(AF_INET6, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
return false;
::closesocket(s);
# else
int fd = ::socket(AF_INET6, SOCK_STREAM, 0);
if (fd == -1)
return false;
::close(fd);
# endif
return true;
#else
return false;
#endif
}
// blacklist management
static QMutex blacklistMutex; // KDE4: change to a QReadWriteLock
QStringList KBlacklistWorker::blacklist;
void KBlacklistWorker::init()
{
if (!KComponentData::hasMainComponent())
return;
static bool beenhere = false;
if (beenhere)
return;
beenhere = true;
loadBlacklist();
}
void KBlacklistWorker::loadBlacklist()
{
QMutexLocker locker(&blacklistMutex);
QStringList filelist = KGlobal::dirs()->findAllResources("config", QLatin1String("ipv6blacklist"));
QStringList::ConstIterator it = filelist.constBegin(),
end = filelist.constEnd();
for ( ; it != end; ++it)
{
// for each file, each line is a domainname to be blacklisted
QFile f(*it);
if (!f.open(QIODevice::ReadOnly))
continue;
QTextStream stream(&f);
stream.setCodec("latin1");
for (QString line = stream.readLine(); !line.isNull();
line = stream.readLine())
{
if (line.isEmpty())
continue;
// make sure there are no surrounding whitespaces
// and that it starts with .
line = line.trimmed();
if (line[0] != QLatin1Char('.'))
line.prepend(QLatin1Char('.'));
blacklist.append(line.toLower());
}
}
}
// checks the blacklist to see if the domain is listed
// it matches the domain ending part
bool KBlacklistWorker::isBlacklisted(const QString& host)
{
KBlacklistWorker::init();
// empty hostnames cannot be blacklisted
if (host.isEmpty())
return false;
QString ascii = QLatin1String(KResolver::domainToAscii(host));
QMutexLocker locker(&blacklistMutex);
// now find out if this hostname is present
QStringList::ConstIterator it = blacklist.constBegin(),
end = blacklist.constEnd();
for ( ; it != end; ++it)
if (ascii.endsWith(*it))
return true;
// no match:
return false;
}
bool KBlacklistWorker::preprocess()
{
if (isBlacklisted(nodeName()))
{
results.setError(KResolver::NoName);
finished();
return true;
}
return false;
}
bool KBlacklistWorker::run()
{
results.setError(KResolver::NoName);
finished();
return false; // resolution failure
}
namespace
{
/*
* Note on the use of the system resolver functions:
*
* In all cases, we prefer to use the new getaddrinfo(3) call. That means
* it will always be used if it is found.
*
* If it's not found, we have the option to use gethostbyname2_r,
* gethostbyname_r, gethostbyname2 and gethostbyname. If gethostbyname2_r
* is defined, we will use it.
*
* If it's not defined, we have to choose between the non-reentrant
* gethostbyname2 and the reentrant but IPv4-only gethostbyname_r:
* we will choose gethostbyname2 if AF_INET6 is defined.
*
* Lastly, gethostbyname will be used if nothing else is present.
*/
#if !HAVE_GETADDRINFO
# if HAVE_GETHOSTBYNAME2_R
# define USE_GETHOSTBYNAME2_R
# elif HAVE_GETHOSTBYNAME_R && (!defined(AF_INET6) || !HAVE_GETHOSTBYNAME2)
# define USE_GETHOSTBYNAME_R
# elif HAVE_GETHOSTBYNAME2
# define USE_GETHOSTBYNAME2
# else
# define USE_GETHOSTBYNAME
# endif
class GetHostByNameThread: public KResolverWorkerBase
{
public:
QByteArray m_hostname; // might be different!
quint16 m_port;
int m_scopeid;
int m_af;
KResolverResults& results;
GetHostByNameThread(const char * hostname, quint16 port,
int scopeid, int af, KResolverResults* res) :
m_hostname(hostname), m_port(port), m_scopeid(scopeid), m_af(af),
results(*res)
{ }
~GetHostByNameThread()
{ }
virtual bool preprocess()
{ return true; }
virtual bool run();
void processResults(hostent* he, int my_h_errno);
};
bool GetHostByNameThread::run()
{
hostent *resultptr;
hostent my_results;
unsigned buflen = 1024;
int res;
int my_h_errno;
char *buf = 0L;
// qDebug("ResolveThread::run(): started threaded gethostbyname for %s (af = %d)",
// m_hostname.data(), m_af);
ResolverLocker resLock( this );
do
{
res = 0;
my_h_errno = HOST_NOT_FOUND;
// check blacklist
if (m_af != AF_INET &&
KBlacklistWorker::isBlacklisted(QLatin1String(m_hostname)))
break;
# ifdef USE_GETHOSTBYNAME2_R
buf = new char[buflen];
res = gethostbyname2_r(m_hostname, m_af, &my_results, buf, buflen,
&resultptr, &my_h_errno);
# elif defined(USE_GETHOSTBYNAME_R)
if (m_af == AF_INET)
{
buf = new char[buflen];
res = gethostbyname_r(m_hostname, &my_results, buf, buflen,
&resultptr, &my_h_errno);
}
else
resultptr = 0; // signal error
# elif defined(USE_GETHOSTBYNAME2)
// must lock mutex
resultptr = gethostbyname2(m_hostname, m_af);
my_h_errno = h_errno;
# else
if (m_af == AF_INET)
{
// must lock mutex
resultptr = gethostbyname(m_hostname);
my_h_errno = h_errno;
}
else
resultptr = 0;
# endif
if (resultptr != 0L)
my_h_errno = 0;
// qDebug("GetHostByNameThread::run(): gethostbyname for %s (af = %d) returned: %d",
// m_hostname.data(), m_af, my_h_errno);
if (res == ERANGE)
{
// Enlarge the buffer
buflen += 1024;
delete [] buf;
buf = new char[buflen];
}
if ((res == ERANGE || my_h_errno != 0) && checkResolver())
{
// resolver needs updating, so we might as well do it now
resLock.openClose();
}
}
while (res == ERANGE);
processResults(resultptr, my_h_errno);
delete [] buf;
finished();
return results.error() == KResolver::NoError;
}
void GetHostByNameThread::processResults(hostent *he, int herrno)
{
if (herrno)
{
qDebug("KStandardWorker::processResults: got error %d", herrno);
switch (herrno)
{
case HOST_NOT_FOUND:
results.setError(KResolver::NoName);
return;
case TRY_AGAIN:
results.setError(KResolver::TryAgain);
return;
case NO_RECOVERY:
results.setError(KResolver::NonRecoverable);
return;
case NO_ADDRESS:
results.setError(KResolver::NoName);
return;
default:
results.setError(KResolver::UnknownError);
return;
}
}
else if (he == 0L)
{
results.setError(KResolver::NoName);
return; // this was an error
}
// clear any errors
setError(KResolver::NoError);
results.setError(KResolver::NoError);
// we process results in the reverse order
// that is, we prepend each result to the list of results
int proto = protocol();
int socktype = socketType();
if (socktype == 0)
socktype = SOCK_STREAM; // default
QString canon = KResolver::domainToUnicode(QLatin1String(he->h_name));
KInetSocketAddress sa;
sa.setPort(m_port);
if (he->h_addrtype != AF_INET)
sa.setScopeId(m_scopeid); // this will also change the socket into IPv6
for (int i = 0; he->h_addr_list[i]; i++)
{
sa.setHost(KIpAddress(he->h_addr_list[i], he->h_addrtype == AF_INET ? 4 : 6));
results.prepend(KResolverEntry(sa, socktype, proto, canon, m_hostname));
// qDebug("KStandardWorker::processResults: adding %s", sa.toString().toLatin1().constData());
}
// qDebug("KStandardWorker::processResults: added %d entries", i);
}
#else // HAVE_GETADDRINFO
class GetAddrInfoThread: public KResolverWorkerBase
{
public:
QByteArray m_node;
QByteArray m_serv;
int m_af;
int m_flags;
KResolverResults& results;
GetAddrInfoThread(const char* node, const char* serv, int af, int flags,
KResolverResults* res) :
m_node(node), m_serv(serv), m_af(af), m_flags(flags), results(*res)
{ }
~GetAddrInfoThread()
{ }
virtual bool preprocess()
{ return true; }
virtual bool run();
void processResults(addrinfo* ai, int ret_code, KResolverResults& rr);
};
bool GetAddrInfoThread::run()
{
// check blacklist
if ((m_af != AF_INET && m_af != AF_UNSPEC) &&
KBlacklistWorker::isBlacklisted(QLatin1String(m_node)))
{
results.setError(KResolver::NoName);
finished();
return false; // failed
}
do
{
ResolverLocker resLock( this );
// process hints
addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_family = m_af;
hint.ai_socktype = socketType();
hint.ai_protocol = protocol();
if (hint.ai_socktype == 0)
hint.ai_socktype = SOCK_STREAM; // default
if (m_flags & KResolver::Passive)
hint.ai_flags |= AI_PASSIVE;
if (m_flags & KResolver::CanonName)
hint.ai_flags |= AI_CANONNAME;
# ifdef AI_NUMERICHOST
if (m_flags & KResolver::NoResolve)
hint.ai_flags |= AI_NUMERICHOST;
# endif
# ifdef AI_ADDRCONFIG
hint.ai_flags |= AI_ADDRCONFIG;
# endif
// now we do the blocking processing
if (m_node.isEmpty())
m_node = "*"; // krazy:exclude=doublequote_chars
addrinfo *result;
int res = getaddrinfo(m_node, m_serv, &hint, &result);
// kDebug(179) << "getaddrinfo(\""
// << m_node << "\", \"" << m_serv << "\", af="
// << m_af << ") returned " << res << endl;
if (res != 0)
{
if (checkResolver())
{
// resolver requires reinitialisation
resLock.openClose();
continue;
}
switch (res)
{
case EAI_BADFLAGS:
results.setError(KResolver::BadFlags);
break;
#ifdef EAI_NODATA
// In some systems, EAI_NODATA was #define'd to EAI_NONAME which would break this case.
#if EAI_NODATA != EAI_NONAME
case EAI_NODATA: // it was removed in RFC 3493
#endif
#endif
case EAI_NONAME:
results.setError(KResolver::NoName);
break;
case EAI_AGAIN:
results.setError(KResolver::TryAgain);
break;
case EAI_FAIL:
results.setError(KResolver::NonRecoverable);
break;
case EAI_FAMILY:
results.setError(KResolver::UnsupportedFamily);
break;
case EAI_SOCKTYPE:
results.setError(KResolver::UnsupportedSocketType);
break;
case EAI_SERVICE:
results.setError(KResolver::UnsupportedService);
break;
case EAI_MEMORY:
results.setError(KResolver::Memory);
break;
#ifdef EAI_SYSTEM // not available on windows
case EAI_SYSTEM:
results.setError(KResolver::SystemError, errno);
break;
#endif
default:
results.setError(KResolver::UnknownError, errno);
break;
}
finished();
return false; // failed
}
// if we are here, lookup succeeded
QString canon;
const char *previous_canon = 0L;
for (addrinfo* p = result; p; p = p->ai_next)
{
// cache the last canon name to avoid doing the ToUnicode processing unnecessarily
if ((previous_canon && !p->ai_canonname) ||
(!previous_canon && p->ai_canonname) ||
(p->ai_canonname != previous_canon &&
strcmp(p->ai_canonname, previous_canon) != 0))
{
- canon = KResolver::domainToUnicode(QString::fromAscii(p->ai_canonname));
+ canon = KResolver::domainToUnicode(QString::fromLatin1(p->ai_canonname));
previous_canon = p->ai_canonname;
}
results.append(KResolverEntry(p->ai_addr, p->ai_addrlen, p->ai_socktype,
p->ai_protocol, canon, m_node));
}
freeaddrinfo(result);
results.setError(KResolver::NoError);
finished();
return results.error() == KResolver::NoError;
}
while (true);
}
#endif // HAVE_GETADDRINFO
} // namespace
KStandardWorker::~KStandardWorker()
{
qDeleteAll(resultList);
}
bool KStandardWorker::sanityCheck()
{
// check that the requested values are sensible
if (!nodeName().isEmpty())
{
QString node = nodeName();
if (node.indexOf(QLatin1Char('%')) != -1)
node.truncate(node.indexOf(QLatin1Char('%')));
if (node.isEmpty() || node == QLatin1String("*") ||
node == QLatin1String("localhost"))
m_encodedName.truncate(0);
else
{
m_encodedName = KResolver::domainToAscii(node);
if (m_encodedName.isNull())
{
qDebug("could not encode hostname '%s' (UTF-8)", node.toUtf8().data());
setError(KResolver::NoName);
return false; // invalid hostname!
}
// qDebug("Using encoded hostname '%s' for '%s' (UTF-8)", m_encodedName.data(),
// node.toUtf8().data());
}
}
else
m_encodedName.truncate(0); // just to be sure, but it should be clear already
if (protocol() == -1)
{
setError(KResolver::NonRecoverable);
return false; // user passed invalid protocol name
}
return true; // it's sane
}
bool KStandardWorker::resolveScopeId()
{
// we must test the original name, not the encoded one
scopeid = 0;
int pos = nodeName().lastIndexOf(QLatin1Char('%'));
if (pos == -1)
return true;
QString scopename = nodeName().mid(pos + 1);
bool ok;
scopeid = scopename.toInt(&ok);
if (!ok)
{
// it's not a number
// therefore, it's an interface name
#if HAVE_IF_NAMETOINDEX
scopeid = if_nametoindex(scopename.toLatin1());
#else
scopeid = 0;
#endif
}
return true;
}
bool KStandardWorker::resolveService()
{
// find the service first
bool ok;
port = serviceName().toUInt(&ok);
if (!ok)
{
// service name does not contain a port number
// must be a name
if (serviceName().isEmpty() || serviceName().compare(QLatin1String("*")) == 0)
port = 0;
else
{
// it's a name. We need the protocol name in order to lookup.
QByteArray protoname = protocolName();
if (protoname.isEmpty() && protocol())
{
protoname = KResolver::protocolName(protocol()).first();
// if it's still empty...
if (protoname.isEmpty())
{
// lookup failed!
setError(KResolver::NoName);
return false;
}
}
else
protoname = "tcp";
// it's not, so we can do a port lookup
int result = KResolver::servicePort(serviceName().toLatin1(), protoname);
if (result == -1)
{
// lookup failed!
setError(KResolver::NoName);
return false;
}
// it worked, we have a port number
port = (quint16)result;
}
}
// we found a port
return true;
}
KResolver::ErrorCodes KStandardWorker::addUnix()
{
// before trying to add, see if the user wants Unix sockets
if ((familyMask() & KResolver::UnixFamily) == 0)
// no, Unix sockets are not wanted
return KResolver::UnsupportedFamily;
// now check if the requested data are good for a Unix socket
if (!m_encodedName.isEmpty())
return KResolver::AddrFamily; // non local hostname
if (protocol() || !protocolName().isNull())
return KResolver::BadFlags; // cannot have Unix sockets with protocols
QString pathname = serviceName();
if (pathname.isEmpty())
return KResolver::NoName;; // no path?
if (pathname[0] != QLatin1Char('/'))
// non absolute pathname
// put it in /tmp
pathname.prepend(QLatin1String("/tmp/"));
// qDebug("QNoResolveWorker::addUnix(): adding Unix socket for %s", pathname.toLocal8Bit().data());
KUnixSocketAddress sa(pathname);
int socktype = socketType();
if (socktype == 0)
socktype = SOCK_STREAM; // default
results.append(KResolverEntry(sa, socktype, 0));
setError(KResolver::NoError);
return KResolver::NoError;
}
bool KStandardWorker::resolveNumerically()
{
// if the NoResolve flag is active, our result from this point forward
// will always be true, even if the resolution failed.
// that indicates that our result is authoritative.
bool wantV4 = familyMask() & KResolver::IPv4Family,
wantV6 = familyMask() & KResolver::IPv6Family;
if (!wantV6 && !wantV4)
// no Internet address is wanted!
return (flags() & KResolver::NoResolve);
// now try to find results
if (!resolveScopeId() || !resolveService())
return (flags() & KResolver::NoResolve);
// we have scope IDs and port numbers
// now try to resolve the hostname numerically
KInetSocketAddress sa;
setError(KResolver::NoError);
sa.setHost(KIpAddress(QLatin1String(m_encodedName)));
// if it failed, the length was reset to 0
bool ok = sa.length() != 0;
sa.setPort(port);
if (sa.ipVersion() == 6)
sa.setScopeId(scopeid);
int proto = protocol();
int socktype = socketType();
if (socktype == 0)
socktype = SOCK_STREAM;
if (ok)
{
// the given hostname was successfully converted to an IP address
// check if the user wanted this kind of address
if ((sa.ipVersion() == 4 && wantV4) ||
(sa.ipVersion() == 6 && wantV6))
results.append(KResolverEntry(sa, socktype, proto));
else
{
// Note: the address *IS* a numeric IP
// but it's not of the kind the user asked for
//
// that means that it cannot be a Unix socket (because it's an IP)
// and that means that no resolution will tell us otherwise
//
// This is a failed resolution
setError(KResolver::AddrFamily);
return true;
}
}
else if (m_encodedName.isEmpty())
{
// user wanted localhost
if (flags() & KResolver::Passive)
{
if (wantV6)
{
sa.setHost(KIpAddress::anyhostV6);
results.append(KResolverEntry(sa, socktype, proto));
}
if (wantV4)
{
sa.setHost(KIpAddress::anyhostV4);
results.append(KResolverEntry(sa, socktype, proto));
}
}
else
{
if (wantV6)
{
sa.setHost(KIpAddress::localhostV6);
results.append(KResolverEntry(sa, socktype, proto));
}
if (wantV4)
{
sa.setHost(KIpAddress::localhostV4);
results.append(KResolverEntry(sa, socktype, proto));
}
}
ok = true;
}
else
{
// probably bad flags, since the address is not convertible without
// resolution
setError(KResolver::BadFlags);
ok = false;
}
return ok || (flags() & KResolver::NoResolve);
}
bool KStandardWorker::preprocess()
{
// check sanity
if (!sanityCheck())
return false;
// this worker class can only handle known families
if (familyMask() & KResolver::UnknownFamily)
{
setError(KResolver::UnsupportedFamily);
return false; // we don't know about this
}
// check the socket types
if (socketType() != SOCK_STREAM && socketType() != SOCK_DGRAM && socketType() != 0)
{
setError(KResolver::UnsupportedSocketType);
return false;
}
// check if we can resolve all numerically
// resolveNumerically always returns true if the NoResolve flag is set
if (resolveNumerically() || m_encodedName.isEmpty())
{
// indeed, we have resolved numerically
setError(addUnix());
if (results.count())
setError(KResolver::NoError);
finished();
return true;
}
// check if the user wants something we know about
#ifdef AF_INET6
# define mask (KResolver::IPv6Family | KResolver::IPv4Family | KResolver::UnixFamily)
#else
# define mask (KResolver::IPv4Family | KResolver::UnixFamily)
#endif
if ((familyMask() & mask) == 0)
// errr... nothing we know about
return false;
#undef mask
return true; // it's ok
}
bool KStandardWorker::run()
{
#if !HAVE_GETADDRINFO
// check the scope id first
// since most of the resolutions won't have a scope id, this should be fast
// and we won't have wasted time on services if this fails
if (!resolveScopeId())
return false;
// resolve the service now, before entering the blocking operation
if (!resolveService())
return false;
#endif
// good
// now we need the hostname
setError(KResolver::NoName);
// these are the family types that we know of
struct
{
KResolver::SocketFamilies mask;
int af;
} families[] = { { KResolver::IPv4Family, AF_INET }
#ifdef AF_INET6
, { KResolver::IPv6Family, AF_INET6 }
#endif
};
int familyCount = sizeof(families)/sizeof(families[0]);
bool skipIPv6 = !hasIPv6();
for (int i = 0; i < familyCount; i++)
if (familyMask() & families[i].mask)
{
#ifdef AF_INET6
if (skipIPv6 && families[i].af == AF_INET6)
continue;
#endif
KResolverWorkerBase *worker;
KResolverResults *res = new KResolverResults;
resultList.append(res);
#if HAVE_GETADDRINFO
worker = new GetAddrInfoThread(m_encodedName,
serviceName().toLatin1(),
families[i].af, flags(), res);
#else
worker = new GetHostByNameThread(m_encodedName, port, scopeid,
families[i].af, res);
#endif
enqueue(worker);
}
// not finished
return true;
}
bool KStandardWorker::postprocess()
{
if (results.count())
return true; // no need
// now copy over what we need from the underlying results
// start backwards because IPv6 was launched later (if at all)
if (resultList.isEmpty())
{
results.setError(KResolver::NoName);
return true;
}
for (int i = resultList.size(); i > 0; --i)
{
KResolverResults* rr = resultList.at(i - 1);
if (!rr->isEmpty())
{
results.setError(KResolver::NoError);
KResolverResults::Iterator it = rr->begin();
for ( ; it != rr->end(); ++it)
results.append(*it);
}
else if (results.isEmpty())
// this generated an error
// copy the error code over
setError(rr->error(), rr->systemError());
delete rr;
resultList[i - 1] = 0L;
}
resultList.clear();
return true;
}
#if HAVE_GETADDRINFO
KGetAddrinfoWorker::~KGetAddrinfoWorker()
{
}
bool KGetAddrinfoWorker::preprocess()
{
// getaddrinfo(3) can always handle any kind of request that makes sense
if (!sanityCheck())
return false;
if (flags() & KResolver::NoResolve)
// oops, numeric resolution?
return run();
return true;
}
bool KGetAddrinfoWorker::run()
{
// make an AF_UNSPEC getaddrinfo(3) call
GetAddrInfoThread worker(m_encodedName, serviceName().toLatin1(),
AF_UNSPEC, flags(), &results);
if (!worker.run())
{
if (wantThis(AF_UNIX))
{
if (addUnix() == KResolver::NoError)
setError(KResolver::NoError);
}
else
setError(worker.results.error(), worker.results.systemError());
return false;
}
// The worker has finished working
// now copy over only what we may want
// keep track of any Unix-domain sockets
bool seen_unix = false;
int i = 0;
while ( i < results.count() )
{
const KResolverEntry& res = results[i];
if (res.family() == AF_UNIX)
seen_unix = true;
if (!wantThis(res.family()))
results.removeAt(i);
else
++i;
}
if (!seen_unix)
addUnix();
finished();
return true;
}
bool KGetAddrinfoWorker::wantThis(int family)
{
// tells us if the user wants a socket of this family
#ifdef AF_INET6
if (family == AF_INET6 && familyMask() & KResolver::IPv6Family)
return true;
#endif
if (family == AF_INET && familyMask() & KResolver::IPv4Family)
return true;
if (family == AF_UNIX && familyMask() & KResolver::UnixFamily)
return true;
// it's not a family we know about...
if (familyMask() & KResolver::UnknownFamily)
return true;
return false;
}
#endif
void KNetwork::Internal::initStandardWorkers()
{
//KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KBlacklistWorker>);
KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KStandardWorker>);
#if HAVE_GETADDRINFO
KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KGetAddrinfoWorker>);
#endif
}
diff --git a/staging/kde4support/src/kdeui/kprogressdialog.cpp b/staging/kde4support/src/kdeui/kprogressdialog.cpp
index 3b704419ed..a4276d3988 100644
--- a/staging/kde4support/src/kdeui/kprogressdialog.cpp
+++ b/staging/kde4support/src/kdeui/kprogressdialog.cpp
@@ -1,256 +1,256 @@
/* This file is part of the KDE libraries
Copyright (C) 1996 Martynas Kunigelis // krazy:exclude=copyright (email unknown)
Copyright (C) 2006-2007 Urs Wolfer <uwolfer at 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 version 2 as published by the Free Software Foundation.
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 "kprogressdialog.h"
#include <QLabel>
#include <QLayout>
#include <QProgressBar>
#include <QTimer>
#include <kguiitem.h>
#include <kpushbutton.h>
class KProgressDialog::KProgressDialogPrivate
{
public:
KProgressDialogPrivate(KProgressDialog *q)
: q(q),
cancelButtonShown(true),
mAutoClose(true),
mAutoReset(false),
mCancelled(false),
mAllowCancel(true),
mShown(false),
mMinDuration(2000)
{
}
void slotAutoShow();
void slotAutoActions(int percentage);
KProgressDialog *q;
bool cancelButtonShown : 1;
bool mAutoClose : 1;
bool mAutoReset : 1;
bool mCancelled : 1;
bool mAllowCancel : 1;
bool mShown : 1;
QString mCancelText;
QLabel* mLabel;
QProgressBar* mProgressBar;
QTimer* mShowTimer;
int mMinDuration;
};
KProgressDialog::KProgressDialog(QWidget* parent, const QString& caption,
- const QString& text, Qt::WFlags flags)
+ const QString& text, Qt::WindowFlags flags)
: KDialog(parent, flags),
d(new KProgressDialogPrivate(this))
{
setCaption(caption);
setButtons(KDialog::Cancel);
d->mShowTimer = new QTimer(this);
d->mCancelText = KDialog::buttonText(KDialog::Cancel);
QWidget *mainWidget = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(mainWidget);
layout->setMargin(10);
d->mLabel = new QLabel(text, mainWidget);
layout->addWidget(d->mLabel);
d->mProgressBar = new QProgressBar(mainWidget);
layout->addWidget(d->mProgressBar);
setMainWidget(mainWidget);
connect(d->mProgressBar, SIGNAL(valueChanged(int)),
this, SLOT(slotAutoActions(int)));
connect(d->mShowTimer, SIGNAL(timeout()), this, SLOT(slotAutoShow()));
d->mShowTimer->setSingleShot(true);
d->mShowTimer->start(d->mMinDuration);
}
KProgressDialog::~KProgressDialog()
{
delete d;
}
void KProgressDialog::KProgressDialogPrivate::slotAutoShow()
{
if (mShown || mCancelled)
{
return;
}
q->show();
}
void KProgressDialog::showEvent(QShowEvent *event)
{
d->mShown = true;
KDialog::showEvent(event);
}
void KProgressDialog::reject()
{
d->mCancelled = true;
if (d->mAllowCancel)
{
KDialog::reject();
}
}
bool KProgressDialog::wasCancelled() const
{
return d->mCancelled;
}
void KProgressDialog::ignoreCancel()
{
d->mCancelled = false;
}
void KProgressDialog::setMinimumDuration(int ms)
{
d->mMinDuration = ms;
if (!d->mShown)
{
d->mShowTimer->stop();
d->mShowTimer->setSingleShot(true);
d->mShowTimer->start(d->mMinDuration);
}
}
int KProgressDialog::minimumDuration() const
{
return d->mMinDuration;
}
void KProgressDialog::setAllowCancel(bool allowCancel)
{
d->mAllowCancel = allowCancel;
showCancelButton(allowCancel);
}
bool KProgressDialog::allowCancel() const
{
return d->mAllowCancel;
}
QProgressBar* KProgressDialog::progressBar()
{
return d->mProgressBar;
}
const QProgressBar* KProgressDialog::progressBar() const
{
return d->mProgressBar;
}
void KProgressDialog::setLabelText(const QString& text)
{
d->mLabel->setText(text);
}
QString KProgressDialog::labelText() const
{
return d->mLabel->text();
}
void KProgressDialog::showCancelButton(bool show)
{
showButton(Cancel, show);
}
bool KProgressDialog::autoClose() const
{
return d->mAutoClose;
}
void KProgressDialog::setAutoClose(bool autoClose)
{
d->mAutoClose = autoClose;
}
bool KProgressDialog::autoReset() const
{
return d->mAutoReset;
}
void KProgressDialog::setAutoReset(bool autoReset)
{
d->mAutoReset = autoReset;
}
void KProgressDialog::setButtonText(const QString& text)
{
d->mCancelText = text;
setButtonGuiItem(Cancel, KGuiItem(text));
}
QString KProgressDialog::buttonText() const
{
return d->mCancelText;
}
void KProgressDialog::KProgressDialogPrivate::slotAutoActions(int percentage)
{
if (percentage < mProgressBar->maximum() ||
(mProgressBar->minimum() == mProgressBar->maximum())) // progress dialogs with busy indicators (see #178648)
{
if (!cancelButtonShown)
{
q->setButtonGuiItem(KDialog::Cancel, KGuiItem(mCancelText));
cancelButtonShown = true;
}
return;
}
mShowTimer->stop();
if (mAutoReset)
{
mProgressBar->setValue(0);
}
else
{
q->setAllowCancel(true);
q->setButtonGuiItem(Cancel, KStandardGuiItem::close());
cancelButtonShown = false;
}
if (mAutoClose)
{
if (mShown)
{
q->hide();
}
else
{
emit q->finished();
}
}
}
#include "moc_kprogressdialog.cpp"
diff --git a/staging/kde4support/src/kdeui/kprogressdialog.h b/staging/kde4support/src/kdeui/kprogressdialog.h
index 56e6fc7206..592317ebdf 100644
--- a/staging/kde4support/src/kdeui/kprogressdialog.h
+++ b/staging/kde4support/src/kdeui/kprogressdialog.h
@@ -1,212 +1,212 @@
/* This file is part of the KDE libraries
Copyright (C) 1996 Martynas Kunigelis // krazy:exclude=copyright (email unknown)
Copyright (C) 2006-2007 Urs Wolfer <uwolfer at 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 version 2 as published by the Free Software Foundation.
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 KPROGRESSDIALOG_H
#define KPROGRESSDIALOG_H
#include <QProgressBar>
#include <kdialog.h>
#include <kde4support_export.h>
/**
* @short A dialog with a progress bar
*
* KProgressDialog provides a dialog with a text label, a progress bar
* and an optional cancel button with a KDE look 'n feel.
*
* Since knowing how long it can take to complete an action and it is
* undesirable to show a dialog for a split second before hiding it,
* there are a few ways to control the timing behavior of KProgressDialog.
* There is a time out that can be set before showing the dialog as well
* as an option to autohide or keep displaying the dialog once complete.
*
* All the functionality of QProgressBar is available through direct access
* to the progress bar widget via progressBar();
*
* \image html kprogressdialog.png "KDE Progress Dialog"
*
* @author Aaron J. Seigo
* @author Urs Wolfer uwolfer @ kde.org
*/
class KDE4SUPPORT_EXPORT KProgressDialog : public KDialog
{
Q_OBJECT
public:
/**
* Constructs a KProgressDialog
*
* @param parent Parent of the widget
* @param caption Text to display in window title bar
* @param text Text to display in the dialog
* @param flags The widget flags
*/
explicit KProgressDialog(QWidget* parent = 0, const QString& caption = QString(),
- const QString& text = QString(), Qt::WFlags flags = 0);
+ const QString& text = QString(), Qt::WindowFlags flags = 0);
/**
* Destructor
*/
~KProgressDialog();
/**
* Returns the QProgressBar used in this dialog.
* To set the number of steps or other progress bar related
* settings, access the QProgressBar object directly via this method.
*/
QProgressBar *progressBar();
/**
* Returns the QProgressBar used in this dialog.
* To set the number of steps or other progress bar related
* settings, access the QProgressBar object directly via this method.
*/
const QProgressBar *progressBar() const;
/**
* Sets the text in the dialog
*
* @param text the text to display
*/
void setLabelText(const QString &text);
/**
* Returns the current dialog text
*/
QString labelText() const;
/**
* Sets whether or not the user can cancel the process.
* If the dialog is cancellable, the Cancel button will be shown
* and the user can close the window using the window decorations.
* If the process is not (or should not be) interuptable,
* set the dialog to be modal and not cancellable.
*
* The default is true.
*
* @param allowCancel Set to true to make the dialog non-closable
*/
void setAllowCancel(bool allowCancel);
/**
* Returns true if the dialog can be canceled, false otherwise
*/
bool allowCancel() const;
/**
* Sets whether the cancel button is visible. setAllowCancel(false)
* implies showCancelButton(false)
*
* The default is true.
*
* @param show Whether or not the cancel button should be shown
*/
void showCancelButton(bool show);
/**
* Sets whether the dialog should close automagically when
* all the steps in the QProgressBar have been completed.
*
* The default is true.
*/
void setAutoClose(bool close);
/**
* Returns true if the dialog will close upon completion,
* or false otherwise
*/
bool autoClose() const;
/**
* Sets whether the dialog should reset the QProgressBar dialog
* back to 0 steps compelete when all steps have been completed.
* This is useful for KProgressDialogs that will be reused.
*
* The default is false.
*/
void setAutoReset(bool autoReset);
/**
* Returns true if the QProgressBar widget will be reset
* upon completion, or false otherwise
*/
bool autoReset() const;
/**
* Returns true if the dialog was closed or canceled
* before completion. If the dialog is not cancellable
* it will always return false.
*/
bool wasCancelled() const;
/**
* Ignores the last cancel action if the cancel button was
* pressed. Useful for kdialog when combined with a KMessageBox
* to display a message like "Are you sure you want to cancel?"
*/
void ignoreCancel();
/**
* Sets the text to appear on the cancel button.
*/
void setButtonText(const QString &text);
/**
* Returns the text on the cancel button
*/
QString buttonText() const;
/**
* Set the minimum number of milliseconds to wait before
* actually showing the dialog.
*
* If the expected duration of the task is less than the minimumDuration, the dialog will
* not appear at all. This prevents the dialog popping up for tasks that are quickly over.
* For tasks that are expected to exceed the minimumDuration, the dialog will pop up after
* the minimumDuration time.
* If set to 0, the dialog is always shown immediately. The default is
* 2000 milliseconds.
*/
void setMinimumDuration(int ms);
/**
* Returns the time that must pass before the dialog appears.
* @see setMinimumDuration
*/
int minimumDuration() const;
virtual void reject();
protected:
virtual void showEvent(QShowEvent *event);
private:
Q_PRIVATE_SLOT(d, void slotAutoShow())
Q_PRIVATE_SLOT(d, void slotAutoActions(int percentage))
private:
class KProgressDialogPrivate;
friend class KProgressDialogPrivate;
KProgressDialogPrivate *const d;
Q_DISABLE_COPY(KProgressDialog)
};
#endif
diff --git a/staging/kde4support/src/kdeui/ksplashscreen.cpp b/staging/kde4support/src/kdeui/ksplashscreen.cpp
index 54be3ae1ee..a3ecb6eb11 100644
--- a/staging/kde4support/src/kdeui/ksplashscreen.cpp
+++ b/staging/kde4support/src/kdeui/ksplashscreen.cpp
@@ -1,42 +1,42 @@
/* This file is part of the KDE libraries
Copyright (C) 2003 Chris Howells (howells@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.
*/
#include "ksplashscreen.h"
#include <kconfig.h>
#include <kglobalsettings.h>
#include <QPixmap>
-KSplashScreen::KSplashScreen(const QPixmap &pixmap, Qt::WFlags f)
+KSplashScreen::KSplashScreen(const QPixmap &pixmap, Qt::WindowFlags f)
: QSplashScreen( pixmap, f ),
d( 0 )
{
QRect desk = KGlobalSettings::splashScreenDesktopGeometry();
resize(pixmap.width(), pixmap.height());
setGeometry( ( desk.width() / 2 ) - ( width() / 2 ) + desk.left(),
( desk.height() / 2 ) - ( height() / 2 ) + desk.top(),
width(), height() );
}
KSplashScreen::~KSplashScreen()
{
}
diff --git a/staging/kde4support/src/kdeui/ksplashscreen.h b/staging/kde4support/src/kdeui/ksplashscreen.h
index f9c3abf74a..fa7b37d848 100644
--- a/staging/kde4support/src/kdeui/ksplashscreen.h
+++ b/staging/kde4support/src/kdeui/ksplashscreen.h
@@ -1,67 +1,67 @@
/* This file is part of the KDE libraries
Copyright (C) 2003 Chris Howells (howells@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 KSPLASHSCREEN_H
#define KSPLASHSCREEN_H
#include <kde4support_export.h>
#include <QSplashScreen>
class QPixmap;
/**
* @short %KDE splash screen
*
* This class is based on QSplashScreen and exists solely to make
* splash screens obey KDE's Xinerama settings.
*
* For documentation on how to use the class, see the documentation
* for QSplashScreen.
*
* \image html ksplashscreen.png "KDE Splash Screen"
*
* @author Chris Howells (howells@kde.org)
*/
class KDE4SUPPORT_EXPORT KSplashScreen : public QSplashScreen //krazy:exclude=qclasses
{
Q_OBJECT
public:
/**
* Constructs a splash screen.
*/
- explicit KSplashScreen(const QPixmap &pixmap, Qt::WFlags f = 0);
+ explicit KSplashScreen(const QPixmap &pixmap, Qt::WindowFlags f = 0);
/**
* Destructor.
*
* Deletes all internal objects.
*/
~KSplashScreen();
private:
class Private;
Private* const d;
Q_DISABLE_COPY( KSplashScreen )
};
#endif //KSPLASHSCREEN_H
diff --git a/staging/kguiaddons/src/colors/kcolorcollection.cpp b/staging/kguiaddons/src/colors/kcolorcollection.cpp
index 9ea2e3e4b1..d3755874a5 100644
--- a/staging/kguiaddons/src/colors/kcolorcollection.cpp
+++ b/staging/kguiaddons/src/colors/kcolorcollection.cpp
@@ -1,270 +1,270 @@
/* This file is part of the KDE libraries
Copyright (C) 1999 Waldo Bastian (bastian@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.
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.
*/
//-----------------------------------------------------------------------------
// KDE color collection
#include "kcolorcollection.h"
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QTextStream>
#include <qsavefile.h>
#include <qstandardpaths.h>
#include <kstandarddirs.h>
#include <kglobal.h>
//BEGIN KColorCollectionPrivate
class KColorCollectionPrivate
{
public:
KColorCollectionPrivate(const QString&);
KColorCollectionPrivate(const KColorCollectionPrivate&);
~KColorCollectionPrivate() {}
struct ColorNode
{
ColorNode(const QColor &c, const QString &n)
: color(c), name(n) {}
QColor color;
QString name;
};
QList<ColorNode> colorList;
QString name;
QString desc;
KColorCollection::Editable editable;
};
KColorCollectionPrivate::KColorCollectionPrivate(const QString &_name)
: name(_name)
{
if (name.isEmpty()) return;
QString filename = QStandardPaths::locate(QStandardPaths::ConfigLocation, "colors/"+name);
if (filename.isEmpty()) return;
QFile paletteFile(filename);
if (!paletteFile.exists()) return;
if (!paletteFile.open(QIODevice::ReadOnly)) return;
// Read first line
// Expected "GIMP Palette"
QString line = QString::fromLocal8Bit(paletteFile.readLine());
if (line.indexOf(" Palette") == -1) return;
while( !paletteFile.atEnd() )
{
line = QString::fromLocal8Bit(paletteFile.readLine());
if (line[0] == '#')
{
// This is a comment line
line = line.mid(1); // Strip '#'
line = line.trimmed(); // Strip remaining white space..
if (!line.isEmpty())
{
desc += line+'\n'; // Add comment to description
}
}
else
{
// This is a color line, hopefully
line = line.trimmed();
if (line.isEmpty()) continue;
int r, g, b;
int pos = 0;
- if (sscanf(line.toAscii(), "%d %d %d%n", &r, &g, &b, &pos) >= 3)
+ if (sscanf(line.toLatin1(), "%d %d %d%n", &r, &g, &b, &pos) >= 3)
{
r = qBound(0, r, 255);
g = qBound(0, g, 255);
b = qBound(0, b, 255);
QString name = line.mid(pos).trimmed();
colorList.append(ColorNode(QColor(r, g, b), name));
}
}
}
}
KColorCollectionPrivate::KColorCollectionPrivate(const KColorCollectionPrivate& p)
: colorList(p.colorList), name(p.name), desc(p.desc), editable(p.editable)
{
}
//END KColorCollectionPrivate
QStringList KColorCollection::installedCollections()
{
QStringList paletteDirs = QStandardPaths::locateAll(QStandardPaths::ConfigLocation, "colors", QStandardPaths::LocateDirectory);
QStringList paletteList;
Q_FOREACH(const QString& dir, paletteDirs) {
paletteList += QDir(dir).entryList(QDir::NoDotAndDotDot);
}
paletteList.removeDuplicates();
return paletteList;
}
KColorCollection::KColorCollection(const QString &name)
{
d = new KColorCollectionPrivate(name);
}
KColorCollection::KColorCollection(const KColorCollection &p)
{
d = new KColorCollectionPrivate(*p.d);
}
KColorCollection::~KColorCollection()
{
// Need auto-save?
delete d;
}
bool
KColorCollection::save()
{
QString filename = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + "colors/" + d->name;
QSaveFile sf(filename);
if (!sf.open(QIODevice::WriteOnly)) return false;
QTextStream str ( &sf );
QString description = d->desc.trimmed();
description = '#'+description.split( '\n', QString::KeepEmptyParts).join("\n#");
str << "KDE RGB Palette\n";
str << description << "\n";
foreach (const KColorCollectionPrivate::ColorNode &node, d->colorList)
{
int r,g,b;
node.color.getRgb(&r, &g, &b);
str << r << " " << g << " " << b << " " << node.name << "\n";
}
return sf.commit();
}
QString KColorCollection::description() const
{
return d->desc;
}
void KColorCollection::setDescription(const QString &desc)
{
d->desc = desc;
}
QString KColorCollection::name() const
{
return d->name;
}
void KColorCollection::setName(const QString &name)
{
d->name = name;
}
KColorCollection::Editable KColorCollection::editable() const
{
return d->editable;
}
void KColorCollection::setEditable(Editable editable)
{
d->editable = editable;
}
int KColorCollection::count() const
{
return (int) d->colorList.count();
}
KColorCollection&
KColorCollection::operator=( const KColorCollection &p)
{
if (&p == this) return *this;
d->colorList = p.d->colorList;
d->name = p.d->name;
d->desc = p.d->desc;
d->editable = p.d->editable;
return *this;
}
QColor
KColorCollection::color(int index) const
{
if ((index < 0) || (index >= count()))
return QColor();
return d->colorList[index].color;
}
int
KColorCollection::findColor(const QColor &color) const
{
for (int i = 0; i < d->colorList.size(); ++i)
{
if (d->colorList[i].color == color)
return i;
}
return -1;
}
QString
KColorCollection::name(int index) const
{
if ((index < 0) || (index >= count()))
return QString();
return d->colorList[index].name;
}
QString KColorCollection::name(const QColor &color) const
{
return name(findColor(color));
}
int
KColorCollection::addColor(const QColor &newColor, const QString &newColorName)
{
d->colorList.append(KColorCollectionPrivate::ColorNode(newColor, newColorName));
return count() - 1;
}
int
KColorCollection::changeColor(int index,
const QColor &newColor,
const QString &newColorName)
{
if ((index < 0) || (index >= count()))
return -1;
KColorCollectionPrivate::ColorNode& node = d->colorList[index];
node.color = newColor;
node.name = newColorName;
return index;
}
int KColorCollection::changeColor(const QColor &oldColor,
const QColor &newColor,
const QString &newColorName)
{
return changeColor( findColor(oldColor), newColor, newColorName);
}
diff --git a/staging/kservice/src/services/kmimetypetrader.h b/staging/kservice/src/services/kmimetypetrader.h
index 108b66cf14..323107921f 100644
--- a/staging/kservice/src/services/kmimetypetrader.h
+++ b/staging/kservice/src/services/kmimetypetrader.h
@@ -1,196 +1,196 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Torben Weis <weis@kde.org>
Copyright (C) 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 version 2 as published by the Free Software Foundation.
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 KMIMETYPETRADER_H
#define KMIMETYPETRADER_H
#include <kservice.h>
class KServiceOffer;
/**
* KDE's trader for services associated to a given mimetype.
*
* Example: say that you want to the list of all KParts components that can handle HTML.
* Our code would look like:
* \code
* KService::List lst = KMimeTypeTrader::self()->query("text/html",
* "KParts/ReadOnlyPart");
* \endcode
*
* If you want to get the preferred KParts component for text/html you could use
* preferredService("text/html", "KParts/ReadOnlyPart"), although if this is about
* loading that component you would use createPartInstanceFromQuery directly.
*
* @see KServiceTypeTrader, KService
*/
class KSERVICE_EXPORT KMimeTypeTrader
{
public:
/**
* Standard destructor
*/
~KMimeTypeTrader();
/**
* This method returns a list of services which are associated with a given mimetype.
*
* Example usage:
* To get list of applications that can handle a given mimetype,
* set @p genericServiceType to "Application" (which is the default).
* To get list of embeddable components that can handle a given mimetype,
* set @p genericServiceType to "KParts/ReadOnlyPart".
*
* The constraint parameter is used to limit the possible choices
* returned based on the constraints you give it.
*
* The @p constraint language is rather full. The most common
* keywords are AND, OR, NOT, IN, and EXIST, all used in an
* almost spoken-word form. An example is:
* \code
* (Type == 'Service') and (('Browser/View' in ServiceTypes) and (exist Library))
* \endcode
*
* The keys used in the query (Type, ServiceTypes, Library) are all
* fields found in the .desktop files.
*
* @param mimeType A mime type like 'text/plain' or 'text/html'.
* @param genericServiceType a basic service type, like 'KParts/ReadOnlyPart' or 'Application'
* @param constraint A constraint to limit the choices returned, QString() to
* get all services that can handle the given @p mimetype
*
* @return A list of services that satisfy the query, sorted by preference
* (preferred service first)
* @see http://techbase.kde.org/Development/Tutorials/Services/Traders#The_KTrader_Query_Language
*/
KService::List query( const QString& mimeType,
const QString& genericServiceType = QString::fromLatin1("Application"),
const QString& constraint = QString() ) const;
/**
* Returns the preferred service for @p mimeType and @p genericServiceType
*
* This is almost like query().first(), except that it also checks
* if the service is allowed as a preferred service (see KService::allowAsDefault).
*
* @param mimeType the mime type (see query())
* @param genericServiceType the service type (see query())
* @return the preferred service, or 0 if no service is available
*/
KService::Ptr preferredService( const QString & mimeType, const QString & genericServiceType = QString::fromLatin1("Application") );
/**
* This method creates and returns a part object from the trader query for a given \p mimeType.
*
* Example:
* \code
* KParts::ReadOnlyPart* part = KMimeTypeTrader::createInstanceFromQuery<KParts::ReadOnlyPart>("text/plain", parentWidget, parentObject);
* if (part) {
* part->openUrl(url);
* part->widget()->show(); // also insert the widget into a layout
* }
* \endcode
*
* @param mimeType the mimetype which this part is associated with
* @param parentWidget the parent widget, will be set as the parent of the part's widget
* @param parent the parent object for the part itself
* @param constraint an optional constraint to pass to the trader
* @param args A list of arguments passed to the service component
* @param error The string passed here will contain an error description.
* @return A pointer to the newly created object or a null pointer if the
* factory was unable to create an object of the given type.
*/
template <class T>
static T *createPartInstanceFromQuery(const QString &mimeType, QWidget *parentWidget = 0, QObject *parent = 0,
const QString &constraint = QString(),
const QVariantList &args = QVariantList(),
QString *error = 0)
{
- const KService::List offers = self()->query(mimeType, QString::fromAscii("KParts/ReadOnlyPart"), constraint);
+ const KService::List offers = self()->query(mimeType, QString::fromLatin1("KParts/ReadOnlyPart"), constraint);
Q_FOREACH (const KService::Ptr &ptr, offers) {
T *component = ptr->template createInstance<T>(parentWidget, parent, args, error);
if (component) {
if (error)
error->clear();
return component;
}
}
if (error)
*error = QCoreApplication::translate("", "No service matching the requirements was found");
return 0;
}
/**
* This can be used to create a service instance from a mime type query
*
* @param mimeType A mime type like 'text/plain' or 'text/html'.
* @param serviceType a basic service type
* @param parent the parent object for the plugin itself
* @param constraint A constraint to limit the choices returned, QString() to
* get all services that can handle the given @p mimetype
* @param args A list of arguments passed to the service component
* @param error The string passed here will contain an error description.
* @return A pointer to the newly created object or a null pointer if the
* factory was unable to create an object of the given type.
*/
template <class T>
static T *createInstanceFromQuery(const QString &mimeType, const QString &serviceType, QObject *parent = 0,
const QString &constraint = QString(),
const QVariantList &args = QVariantList(),
QString *error = 0)
{
const KService::List offers = self()->query(mimeType, serviceType, constraint);
Q_FOREACH (const KService::Ptr &ptr, offers) {
T *component = ptr->template createInstance<T>(parent, args, error);
if (component) {
if (error)
error->clear();
return component;
}
}
if (error)
*error = QCoreApplication::translate("", "No service matching the requirements was found");
return 0;
}
/**
* This is a static pointer to the KMimeTypeTrader singleton.
*
* You will need to use this to access the KMimeTypeTrader functionality since the
* constructors are protected.
*
* @return Static KMimeTypeTrader instance
*/
static KMimeTypeTrader* self();
private:
/**
* @internal
*/
KMimeTypeTrader();
private:
class Private;
Private * const d;
friend class KMimeTypeTraderSingleton;
};
#endif /* KMIMETYPETRADER_H */
diff --git a/staging/kservice/src/sycoca/kmemfile.cpp b/staging/kservice/src/sycoca/kmemfile.cpp
index 8e64ecee9a..6f9830d139 100644
--- a/staging/kservice/src/sycoca/kmemfile.cpp
+++ b/staging/kservice/src/sycoca/kmemfile.cpp
@@ -1,252 +1,252 @@
/*
This file is part of the KDE libraries
Copyright (C) 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de>
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 "kmemfile_p.h"
#ifndef QT_NO_SHAREDMEMORY
#include <QtCore/QSharedMemory>
#include <QtCore/QCryptographicHash>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QCoreApplication>
class KMemFile::Private
{
public:
struct sharedInfoData {
int shmCounter;
qint64 shmDataSize;
sharedInfoData() {
memset ( this, 0, sizeof ( *this ) );
}
};
Private ( KMemFile *_parent ) : readWritePos ( 0 ), shmDataSize ( 0 ), parent ( _parent ) {}
QString getShmKey ( int iCounter = -1 );
static QString getShmKey ( const QString &filename, int iCounter = -1 );
bool loadContentsFromFile();
void close();
QString filename;
QSharedMemory shmInfo;
QSharedMemory shmData;
qint64 readWritePos;
qint64 shmDataSize;
KMemFile *parent;
};
QString KMemFile::Private::getShmKey ( int iCounter )
{
return getShmKey ( filename, iCounter );
}
QString KMemFile::Private::getShmKey ( const QString &filename, int iCounter )
{
QByteArray tmp = QString ( QDir ( filename ).canonicalPath() + QString::number ( iCounter ) ).toUtf8();
- return QString::fromAscii ( QCryptographicHash::hash ( tmp, QCryptographicHash::Sha1 ) );
+ return QString::fromLatin1 ( QCryptographicHash::hash ( tmp, QCryptographicHash::Sha1 ) );
}
bool KMemFile::Private::loadContentsFromFile()
{
QFile f ( filename );
if ( !f.exists() ) {
close();
parent->setErrorString(QCoreApplication::translate("", "File %1 does not exist").arg(filename));
return false;
}
if ( !f.open ( QIODevice::ReadOnly ) ) {
close();
parent->setErrorString(QCoreApplication::translate("", "Cannot open %1 for reading").arg(filename));
return false;
}
sharedInfoData *infoPtr = static_cast<sharedInfoData*> ( shmInfo.data() );
infoPtr->shmDataSize = f.size();
shmData.setKey ( getShmKey ( infoPtr->shmCounter ) );
if ( !shmData.create ( infoPtr->shmDataSize ) ) {
close();
parent->setErrorString(QCoreApplication::translate("", "Cannot create memory segment for file %1").arg(filename));
return false;
}
shmData.lock();
qint64 size = 0;
qint64 bytesRead;
char *data = static_cast<char*> ( shmData.data() );
bytesRead = f.read ( data, infoPtr->shmDataSize );
if ( bytesRead != infoPtr->shmDataSize ) {
close();
parent->setErrorString(QCoreApplication::translate("", "Could not read data from %1 into shm").arg(filename));
return false;
}
shmDataSize = size;
shmData.unlock();
return true;
}
void KMemFile::Private::close()
{
shmData.unlock();
shmData.detach();
shmInfo.unlock();
shmInfo.detach();
readWritePos = 0;
shmDataSize = 0;
}
KMemFile::KMemFile ( const QString &filename, QObject *parent )
: QIODevice ( parent ), d ( new Private ( this ) )
{
d->filename = filename;
}
KMemFile::~KMemFile()
{
close();
delete d;
}
void KMemFile::close ()
{
QIODevice::close();
if ( !isOpen() )
return;
d->close();
}
bool KMemFile::isSequential () const
{
return false;
}
bool KMemFile::open ( OpenMode mode )
{
if ( isOpen() ) {
QIODevice::open ( mode );
return false;
}
if ( mode != QIODevice::ReadOnly ) {
setErrorString(QCoreApplication::translate("", "Only 'ReadOnly' allowed"));
return false;
}
if ( !QFile::exists ( d->filename ) ) {
setErrorString(QCoreApplication::translate("", "File %1 does not exist").arg(d->filename));
return false;
}
QSharedMemory lock ( QDir ( d->filename ).canonicalPath() );
lock.lock();
Private::sharedInfoData *infoPtr;
d->shmInfo.setKey ( d->getShmKey() );
// see if it's already in memory
if ( !d->shmInfo.attach ( QSharedMemory::ReadWrite ) ) {
if ( !d->shmInfo.create ( sizeof ( Private::sharedInfoData ) ) ) {
lock.unlock();
setErrorString(QCoreApplication::translate("", "Cannot create memory segment for file %1").arg(d->filename));
return false;
}
d->shmInfo.lock();
// no -> create it
infoPtr = static_cast<Private::sharedInfoData*> ( d->shmInfo.data() );
memset ( infoPtr, 0, sizeof ( Private::sharedInfoData ) );
infoPtr->shmCounter = 1;
if ( !d->loadContentsFromFile() ) {
d->shmInfo.unlock();
d->shmInfo.detach();
lock.unlock();
return false;
}
} else {
d->shmInfo.lock();
infoPtr = static_cast<Private::sharedInfoData*> ( d->shmInfo.data() );
d->shmData.setKey ( d->getShmKey ( infoPtr->shmCounter ) );
if ( !d->shmData.attach ( QSharedMemory::ReadOnly ) ) {
if ( !d->loadContentsFromFile() ) {
d->shmInfo.unlock();
d->shmInfo.detach();
lock.unlock();
return false;
}
}
}
d->shmDataSize = infoPtr->shmDataSize;
d->shmInfo.unlock();
lock.unlock();
setOpenMode ( mode );
return true;
}
bool KMemFile::seek ( qint64 pos )
{
if ( d->shmDataSize < pos ) {
setErrorString ( QCoreApplication::translate("", "Cannot seek past eof" ) );
return false;
}
d->readWritePos = pos;
QIODevice::seek ( pos );
return true;
}
qint64 KMemFile::size () const
{
return d->shmDataSize;
}
qint64 KMemFile::readData ( char * data, qint64 maxSize )
{
if ( ( openMode() & QIODevice::ReadOnly ) == 0 )
return -1;
qint64 maxRead = size() - d->readWritePos;
qint64 bytesToRead = qMin ( maxRead, maxSize );
const char *src = static_cast<const char*> ( d->shmData.data() );
memcpy ( data, &src[d->readWritePos], bytesToRead );
d->readWritePos += bytesToRead;
return bytesToRead;
}
qint64 KMemFile::writeData ( const char *, qint64 )
{
return -1;
}
void KMemFile::fileContentsChanged ( const QString &filename )
{
QSharedMemory lock ( QDir ( filename ).canonicalPath() );
lock.lock();
QSharedMemory shmData ( Private::getShmKey ( filename ) );
if ( !shmData.attach() )
return;
shmData.lock();
Private::sharedInfoData *infoPtr = static_cast<Private::sharedInfoData*> ( shmData.data() );
infoPtr->shmCounter++;
infoPtr->shmDataSize = 0;
shmData.unlock();
}
#endif //QT_NO_SHAREDMEMORY
diff --git a/staging/kwidgets/src/dialogs/kdialog.cpp b/staging/kwidgets/src/dialogs/kdialog.cpp
index c904ce6033..810ec135a7 100644
--- a/staging/kwidgets/src/dialogs/kdialog.cpp
+++ b/staging/kwidgets/src/dialogs/kdialog.cpp
@@ -1,1041 +1,1041 @@
/* This file is part of the KDE Libraries
* Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net)
* Additions 1999-2000 by Espen Sand (espen@kde.org)
* by Holger Freyther <freyther@kde.org>
* 2005-2009 by Olivier Goffart (ogoffart at 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.
*/
#include "kdialog.h"
#include "kdialog_p.h"
#include <kglobal.h> // remove KGlobal::caption once done by QPA
#include <QApplication>
#include <QDesktopWidget>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QHideEvent>
#include <QPointer>
#include <QStyle>
#include <QTimer>
#include <QVBoxLayout>
#include <QWhatsThis>
#include <QDebug>
#include <kconfig.h>
#include <klocalizedstring.h>
#include <kpushbutton.h>
#include <kseparator.h>
#include <kstandardguiitem.h>
#include <khelpclient.h>
#include <kurllabel.h>
#include <kwindowconfig.h>
#include <config-kwidgets.h>
#if HAVE_X11
#include <qx11info_x11.h>
#include <netwm.h>
#endif
static bool sAllowEmbeddingInGraphicsView = false;
void KDialogPrivate::setupLayout()
{
Q_Q(KDialog);
if (!dirty) {
QMetaObject::invokeMethod( q, "queuedLayoutUpdate", Qt::QueuedConnection );
dirty = true;
}
}
void KDialogPrivate::queuedLayoutUpdate()
{
if (!dirty)
return;
dirty = false;
Q_Q(KDialog);
// Don't lose the focus widget when re-creating the layout.
// Testcase: KOrganizer's "Select Categories" dialog
QPointer<QWidget> focusWidget = mMainWidget ? mMainWidget->focusWidget() : 0;
if (q->layout() && q->layout() != mTopLayout) {
qWarning() << q->metaObject()->className() << "created with a layout; don't do that, KDialog takes care of it, use mainWidget or setMainWidget instead";
delete q->layout();
}
delete mTopLayout;
if ( mButtonOrientation == Qt::Horizontal )
mTopLayout = new QVBoxLayout(q);
else
mTopLayout = new QHBoxLayout(q);
if ( mUrlHelp )
mTopLayout->addWidget( mUrlHelp, 0, Qt::AlignRight );
if ( mMainWidget )
mTopLayout->addWidget( mMainWidget, 10 );
if ( mDetailsWidget )
mTopLayout->addWidget( mDetailsWidget );
if ( mActionSeparator )
mTopLayout->addWidget( mActionSeparator );
if ( mButtonBox ) {
mButtonBox->setOrientation( mButtonOrientation );
mTopLayout->addWidget( mButtonBox );
}
if (focusWidget) {
focusWidget->setFocus();
}
}
void KDialogPrivate::appendButton(KDialog::ButtonCode key, const KGuiItem &item)
{
Q_Q(KDialog);
QDialogButtonBox::ButtonRole role = QDialogButtonBox::InvalidRole;
switch ( key ) {
case KDialog::Help:
case KDialog::Details:
role = QDialogButtonBox::HelpRole;
break;
case KDialog::Default:
case KDialog::Reset:
role = QDialogButtonBox::ResetRole;
break;
case KDialog::Ok:
role = QDialogButtonBox::AcceptRole;
break;
case KDialog::Apply:
role = QDialogButtonBox::ApplyRole;
break;
case KDialog::Try:
case KDialog::Yes:
role = QDialogButtonBox::YesRole;
break;
case KDialog::Close:
case KDialog::Cancel:
role = QDialogButtonBox::RejectRole;
break;
case KDialog::No:
role = QDialogButtonBox::NoRole;
break;
case KDialog::User1:
case KDialog::User2:
case KDialog::User3:
role = QDialogButtonBox::ActionRole;
break;
default:
role = QDialogButtonBox::InvalidRole;
break;
}
if ( role == QDialogButtonBox::InvalidRole )
return;
KPushButton *button = new KPushButton;
KGuiItem::assign(button, item);
mButtonBox->addButton( button, role );
mButtonList.insert( key, button );
mButtonSignalMapper.setMapping( button, key );
QObject::connect(button, SIGNAL(clicked()),
&mButtonSignalMapper, SLOT(map()) );
if (key == mDefaultButton) {
// Now that it exists, set it as default
q->setDefaultButton(mDefaultButton);
}
}
void KDialogPrivate::init(KDialog *q)
{
q_ptr = q;
dirty = false;
q->setButtons(KDialog::Ok | KDialog::Cancel);
q->setDefaultButton(KDialog::Ok);
q->connect(&mButtonSignalMapper, SIGNAL(mapped(int)), q, SLOT(slotButtonClicked(int)));
q->setPlainCaption(KGlobal::caption()); // set appropriate initial window title for case it gets not set later
}
void KDialogPrivate::helpLinkClicked()
{
q_ptr->slotButtonClicked(KDialog::Help);
}
-KDialog::KDialog( QWidget *parent, Qt::WFlags flags )
+KDialog::KDialog( QWidget *parent, Qt::WindowFlags flags )
: QDialog(parent, sAllowEmbeddingInGraphicsView ? flags : flags | Qt::BypassGraphicsProxyWidget ), d_ptr(new KDialogPrivate)
{
d_ptr->init(this);
}
-KDialog::KDialog(KDialogPrivate &dd, QWidget *parent, Qt::WFlags flags)
+KDialog::KDialog(KDialogPrivate &dd, QWidget *parent, Qt::WindowFlags flags)
: QDialog(parent, sAllowEmbeddingInGraphicsView ? flags : flags | Qt::BypassGraphicsProxyWidget), d_ptr(&dd)
{
d_ptr->init(this);
}
KDialog::~KDialog()
{
delete d_ptr;
}
void KDialog::setButtons( ButtonCodes buttonMask )
{
Q_D(KDialog);
if ( d->mButtonBox ) {
d->mButtonList.clear();
delete d->mButtonBox;
d->mButtonBox = 0;
}
if ( buttonMask & Cancel )
buttonMask &= ~Close;
if ( buttonMask & Apply )
buttonMask &= ~Try;
if ( buttonMask & Details )
buttonMask &= ~Default;
if ( buttonMask == None ) {
d->setupLayout();
return; // When we want no button box
}
d->mEscapeButton = (buttonMask & Cancel) ? Cancel : Close;
d->mButtonBox = new QDialogButtonBox( this );
if ( buttonMask & Help )
d->appendButton( Help, KStandardGuiItem::help() );
if ( buttonMask & Default )
d->appendButton( Default, KStandardGuiItem::defaults() );
if ( buttonMask & Reset )
d->appendButton( Reset, KStandardGuiItem::reset() );
if ( buttonMask & User3 )
d->appendButton( User3, KGuiItem() );
if ( buttonMask & User2 )
d->appendButton( User2, KGuiItem() );
if ( buttonMask & User1 )
d->appendButton( User1, KGuiItem() );
if ( buttonMask & Ok )
d->appendButton( Ok, KStandardGuiItem::ok() );
if ( buttonMask & Apply )
d->appendButton( Apply, KStandardGuiItem::apply() );
if ( buttonMask & Try )
d->appendButton( Try, KGuiItem(i18n( "&Try" )) );
if ( buttonMask & Cancel )
d->appendButton( Cancel, KStandardGuiItem::cancel() );
if ( buttonMask & Close )
d->appendButton( Close, KStandardGuiItem::close() );
if ( buttonMask & Yes )
d->appendButton( Yes, KStandardGuiItem::yes() );
if ( buttonMask & No )
d->appendButton( No, KStandardGuiItem::no() );
if ( buttonMask & Details ) {
d->appendButton( Details, KGuiItem(QString(), "help-about") );
setDetailsWidgetVisible( false );
}
d->setupLayout();
}
void KDialog::setButtonsOrientation( Qt::Orientation orientation )
{
Q_D(KDialog);
if ( d->mButtonOrientation != orientation ) {
d->mButtonOrientation = orientation;
if ( d->mActionSeparator )
d->mActionSeparator->setOrientation( d->mButtonOrientation );
if ( d->mButtonOrientation == Qt::Vertical )
enableLinkedHelp( false ); // 2000-06-18 Espen: No support for this yet.
}
}
void KDialog::setEscapeButton( ButtonCode id )
{
d_func()->mEscapeButton = id;
}
void KDialog::setDefaultButton( ButtonCode newDefaultButton )
{
Q_D(KDialog);
if (newDefaultButton == None)
newDefaultButton = NoDefault; // #148969
const KDialog::ButtonCode oldDefault = defaultButton();
bool oldDefaultHadFocus = false;
if (oldDefault != NoDefault) {
QPushButton *old = button(oldDefault);
if (old) {
oldDefaultHadFocus = (focusWidget() == old);
old->setDefault(false);
}
}
if (newDefaultButton != NoDefault) {
QPushButton *b = button(newDefaultButton);
if (b) {
b->setDefault(true);
if (focusWidget() == 0 || oldDefaultHadFocus) {
// No widget had focus yet, or the old default button had
// -> ok to give focus to the new default button, so that it's
// really default (Enter triggers it).
// But we don't do this if the kdialog user gave focus to a
// specific widget in the dialog.
b->setFocus();
}
}
}
d->mDefaultButton = newDefaultButton;
Q_ASSERT(defaultButton() == newDefaultButton);
}
KDialog::ButtonCode KDialog::defaultButton() const
{
Q_D(const KDialog);
QHashIterator<int, KPushButton*> it( d->mButtonList );
while ( it.hasNext() ) {
it.next();
if (it.value()->isDefault()) {
return (ButtonCode)it.key();
}
}
return d->mDefaultButton;
}
void KDialog::setMainWidget( QWidget *widget )
{
Q_D(KDialog);
if ( d->mMainWidget == widget )
return;
d->mMainWidget = widget;
if (d->mMainWidget && d->mMainWidget->layout()) {
// Avoid double-margin problem
d->mMainWidget->layout()->setMargin(0);
}
d->setupLayout();
}
QWidget *KDialog::mainWidget()
{
Q_D(KDialog);
if (!d->mMainWidget)
setMainWidget( new QWidget(this) );
return d->mMainWidget;
}
QSize KDialog::sizeHint() const
{
Q_D(const KDialog);
if (!d->mMinSize.isEmpty())
return d->mMinSize.expandedTo( minimumSizeHint() ) + d->mIncSize;
else {
if (d->dirty)
const_cast<KDialogPrivate*>(d)->queuedLayoutUpdate();
return QDialog::sizeHint() + d->mIncSize;
}
}
QSize KDialog::minimumSizeHint() const
{
Q_D(const KDialog);
if (d->dirty)
const_cast<KDialogPrivate*>(d)->queuedLayoutUpdate();
return QDialog::minimumSizeHint() + d->mIncSize;
}
//
// Grab QDialogs keypresses if non-modal.
//
void KDialog::keyPressEvent( QKeyEvent *event )
{
Q_D(KDialog);
if ( event->modifiers() == 0 ) {
if ( event->key() == Qt::Key_F1 ) {
QPushButton *button = this->button( Help );
if ( button ) {
button->animateClick();
event->accept();
return;
}
}
if ( event->key() == Qt::Key_Escape ) {
QPushButton *button = this->button( d->mEscapeButton );
if ( button ) {
button->animateClick();
event->accept();
return;
}
}
} else if ( event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier ) {
QWhatsThis::enterWhatsThisMode();
event->accept();
return;
} else if ( event->modifiers() == Qt::ControlModifier &&
( event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter ) ) {
// accept the dialog when Ctrl-Return is pressed
QPushButton *button = this->button( Ok );
if ( button ) {
button->animateClick();
event->accept();
return;
}
}
QDialog::keyPressEvent( event );
}
int KDialog::marginHint()
{
return QApplication::style()->pixelMetric( QStyle::PM_DefaultChildMargin );
}
int KDialog::spacingHint()
{
return QApplication::style()->pixelMetric( QStyle::PM_DefaultLayoutSpacing );
}
int KDialog::groupSpacingHint()
{
return QApplication::fontMetrics().lineSpacing();
}
QString KDialog::makeStandardCaption( const QString &userCaption,
QWidget* window,
CaptionFlags flags )
{
Q_UNUSED(window);
QString caption = KGlobal::caption();
QString captionString = userCaption.isEmpty() ? caption : userCaption;
// If the document is modified, add '[modified]'.
if (flags & ModifiedCaption)
captionString += QString::fromUtf8(" [") + i18n("modified") + QString::fromUtf8("]");
if ( !userCaption.isEmpty() ) {
// Add the application name if:
// User asked for it, it's not a duplication and the app name (caption()) is not empty
if ( flags & AppNameCaption &&
!caption.isEmpty() &&
!userCaption.endsWith(caption) ) {
// TODO: check to see if this is a transient/secondary window before trying to add the app name
// on platforms that need this
captionString += i18nc("Document/application separator in titlebar", " – ") + caption;
}
}
return captionString;
}
void KDialog::setCaption( const QString &_caption )
{
const QString caption = makeStandardCaption( _caption, this );
setPlainCaption( caption );
}
void KDialog::setCaption( const QString &caption, bool modified )
{
CaptionFlags flags = HIGCompliantCaption;
// ### Qt5 TODO: port to [*], see QWidget::setWindowFilePath
if ( modified )
{
flags |= ModifiedCaption;
}
setPlainCaption( makeStandardCaption(caption, this, flags) );
}
void KDialog::setPlainCaption( const QString &caption )
{
if (QWidget *win = window()) {
win->setWindowTitle( caption );
#if HAVE_X11
NETWinInfo info( QX11Info::display(), win->winId(), QX11Info::appRootWindow(), 0 );
info.setName( caption.toUtf8().constData() );
#endif
}
}
void KDialog::resizeLayout( QWidget *widget, int margin, int spacing ) //static
{
if ( widget->layout() )
resizeLayout( widget->layout(), margin, spacing );
if ( widget->children().count() > 0 ) {
const QList<QObject*> list = widget->children();
foreach ( QObject *object, list ) {
if ( object->isWidgetType() )
resizeLayout( (QWidget*)object, margin, spacing );
}
}
}
void KDialog::resizeLayout( QLayout *layout, int margin, int spacing ) //static
{
QLayoutItem *child;
int pos = 0;
while ( (child = layout->itemAt( pos ) ) ) {
if ( child->layout() )
resizeLayout( child->layout(), margin, spacing );
++pos;
}
if ( layout->layout() ) {
layout->layout()->setMargin( margin );
layout->layout()->setSpacing( spacing );
}
}
static QRect screenRect( QWidget *widget, int screen )
{
QDesktopWidget *desktop = QApplication::desktop();
KConfig gc( "kdeglobals", KConfig::NoGlobals );
KConfigGroup cg(&gc, "Windows" );
if ( desktop->isVirtualDesktop() &&
cg.readEntry( "XineramaEnabled", true ) &&
cg.readEntry( "XineramaPlacementEnabled", true ) ) {
if ( screen < 0 || screen >= desktop->numScreens() ) {
if ( screen == -1 )
screen = desktop->primaryScreen();
else if ( screen == -3 )
screen = desktop->screenNumber( QCursor::pos() );
else
screen = desktop->screenNumber( widget );
}
return desktop->availableGeometry( screen );
} else
return desktop->geometry();
}
void KDialog::centerOnScreen( QWidget *widget, int screen )
{
if ( !widget )
return;
#if HAVE_X11
if( !( widget->windowFlags() & Qt::X11BypassWindowManagerHint ) && widget->windowType() != Qt::Popup
&& NETRootInfo( QX11Info::display(), NET::Supported ).isSupported( NET::WM2FullPlacement )) {
return; // the WM can handle placement much better
}
#endif
QRect rect = screenRect( widget, screen );
widget->move( rect.center().x() - widget->width() / 2,
rect.center().y() - widget->height() / 2 );
}
bool KDialog::avoidArea( QWidget *widget, const QRect& area, int screen )
{
if ( !widget )
return false;
QRect fg = widget->frameGeometry();
if ( !fg.intersects( area ) )
return true; // nothing to do.
const QRect scr = screenRect( widget, screen );
QRect avoid( area ); // let's add some margin
avoid.translate( -5, -5 );
avoid.setRight( avoid.right() + 10 );
avoid.setBottom( avoid.bottom() + 10 );
if ( qMax( fg.top(), avoid.top() ) <= qMin( fg.bottom(), avoid.bottom() ) ) {
// We need to move the widget up or down
int spaceAbove = qMax( 0, avoid.top() - scr.top() );
int spaceBelow = qMax( 0, scr.bottom() - avoid.bottom() );
if ( spaceAbove > spaceBelow ) // where's the biggest side?
if ( fg.height() <= spaceAbove ) // big enough?
fg.setY( avoid.top() - fg.height() );
else
return false;
else
if ( fg.height() <= spaceBelow ) // big enough?
fg.setY( avoid.bottom() );
else
return false;
}
if ( qMax( fg.left(), avoid.left() ) <= qMin( fg.right(), avoid.right() ) ) {
// We need to move the widget left or right
const int spaceLeft = qMax( 0, avoid.left() - scr.left() );
const int spaceRight = qMax( 0, scr.right() - avoid.right() );
if ( spaceLeft > spaceRight ) // where's the biggest side?
if ( fg.width() <= spaceLeft ) // big enough?
fg.setX( avoid.left() - fg.width() );
else
return false;
else
if ( fg.width() <= spaceRight ) // big enough?
fg.setX( avoid.right() );
else
return false;
}
widget->move( fg.x(), fg.y() );
return true;
}
void KDialog::showButtonSeparator( bool state )
{
Q_D(KDialog);
if ( ( d->mActionSeparator != 0 ) == state )
return;
if ( state ) {
if ( d->mActionSeparator )
return;
d->mActionSeparator = new KSeparator( this );
d->mActionSeparator->setOrientation( d->mButtonOrientation );
} else {
delete d->mActionSeparator;
d->mActionSeparator = 0;
}
d->setupLayout();
}
void KDialog::setInitialSize( const QSize &size )
{
d_func()->mMinSize = size;
adjustSize();
}
void KDialog::incrementInitialSize( const QSize &size )
{
d_func()->mIncSize = size;
adjustSize();
}
QPushButton *KDialog::button( ButtonCode id ) const
{
Q_D(const KDialog);
return d->mButtonList.value( id, 0 );
}
void KDialog::enableButton( ButtonCode id, bool state )
{
QPushButton *button = this->button( id );
if ( button )
button->setEnabled( state );
}
bool KDialog::isButtonEnabled( ButtonCode id ) const
{
QPushButton *button = this->button( id );
if ( button )
return button->isEnabled();
return false;
}
void KDialog::enableButtonOk( bool state )
{
enableButton( Ok, state );
}
void KDialog::enableButtonApply( bool state )
{
enableButton( Apply, state );
}
void KDialog::enableButtonCancel( bool state )
{
enableButton( Cancel, state );
}
void KDialog::showButton( ButtonCode id, bool state )
{
QPushButton *button = this->button( id );
if ( button )
state ? button->show() : button->hide();
}
void KDialog::setButtonGuiItem( ButtonCode id, const KGuiItem &item )
{
QPushButton *button = this->button( id );
if ( !button )
return;
KGuiItem::assign(button, item);
}
void KDialog::setButtonMenu( ButtonCode id, QMenu *menu, ButtonPopupMode popupmode)
{
KPushButton *button = static_cast<KPushButton*>(this->button( id ));
if ( button ) {
if (popupmode==InstantPopup)
button->setMenu( menu );
else
button->setDelayedMenu(menu);
}
}
void KDialog::setButtonText( ButtonCode id, const QString &text )
{
Q_D(KDialog);
if ( !d->mSettingDetails && (id == Details) ) {
d->mDetailsButtonText = text;
setDetailsWidgetVisible( d->mDetailsVisible );
return;
}
QPushButton *button = this->button( id );
if ( button )
button->setText( text );
}
QString KDialog::buttonText( ButtonCode id ) const
{
QPushButton *button = this->button( id );
if ( button )
return button->text();
else
return QString();
}
void KDialog::setButtonIcon( ButtonCode id, const QIcon &icon )
{
QPushButton *button = this->button( id );
if ( button )
button->setIcon( icon );
}
QIcon KDialog::buttonIcon( ButtonCode id ) const
{
QPushButton *button = this->button( id );
if ( button )
return button->icon();
else
return QIcon();
}
void KDialog::setButtonToolTip( ButtonCode id, const QString &text )
{
QPushButton *button = this->button( id );
if ( button ) {
if ( text.isEmpty() )
button->setToolTip( QString() );
else
button->setToolTip( text );
}
}
QString KDialog::buttonToolTip( ButtonCode id ) const
{
QPushButton *button = this->button( id );
if ( button )
return button->toolTip();
else
return QString();
}
void KDialog::setButtonWhatsThis( ButtonCode id, const QString &text )
{
QPushButton *button = this->button( id );
if ( button ) {
if ( text.isEmpty() )
button->setWhatsThis( QString() );
else
button->setWhatsThis( text );
}
}
QString KDialog::buttonWhatsThis( ButtonCode id ) const
{
QPushButton *button = this->button( id );
if ( button )
return button->whatsThis();
else
return QString();
}
void KDialog::setButtonFocus( ButtonCode id )
{
QPushButton *button = this->button( id );
if ( button ) {
button->setFocus();
}
}
void KDialog::setDetailsWidget( QWidget *detailsWidget )
{
Q_D(KDialog);
if ( d->mDetailsWidget == detailsWidget )
return;
delete d->mDetailsWidget;
d->mDetailsWidget = detailsWidget;
if ( d->mDetailsWidget->parentWidget() != this )
d->mDetailsWidget->setParent( this );
d->mDetailsWidget->hide();
d->setupLayout();
if ( !d->mSettingDetails )
setDetailsWidgetVisible( d->mDetailsVisible );
}
bool KDialog::isDetailsWidgetVisible() const
{
return d_func()->mDetailsVisible;
}
void KDialog::setDetailsWidgetVisible( bool visible )
{
Q_D(KDialog);
if ( d->mDetailsButtonText.isEmpty() )
d->mDetailsButtonText = i18n( "&Details" );
d->mSettingDetails = true;
d->mDetailsVisible = visible;
if ( d->mDetailsVisible ) {
emit aboutToShowDetails();
setButtonText( Details, d->mDetailsButtonText + " <<" );
if ( d->mDetailsWidget ) {
if ( layout() )
layout()->setEnabled( false );
d->mDetailsWidget->show();
adjustSize();
if ( layout() ) {
layout()->activate();
layout()->setEnabled( true );
}
}
} else {
setButtonText( Details, d->mDetailsButtonText + " >>" );
if ( d->mDetailsWidget )
d->mDetailsWidget->hide();
if ( layout() ) {
layout()->activate();
adjustSize();
}
}
d->mSettingDetails = false;
}
void KDialog::delayedDestruct()
{
if ( isVisible() )
hide();
deleteLater();
}
void KDialog::slotButtonClicked( int button )
{
Q_D(KDialog);
emit buttonClicked( static_cast<KDialog::ButtonCode>(button) );
switch( button ) {
case Ok:
emit okClicked();
accept();
break;
case Apply:
emit applyClicked();
break;
case Try:
emit tryClicked();
break;
case User3:
emit user3Clicked();
break;
case User2:
emit user2Clicked();
break;
case User1:
emit user1Clicked();
break;
case Yes:
emit yesClicked();
done( Yes );
break;
case No:
emit noClicked();
done( No );
break;
case Cancel:
emit cancelClicked();
reject();
break;
case Close:
emit closeClicked();
done(Close); // KDE5: call reject() instead; more QDialog-like.
break;
case Help:
emit helpClicked();
if ( !d->mAnchor.isEmpty() || !d->mHelpApp.isEmpty() )
KHelpClient::invokeHelp( d->mAnchor, d->mHelpApp );
break;
case Default:
emit defaultClicked();
break;
case Reset:
emit resetClicked();
break;
case Details:
setDetailsWidgetVisible( !d->mDetailsVisible );
break;
}
// If we're here from the closeEvent, and auto-delete is on, well, auto-delete now.
if (d->mDeferredDelete) {
d->mDeferredDelete = false;
delayedDestruct();
}
}
void KDialog::enableLinkedHelp( bool state )
{
Q_D(KDialog);
if ( ( d->mUrlHelp != 0 ) == state )
return;
if ( state ) {
if ( d->mUrlHelp )
return;
d->mUrlHelp = new KUrlLabel( this );
d->mUrlHelp->setText( helpLinkText() );
d->mUrlHelp->setFloatEnabled( true );
d->mUrlHelp->setUnderline( true );
d->mUrlHelp->setMinimumHeight( fontMetrics().height() + marginHint() );
connect( d->mUrlHelp, SIGNAL(leftClickedUrl()), SLOT(helpLinkClicked()) );
d->mUrlHelp->show();
} else {
delete d->mUrlHelp;
d->mUrlHelp = 0;
}
d->setupLayout();
}
void KDialog::setHelp( const QString &anchor, const QString &appname )
{
Q_D(KDialog);
d->mAnchor = anchor;
d->mHelpApp = appname;
}
void KDialog::setHelpLinkText( const QString &text )
{
Q_D(KDialog);
d->mHelpLinkText = text;
if ( d->mUrlHelp )
d->mUrlHelp->setText( helpLinkText() );
}
QString KDialog::helpLinkText() const
{
Q_D(const KDialog);
return ( d->mHelpLinkText.isEmpty() ? i18n( "Get help..." ) : d->mHelpLinkText );
}
void KDialog::updateGeometry()
{
}
void KDialog::hideEvent( QHideEvent *event )
{
emit hidden();
if ( !event->spontaneous() )
emit finished();
}
void KDialog::closeEvent( QCloseEvent *event )
{
Q_D(KDialog);
QPushButton *button = this->button(d->mEscapeButton);
if (button && !isHidden()) {
button->animateClick();
if (testAttribute(Qt::WA_DeleteOnClose)) {
// Don't let QWidget::close do a deferred delete just yet, wait for the click first
d->mDeferredDelete = true;
setAttribute(Qt::WA_DeleteOnClose, false);
}
} else {
QDialog::closeEvent(event);
}
}
#ifndef KDE_NO_DEPRECATED
void KDialog::restoreDialogSize( const KConfigGroup& cfg )
{
KWindowConfig::restoreWindowSize(this, cfg);
}
#endif
#ifndef KDE_NO_DEPRECATED
void KDialog::saveDialogSize( KConfigGroup& config, KConfigGroup::WriteConfigFlags options ) const
{
KWindowConfig::saveWindowSize(this, config, options);
}
#endif
void KDialog::setAllowEmbeddingInGraphicsView( bool allowEmbedding )
{
sAllowEmbeddingInGraphicsView = allowEmbedding;
}
#include "moc_kdialog.cpp"
diff --git a/staging/kwidgets/src/dialogs/kdialog.h b/staging/kwidgets/src/dialogs/kdialog.h
index 18809c2920..611c50223c 100644
--- a/staging/kwidgets/src/dialogs/kdialog.h
+++ b/staging/kwidgets/src/dialogs/kdialog.h
@@ -1,874 +1,874 @@
/* This file is part of the KDE Libraries
* Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net)
* Additions 1999-2000 by Espen Sand (espen@kde.org)
* and Holger Freyther <freyther@kde.org>
* 2005-2009 Olivier Goffart <ogoffart @ kde.org>
* 2006 Tobias Koenig <tokoe@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 KDIALOG_H
#define KDIALOG_H
class QPushButton;
class QMenu;
class KDialogPrivate;
#include <kwidgets_export.h>
#include <kconfiggroup.h>
#include <kguiitem.h>
#include <kiconloader.h>
#include <QDialog>
/**
* @short A dialog base class with standard buttons and predefined layouts.
*
* Provides basic functionality needed by nearly all dialogs.
*
* It offers the standard action buttons you'd expect to find in a
* dialog as well as the capability to define at most three configurable
* buttons. You can define a main widget that contains your specific
* dialog layout
*
* The class takes care of the geometry management. You only need to define
* a minimum size for the widget you want to use as the main widget.
*
* By default, the dialog is non-modal.
*
* <b>Standard buttons (action buttons):</b>\n
*
* You select which buttons should be displayed, but you do not choose the
* order in which they are displayed. This ensures a standard interface in
* KDE. The button order can be changed, but this ability is only available
* for a central KDE control tool. The following buttons are available:
* OK, Cancel/Close, Apply/Try, Default, Help and three user definable
* buttons: User1, User2 and User3. You must specify the text of the UserN
* buttons. Each button emit a signal, so you can choose to connect that signal.
*
* The default action of the Help button will open the help system if you have
* provided a path to the help text.
* The default action of Ok and Cancel will run QDialog::accept() and QDialog::reject(),
* which you can override by reimplementing slotButtonClicked(). The default
* action of the Close button will close the dialog.
*
* Note that the KDialog will animate a button press
* when the user presses Escape. The button that is enabled is either Cancel,
* Close or the button that is defined by setEscapeButton().
* Your custom dialog code should reimplement the keyPressEvent and
* animate the cancel button so that the dialog behaves like regular
* dialogs.
*
* <b>Layout:</b>\n
*
* The dialog consists of a help area on top (becomes visible if you define
* a help path and use enableLinkedHelp()), the main area which is
* the built-in dialog face or your own widget in the middle and by default
* a button box at the bottom. The button box can also be placed at the
* right edge (to the right of the main widget). Use
* setButtonsOrientation() to control this behavior. A separator
* can be placed above the button box (or to the left when the button box
* is at the right edge).
*
* <b>Standard compliance:</b>\n
*
* The marginHint() and spacingHint() sizes shall be used
* whenever you lay out the interior of a dialog. One special note. If
* you make your own action buttons (OK, Cancel etc), the space
* between the buttons shall be spacingHint(), whereas the space
* above, below, to the right and to the left shall be marginHint().
* If you add a separator line above the buttons, there shall be a
* marginHint() between the buttons and the separator and a
* marginHint() above the separator as well.
*
* <b>Example:</b>\n
*
* \code
* KDialog *dialog = new KDialog( this );
* dialog->setCaption( "My title" );
* dialog->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply );
*
* FooWidget *widget = new FooWidget( dialog );
* dialog->setMainWidget( widget );
* connect( dialog, SIGNAL( applyClicked() ), widget, SLOT( save() ) );
* connect( dialog, SIGNAL( okClicked() ), widget, SLOT( save() ) );
* connect( widget, SIGNAL( changed( bool ) ), dialog, SLOT( enableButtonApply( bool ) ) );
*
* dialog->enableButtonApply( false );
* dialog->show();
* \endcode
*
* \image html kdialog.png "KDE Dialog example"
*
* This class can be used in many ways. Note that most KDE ui widgets
* and many of KDE core applications use the KDialog so for more
* inspiration you should study the code for these.
*
*
* @see KPageDialog
* @author Thomas Tanghus <tanghus@earthling.net>
* @author Espen Sand <espensa@online.no>
* @author Mirko Boehm <mirko@kde.org>
* @author Olivier Goffart <ogoffart at kde.org>
* @author Tobias Koenig <tokoe@kde.org>
*/
class KWIDGETS_EXPORT KDialog : public QDialog //krazy:exclude=qclasses
{
Q_OBJECT
Q_ENUMS(ButtonCode)
Q_DECLARE_PRIVATE(KDialog)
public:
enum ButtonCode
{
None = 0x00000000,
Help = 0x00000001, ///< Show Help button. (this button will run the help set with setHelp)
Default = 0x00000002, ///< Show Default button.
Ok = 0x00000004, ///< Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
Apply = 0x00000008, ///< Show Apply button.
Try = 0x00000010, ///< Show Try button.
Cancel = 0x00000020, ///< Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
Close = 0x00000040, ///< Show Close-button. (this button closes the dialog)
No = 0x00000080, ///< Show No button. (this button closes the dialog and sets the result to KDialog::No)
Yes = 0x00000100, ///< Show Yes button. (this button closes the dialog and sets the result to KDialog::Yes)
Reset = 0x00000200, ///< Show Reset button
Details = 0x00000400, ///< Show Details button. (this button will show the detail widget set with setDetailsWidget)
User1 = 0x00001000, ///< Show User defined button 1.
User2 = 0x00002000, ///< Show User defined button 2.
User3 = 0x00004000, ///< Show User defined button 3.
NoDefault = 0x00008000 ///< Used when specifying a default button; indicates that no button should be marked by default.
};
// TODO KDE5: remove NoDefault and use the value None instead
Q_DECLARE_FLAGS(ButtonCodes, ButtonCode)
enum ButtonPopupMode
{
InstantPopup = 0,
DelayedPopup = 1
};
Q_DECLARE_FLAGS(ButtonPopupModes, ButtonPopupMode)
public:
/**
* Creates a dialog.
*
* @param parent The parent of the dialog.
* @param flags The widget flags passed to the QDialog constructor
*/
- explicit KDialog( QWidget *parent = 0, Qt::WFlags flags = 0 );
+ explicit KDialog( QWidget *parent = 0, Qt::WindowFlags flags = 0 );
/**
* Destroys the dialog.
*/
~KDialog();
/**
* Creates (or recreates) the button box and all the buttons in it.
*
* Note that some combinations are not possible. That means, you can't
* have the following pairs of buttons in a dialog:
* - Default and Details
* - Cancel and Close
* - Ok and Try
*
* This will reset all default KGuiItem of all button.
*
* @param buttonMask Specifies what buttons will be made.
*/
void setButtons( ButtonCodes buttonMask );
/**
* Sets the orientation of the button box.
*
* It can be @p Vertical or @p Horizontal. If @p Horizontal
* (default), the button box is positioned at the bottom of the
* dialog. If @p Vertical it will be placed at the right edge of the
* dialog.
*
* @param orientation The button box orientation.
*/
void setButtonsOrientation( Qt::Orientation orientation );
/**
* Sets the button that will be activated when the Escape key
* is pressed.
*
* By default, the Escape key is mapped to either the Cancel or the Close button
* if one of these buttons are defined. The user expects that Escape will
* cancel an operation so use this function with caution.
*
* @param id The button code.
*/
void setEscapeButton( ButtonCode id );
/**
* Sets the button that will be activated when the Enter key
* is pressed.
*
* By default, this is the Ok button if it is present
*
* @param id The button code.
*/
void setDefaultButton( ButtonCode id );
/**
* Returns the button code of the default button,
* or NoDefault if there is no default button.
*/
ButtonCode defaultButton() const;
/**
* Hide or display the a separator line drawn between the action
* buttons an the main widget.
*/
void showButtonSeparator( bool state );
/**
* Hide or display a general action button.
*
* Only buttons that have
* been created in the constructor can be displayed. This method will
* not create a new button.
*
* @param id Button identifier.
* @param state true display the button(s).
*/
void showButton( ButtonCode id, bool state );
/**
* Sets the text of any button.
*
* @param id The button identifier.
* @param text Button text.
*/
void setButtonText( ButtonCode id, const QString &text );
/**
* Returns the text of any button.
*/
QString buttonText( ButtonCode id ) const;
/**
* Sets the icon of any button.
*
* @param id The button identifier.
* @param icon Button icon.
*/
void setButtonIcon( ButtonCode id, const QIcon &icon );
/**
* Returns the icon of any button.
*/
QIcon buttonIcon( ButtonCode id ) const;
/**
* Sets the tooltip text of any button.
*
* @param id The button identifier.
* @param text Button text.
*/
void setButtonToolTip( ButtonCode id, const QString &text );
/**
* Returns the tooltip of any button.
*/
QString buttonToolTip( ButtonCode id ) const;
/**
* Sets the "What's this?" text of any button.
*
* @param id The button identifier.
* @param text Button text.
*/
void setButtonWhatsThis( ButtonCode id, const QString &text );
/**
* Returns the "What's this?" text of any button.
*/
QString buttonWhatsThis( ButtonCode id ) const;
/**
* Sets the KGuiItem directly for the button instead of using 3 methods to
* set the text, tooltip and whatsthis strings. This also allows to set an
* icon for the button which is otherwise not possible for the extra
* buttons beside Ok, Cancel and Apply.
*
* @param id The button identifier.
* @param item The KGuiItem for the button.
*/
void setButtonGuiItem( ButtonCode id, const KGuiItem &item );
/**
* Sets the menu of any button.
*
* @param id The button identifier.
* @param menu The menu.
* @param popupmode Choose if QPushButton setMenu or setDelayedMenu is used
*/
void setButtonMenu( ButtonCode id, QMenu *menu, ButtonPopupMode popupmode=InstantPopup);
/**
* Sets the focus to the button of the passed @p id.
*/
void setButtonFocus( ButtonCode id );
/**
* Convenience method. Sets the initial dialog size.
*
* This method should only be called right before show() or exec().
* The initial size will be ignored if smaller than
* the dialog's minimum size.
*
* @param size Startup size.
*/
void setInitialSize( const QSize &size );
/**
* Convenience method. Add a size to the default minimum size of a
* dialog.
*
* This method should only be called right before show() or exec().
*
* @param size Size added to minimum size.
*/
void incrementInitialSize( const QSize &size );
/**
* Restores the dialog's size from the configuration according to
* the screen size.
*
* @note the group must be set before calling
*
* @param config The config group to read from.
* @deprecated use KWindowConfig::restoreDialogSize() instead
*/
#ifndef KDE_NO_DEPRECATED
KWIDGETS_DEPRECATED void restoreDialogSize( const KConfigGroup& config ) ;
#endif
/**
* Saves the dialog's size dependent on the screen dimension either to the
* global or application config file.
*
* @note the group must be set before calling
*
* @param config The config group to read from.
* @param options passed to KConfigGroup::writeEntry()
* @deprecated use KWindowConfig::saveDialogSize() instead
*/
#ifndef KDE_NO_DEPRECATED
KWIDGETS_DEPRECATED void saveDialogSize( KConfigGroup& config, KConfigGroup::WriteConfigFlags options = KConfigGroup::Normal ) const;
#endif
/**
* Returns the help link text.
*
* If no text has been defined,
* "Get help..." (internationalized) is returned.
*
* @return The help link text.
*
* @see enableLinkedHelp()
* @see setHelpLinkText()
* @see setHelp()
*/
QString helpLinkText() const;
/**
* Returns whether any button is enabled.
*/
bool isButtonEnabled( ButtonCode id ) const;
/**
* Returns the button that corresponds to the @p id.
*
* Normally you should not use this function.
* @em Never delete the object returned by this function.
* See also enableButton(), showButton(), setButtonGuiItem().
*
* @param id Identifier of the button.
* @return The button or 0 if the button does not exist.
*/
QPushButton* button( ButtonCode id ) const;
/**
* Returns the number of pixels that should be used between a
* dialog edge and the outermost widget(s) according to the KDE standard.
*
* @deprecated Use the style's pixelMetric() function to query individual margins.
* Different platforms may use different values for the four margins.
*/
static int marginHint();
/**
* Returns the number of pixels that should be used between
* widgets inside a dialog according to the KDE standard.
*
* @deprecated Use the style's layoutSpacing() function to query individual spacings.
* Different platforms may use different values depending on widget types and pairs.
*/
static int spacingHint();
/**
* Returns the number of pixels that should be used to visually
* separate groups of related options in a dialog according to
* the KDE standard.
* @since 4.2
*/
static int groupSpacingHint();
/**
* @enum StandardCaptionFlag
* Used to specify how to construct a window caption
*
* @value AppName Indicates that the method shall include
* the application name when making the caption string.
* @value Modified Causes a 'modified' sign will be included in the
* returned string. This is useful when indicating that a file is
* modified, i.e., it contains data that has not been saved.
* @value HIGCompliant The base minimum flags required to align a
* caption with the KDE Human Interface Guidelines
*/
enum CaptionFlag
{
NoCaptionFlags = 0,
AppNameCaption = 1,
ModifiedCaption = 2,
HIGCompliantCaption = AppNameCaption
};
Q_DECLARE_FLAGS(CaptionFlags, CaptionFlag)
/**
* Builds a caption that contains the application name along with the
* userCaption using a standard layout.
*
* To make a compliant caption for your window, simply do:
* @p setWindowTitle(KDialog::makeStandardCaption(yourCaption));
*
* To ensure that the caption is appropriate to the desktop in which the
* application is running, pass in a pointer to the window the caption will
* be applied to.
*
* If using a KDialog or KMainWindow subclass, call setCaption instead and
* an appropraite standard caption will be created for you
*
* @param userCaption The caption string you want to display in the
* window caption area. Do not include the application name!
* @param window a pointer to the window this application will apply to
* @param flags
* @return the created caption
*/
static QString makeStandardCaption( const QString &userCaption,
QWidget* window = 0,
CaptionFlags flags = HIGCompliantCaption );
/**
* Resize every layout manager used in @p widget and its nested children.
*
* @param widget The widget used.
* @param margin The new layout margin.
* @param spacing The new layout spacing.
*
* @deprecated Use QLayout functions where necessary. Setting margin and spacing
* values recursively for all children prevents QLayout from creating platform native
* layouts.
*/
static void resizeLayout( QWidget *widget, int margin, int spacing );
/**
* Resize every layout associated with @p lay and its children.
*
* @param lay layout to be resized
* @param margin The new layout margin
* @param spacing The new layout spacing
*
* @deprecated Use QLayout functions where necessary. Setting margin and spacing
* values recursively for all children prevents QLayout from creating platform native
* layouts.
*/
static void resizeLayout( QLayout *lay, int margin, int spacing );
/**
* Centers @p widget on the desktop, taking multi-head setups into
* account. If @p screen is -1, @p widget will be centered on its
* current screen (if it was shown already) or on the primary screen.
* If @p screen is -3, @p widget will be centered on the screen that
* currently contains the mouse pointer.
* @p screen will be ignored if a merged display (like Xinerama) is not
* in use, or merged display placement is not enabled in kdeglobals.
*/
static void centerOnScreen( QWidget *widget, int screen = -1 );
/**
* Places @p widget so that it doesn't cover a certain @p area of the screen.
* This is typically used by the "find dialog" so that the match it finds can
* be read.
* For @p screen, see centerOnScreen
* @return true on success (widget doesn't cover area anymore, or never did),
* false on failure (not enough space found)
*/
static bool avoidArea( QWidget *widget, const QRect& area, int screen = -1 );
/**
* Sets the main widget of the dialog.
*/
void setMainWidget( QWidget *widget );
/**
* @return The current main widget. Will create a QWidget as the mainWidget
* if none was set before. This way you can write
* \code
* ui.setupUi(mainWidget());
* \endcode
* when using designer.
*/
QWidget *mainWidget();
/**
* Reimplemented from QDialog.
*/
virtual QSize sizeHint() const;
/**
* Reimplemented from QDialog.
*/
virtual QSize minimumSizeHint() const;
/**
* Allow embedding the dialogs based on KDialog into a graphics view. By default embedding is not allowed, dialogs
* will appear as separate windows.
* @since 4.6
*/
static void setAllowEmbeddingInGraphicsView( bool allowEmbedding );
public Q_SLOTS:
/**
* Make a KDE compliant caption.
*
* @param caption Your caption. Do @p not include the application name
* in this string. It will be added automatically according to the KDE
* standard.
*/
virtual void setCaption( const QString &caption );
/**
* Makes a KDE compliant caption.
*
* @param caption Your caption. @em Do @em not include the application name
* in this string. It will be added automatically according to the KDE
* standard.
* @param modified Specify whether the document is modified. This displays
* an additional sign in the title bar, usually "**".
*/
virtual void setCaption( const QString &caption, bool modified );
/**
* Make a plain caption without any modifications.
*
* @param caption Your caption. This is the string that will be
* displayed in the window title.
*/
virtual void setPlainCaption( const QString &caption );
/**
* Enable or disable (gray out) a general action button.
*
* @param id Button identifier.
* @param state @p true enables the button(s).
*/
void enableButton( ButtonCode id, bool state );
/**
* Enable or disable (gray out) the OK button.
*
* @param state @p true enables the button.
*/
void enableButtonOk( bool state );
/**
* Enable or disable (gray out) the Apply button.
*
* @param state true enables the button.
*/
void enableButtonApply( bool state );
/**
* Enable or disable (gray out) the Cancel button.
*
* @param state true enables the button.
*/
void enableButtonCancel( bool state );
/**
* Display or hide the help link area on the top of the dialog.
*
* @param state @p true will display the area.
*
* @see helpLinkText()
* @see setHelpLinkText()
* @see setHelp()
*/
void enableLinkedHelp( bool state );
/**
* Sets the text that is shown as the linked text.
*
* If text is empty,
* the text "Get help..." (internationalized) is used instead.
*
* @param text The link text.
*
* @see helpLinkText()
* @see enableLinkedHelp()
* @see setHelp()
*/
void setHelpLinkText( const QString &text );
/**
* Sets the help path and topic.
*
* @param anchor Defined anchor in your docbook sources
* @param appname Defines the appname the help belongs to
* If empty it's the current one
*
* @note The help button works differently for the class
* KCMultiDialog, so it does not make sense to call this
* function for Dialogs of that type. See
* KCMultiDialog::slotHelp() for more information.
*/
void setHelp( const QString &anchor, const QString &appname = QString() );
/**
* Returns the status of the Details button.
*/
bool isDetailsWidgetVisible() const;
/**
* Sets the status of the Details button.
*/
void setDetailsWidgetVisible( bool visible );
/**
* Sets the widget that gets shown when "Details" is enabled.
*
* The dialog takes over ownership of the widget.
* Any previously set widget gets deleted.
*/
void setDetailsWidget( QWidget *detailsWidget );
/**
* Destruct the dialog delayed.
*
* You can call this function from slots like closeClicked() and hidden().
* You should not use the dialog any more after calling this function.
* @deprecated use hide()+deleteLater()
*/
void delayedDestruct();
Q_SIGNALS:
/**
* Emitted when the margin size and/or spacing size
* have changed.
*
* Use marginHint() and spacingHint() in your slot
* to get the new values.
*
* @deprecated This signal is not emitted. Listen to QEvent::StyleChange events instead.
*/
void layoutHintChanged();
/**
* The Help button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void helpClicked();
/**
* The Default button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void defaultClicked();
/**
* The Reset button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void resetClicked();
/**
* The User3 button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void user3Clicked();
/**
* The User2 button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void user2Clicked();
/**
* The User1 button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void user1Clicked();
/**
* The Apply button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void applyClicked();
/**
* The Try button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void tryClicked();
/**
* The OK button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void okClicked();
/**
* The Yes button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void yesClicked();
/**
* The No button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void noClicked();
/**
* The Cancel button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void cancelClicked();
/**
* The Close button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void closeClicked();
/**
* A button has been pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
* @param button is the code of the pressed button.
*/
void buttonClicked( KDialog::ButtonCode button);
/**
* The dialog is about to be hidden.
*
* A dialog is hidden after a user clicks a button that ends
* the dialog or when the user switches to another desktop or
* minimizes the dialog.
*/
void hidden();
/**
* The dialog has finished.
*
* A dialog emits finished after a user clicks a button that ends
* the dialog.
*
* This signal is also emitted when you call hide()
*
* If you have stored a pointer to the
* dialog do @em not try to delete the pointer in the slot that is
* connected to this signal.
*
* You should use deleteLater() instead.
*/
void finished();
/**
* The detailsWidget is about to get shown. This is your last chance
* to call setDetailsWidget if you haven't done so yet.
*/
void aboutToShowDetails();
protected:
/**
* Emits the #hidden signal. You can connect to that signal to
* detect when a dialog has been closed.
*/
virtual void hideEvent( QHideEvent * );
/**
* Detects when a dialog is being closed from the window manager
* controls. If the Cancel or Close button is present then the button
* is activated. Otherwise standard QDialog behavior
* will take place.
*/
virtual void closeEvent( QCloseEvent *e );
/**
* @internal
*/
virtual void keyPressEvent( QKeyEvent* );
protected Q_SLOTS:
/**
* Activated when the button @p button is clicked
*
* Sample that shows how to catch and handle button clicks within
* an own dialog;
* @code
* class MyDialog : public KDialog {
* protected Q_SLOTS:
* virtual void slotButtonClicked(int button) {
* if (button == KDialog::Ok)
* accept();
* else
* KDialog::slotButtonClicked(button);
* }
* }
* @endcode
*
* @param button is the type @a KDialog::ButtonCode
*/
virtual void slotButtonClicked(int button);
/**
* Updates the margins and spacings.
*
* @deprecated KDialog respects the style's margins and spacings automatically. Calling
* this function has no effect.
*/
void updateGeometry();
protected:
- KDialog(KDialogPrivate &dd, QWidget *parent, Qt::WFlags flags = 0);
+ KDialog(KDialogPrivate &dd, QWidget *parent, Qt::WindowFlags flags = 0);
KDialogPrivate *const d_ptr;
private:
Q_DISABLE_COPY(KDialog)
Q_PRIVATE_SLOT(d_ptr, void queuedLayoutUpdate())
Q_PRIVATE_SLOT(d_ptr, void helpLinkClicked())
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KDialog::ButtonCodes)
Q_DECLARE_OPERATORS_FOR_FLAGS(KDialog::CaptionFlags)
#endif // KDIALOG_H
diff --git a/tier1/itemmodels/autotests/bihash/benchmarks.cpp b/tier1/itemmodels/autotests/bihash/benchmarks.cpp
index fc5a06cdca..d5546c6b59 100644
--- a/tier1/itemmodels/autotests/bihash/benchmarks.cpp
+++ b/tier1/itemmodels/autotests/bihash/benchmarks.cpp
@@ -1,414 +1,414 @@
/*
Copyright (C) 2010 Klarälvdalens Datakonsult AB,
a KDAB Group company, info@kdab.net,
author Stephen Kelly <stephen@kdab.com>
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 <QtCore/QObject>
#include <QtTest>
#define KBIHASH 1
// #define BOOST_BIMAP 1
#define LEFT int
#define RIGHT int
// #define LEFT QString
// #define RIGHT QString
#ifdef KBIHASH
#include "kbihash_p.h"
#endif
#ifdef BOOST_BIMAP
#include <boost/bimap.hpp>
#endif
typedef QHash<LEFT, RIGHT> Hash;
#ifdef KBIHASH
typedef KBiHash<LEFT, RIGHT> Mapping;
#endif
#ifdef BOOST_BIMAP
typedef boost::bimap<LEFT, RIGHT> Mapping;
static Mapping hashToBiMap(const Hash &hash)
{
Mapping biMap;
Hash::const_iterator it = hash.constBegin();
const Hash::const_iterator end = hash.constEnd();
for ( ; it != end; ++it)
biMap.insert(Mapping::value_type(it.key(), it.value()));
return biMap;
}
#endif
// #define QBENCHMARK_ONCE
#define MAX_SIZE 25000
#define MAX_DIGITS 5
#define NEW_ROW(num) \
-QTest::newRow(QString("%1").arg(num).toAscii() ) << num;
+QTest::newRow(QString("%1").arg(num).toLatin1() ) << num;
template<typename T> T containedValue(int value );
template<> int containedValue(int value)
{
return value;
}
template<> QString containedValue(int value)
{
return QString("%1").arg(value, MAX_DIGITS);
}
template<typename T> T updatedValue(int value );
template<> int updatedValue(int value)
{
return value + MAX_SIZE;
}
template<> QString updatedValue(int value)
{
return QString("%1").arg(value + MAX_SIZE, MAX_DIGITS);
}
class BiHashBenchmarks : public QObject
{
Q_OBJECT
public:
BiHashBenchmarks(QObject* parent = 0)
{
qsrand(QDateTime::currentDateTime().toTime_t());
}
private:
Hash createHash(int numElements)
{
Hash hash;
hash.reserve(numElements);
for (int i = 0; i < numElements; ++i)
{
hash.insert(containedValue<Hash::key_type>(i), containedValue<Hash::mapped_type>(i));
}
return hash;
}
void getTestData();
private slots:
void testInsert();
void testLookup();
void testReverseLookup();
void testRemoveKey();
void testRemoveValue();
void testUpdateKey();
void testUpdateValue();
void testInsert_data();
void testLookup_data();
void testReverseLookup_data();
void testRemoveKey_data();
void testRemoveValue_data();
void testUpdateKey_data();
void testUpdateValue_data();
};
void BiHashBenchmarks::testInsert()
{
QFETCH(int, numElements);
#ifdef KBIHASH
Mapping biHash;
QBENCHMARK_ONCE{
for (int i = 0; i < numElements; ++i)
{
biHash.insert(containedValue<Hash::key_type>(i), containedValue<Hash::mapped_type>(i));
}
biHash.clear();
}
#else
# ifdef BOOST_BIMAP
Mapping biMap;
QBENCHMARK_ONCE{
for (int i = 0; i < numElements; ++i)
{
biMap.insert(Mapping::value_type(containedValue<Hash::key_type>(i), containedValue<Hash::mapped_type>(i)));
}
biMap.clear();
}
# else
Hash hash;
QBENCHMARK_ONCE {
for (int i = 0; i < numElements; ++i)
{
hash.insert(containedValue<Hash::key_type>(i), containedValue<Hash::mapped_type>(i));
}
hash.clear();
}
# endif
#endif
}
void BiHashBenchmarks::getTestData()
{
QTest::addColumn<int>("numElements");
NEW_ROW(1);
NEW_ROW(2);
NEW_ROW(3);
NEW_ROW(5);
NEW_ROW(8);
NEW_ROW(13);
NEW_ROW(21);
NEW_ROW(34);
NEW_ROW(50);
NEW_ROW(100);
NEW_ROW(150);
NEW_ROW(200);
NEW_ROW(250);
NEW_ROW(500);
NEW_ROW(1000);
NEW_ROW(1500);
NEW_ROW(2000);
NEW_ROW(2500);
NEW_ROW(5000);
NEW_ROW(10000);
NEW_ROW(15000);
NEW_ROW(20000);
NEW_ROW(MAX_SIZE);
}
void BiHashBenchmarks::testInsert_data()
{
getTestData();
}
void BiHashBenchmarks::testLookup()
{
QFETCH(int, numElements);
Hash hash = createHash(numElements);
Hash::mapped_type result;
const Hash::key_type key = containedValue<Hash::key_type>(qrand() % numElements);
#ifdef KBIHASH
Mapping biHash = Mapping::fromHash(hash);
QBENCHMARK_ONCE{
result = biHash.leftToRight(key);
}
#else
# if BOOST_BIMAP
Mapping biMap = hashToBiMap(hash);
QBENCHMARK_ONCE{
result = biMap.left[key];
}
# else
QBENCHMARK_ONCE{
result = hash.value(key);
}
# endif
#endif
}
void BiHashBenchmarks::testLookup_data()
{
getTestData();
}
void BiHashBenchmarks::testReverseLookup()
{
QFETCH(int, numElements);
Hash hash = createHash(numElements);
Hash::key_type result;
const Hash::mapped_type value = containedValue<Hash::mapped_type>(qrand() % numElements);
#ifdef KBIHASH
Mapping biHash = Mapping::fromHash(hash);
QBENCHMARK_ONCE{
result = biHash.rightToLeft(value);
}
#else
# if BOOST_BIMAP
Mapping biMap = hashToBiMap(hash);
QBENCHMARK_ONCE{
result = biMap.right[value];
}
# else
QBENCHMARK_ONCE{
result = hash.key(value);
}
# endif
#endif
}
void BiHashBenchmarks::testReverseLookup_data()
{
getTestData();
}
void BiHashBenchmarks::testRemoveKey()
{
QFETCH(int, numElements);
Hash hash = createHash(numElements);
const Hash::key_type key = containedValue<Hash::key_type>(qrand() % numElements);
Hash::mapped_type value;
#ifdef KBIHASH
Mapping biHash = Mapping::fromHash(hash);
QBENCHMARK_ONCE{
value = biHash.takeLeft(key);
}
#else
# if BOOST_BIMAP
Mapping biMap = hashToBiMap(hash);
Mapping::size_type t;
QBENCHMARK_ONCE{
t = biMap.erase(key);
}
# else
QBENCHMARK_ONCE{
value = hash.take(key);
}
# endif
#endif
}
void BiHashBenchmarks::testRemoveKey_data()
{
getTestData();
}
void BiHashBenchmarks::testRemoveValue()
{
QFETCH(int, numElements);
Hash hash = createHash(numElements);
Hash::key_type result;
const Hash::mapped_type value = containedValue<Hash::mapped_type>(qrand() % numElements);
#ifdef KBIHASH
Mapping biHash = Mapping::fromHash(hash);
QBENCHMARK_ONCE{
result = biHash.takeRight(value);
}
#else
# ifdef BOOST_BIMAP
Mapping biMap = hashToBiMap(hash);
Mapping::size_type t;
QBENCHMARK_ONCE{
t = biMap.right.erase(value);
}
# else
QBENCHMARK_ONCE{
result = hash.key(value);
hash.remove(result);
}
# endif
#endif
}
void BiHashBenchmarks::testRemoveValue_data()
{
getTestData();
}
void BiHashBenchmarks::testUpdateKey()
{
QFETCH(int, numElements);
Hash hash = createHash(numElements);
const int num = qrand() % numElements;
const Hash::key_type oldKey = containedValue<Hash::key_type>(num);
const Hash::key_type newKey = updatedValue<Hash::key_type>(num);
#ifdef KBIHASH
Mapping biHash = Mapping::fromHash(hash);
QBENCHMARK_ONCE{
Mapping::right_iterator it = biHash.findRight(biHash.leftToRight(oldKey));
biHash.updateLeft(it, newKey);
}
#else
# ifdef BOOST_BIMAP
Mapping biMap = hashToBiMap(hash);
QBENCHMARK_ONCE{
Mapping::right_iterator it = biMap.left.find(oldKey);
it->first = newKey;
}
# else
QBENCHMARK_ONCE{
const Hash::mapped_type value = hash.take(oldKey);
hash.insert(newKey, value);
}
# endif
#endif
}
void BiHashBenchmarks::testUpdateKey_data()
{
getTestData();
}
void BiHashBenchmarks::testUpdateValue()
{
QFETCH(int, numElements);
Hash hash = createHash(numElements);
const int num = qrand() % numElements;
const Hash::key_type key = containedValue<Hash::key_type>(num);
#ifdef KBIHASH
Mapping biHash = Mapping::fromHash(hash);
const Hash::mapped_type newValue = updatedValue<Hash::mapped_type>(num);
QBENCHMARK_ONCE{
Mapping::left_iterator it = biHash.findLeft(key);
biHash.updateRight(it, newValue);
}
#else
# if BOOST_BIMAP
Mapping biMap = hashToBiMap(hash);
const Hash::mapped_type newValue = updatedValue<Hash::mapped_type>(num);
QBENCHMARK_ONCE{
Mapping::left_iterator it = biMap.left.find(key);
it->second = newValue;
}
# else
const Hash::mapped_type newValue = updatedValue<Hash::mapped_type>(num);
QBENCHMARK_ONCE{
hash[key] = newValue;
}
# endif
#endif
}
void BiHashBenchmarks::testUpdateValue_data()
{
getTestData();
}
QTEST_MAIN(BiHashBenchmarks)
#include "benchmarks.moc"
diff --git a/tier1/itemmodels/autotests/proxymodeltestapp/modelcommanderwidget.cpp b/tier1/itemmodels/autotests/proxymodeltestapp/modelcommanderwidget.cpp
index a29140bd39..b51f3128c1 100644
--- a/tier1/itemmodels/autotests/proxymodeltestapp/modelcommanderwidget.cpp
+++ b/tier1/itemmodels/autotests/proxymodeltestapp/modelcommanderwidget.cpp
@@ -1,120 +1,120 @@
/*
Copyright (c) 2010 Stephen Kelly <steveire@gmail.com>
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 "modelcommanderwidget.h"
#include <QTreeWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include "dynamictreemodel.h"
#include "modelcommander.h"
#include <QMetaMethod>
ModelCommanderWidget::ModelCommanderWidget(DynamicTreeModel *dynamicTreeModel, QWidget* parent, Qt::WindowFlags f)
: QWidget(parent, f),
m_dynamicTreeModel(dynamicTreeModel),
m_modelCommander(new ModelCommander(m_dynamicTreeModel, this)),
m_treeWidget(new QTreeWidget),
m_executeButton(new QPushButton("Execute"))
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(m_treeWidget);
layout->addWidget(m_executeButton);
init();
connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
connect(m_executeButton, SIGNAL(clicked(bool)), SLOT(executeCurrentTest()));
}
void ModelCommanderWidget::init()
{
const QMetaObject *mo = m_modelCommander->metaObject();
QMetaMethod mm;
for(int i = 0; i < mo->methodCount(); ++i)
{
mm = mo->method(i);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QString signature = mm.signature();
#else
QString signature = mm.methodSignature();
#endif
if (signature.startsWith("init_") && signature.endsWith("(QString)"))
{
QTreeWidgetItem *testFunctionItem = new QTreeWidgetItem(m_treeWidget, QStringList() << signature.mid(5, signature.length() - 14));
m_treeWidget->addTopLevelItem(testFunctionItem);
QStringList testData;
- QMetaObject::invokeMethod(m_modelCommander, QByteArray("execute_" + testFunctionItem->text(0).toAscii()),
+ QMetaObject::invokeMethod(m_modelCommander, QByteArray("execute_" + testFunctionItem->text(0).toLatin1()),
Q_RETURN_ARG(QStringList, testData),
Q_ARG(QString, QString()));
Q_FOREACH(const QString &testRun, testData)
QTreeWidgetItem *testDataItem = new QTreeWidgetItem(testFunctionItem, QStringList() << testRun);
}
}
}
void ModelCommanderWidget::currentItemChanged( QTreeWidgetItem * current, QTreeWidgetItem * previous )
{
Q_UNUSED(previous);
initTest(current);
}
void ModelCommanderWidget::executeCurrentTest()
{
executeTest(m_treeWidget->currentItem());
disconnect(m_executeButton, SIGNAL(clicked(bool)), this, SLOT(executeCurrentTest()));
m_executeButton->setText("Reset");
connect(m_executeButton, SIGNAL(clicked(bool)), SLOT(resetCurrentTest()));
}
void ModelCommanderWidget::resetCurrentTest()
{
initTest(m_treeWidget->currentItem());
disconnect(m_executeButton, SIGNAL(clicked(bool)), this, SLOT(resetCurrentTest()));
m_executeButton->setText("Execute");
connect(m_executeButton, SIGNAL(clicked(bool)), SLOT(executeCurrentTest()));
}
void ModelCommanderWidget::initTest(QTreeWidgetItem *item)
{
if (!item->parent())
return; // m_dynamicTreeModel->clear();
m_dynamicTreeModel->clear();
- bool success = QMetaObject::invokeMethod(m_modelCommander, QByteArray("init_" + item->parent()->text(0).toAscii()),
+ bool success = QMetaObject::invokeMethod(m_modelCommander, QByteArray("init_" + item->parent()->text(0).toLatin1()),
Q_ARG(QString, item->text(0)));
Q_ASSERT(success);
}
void ModelCommanderWidget::executeTest(QTreeWidgetItem *item)
{
if (!item->parent())
return;
- bool success = QMetaObject::invokeMethod(m_modelCommander, QByteArray("execute_" + item->parent()->text(0).toAscii()),
+ bool success = QMetaObject::invokeMethod(m_modelCommander, QByteArray("execute_" + item->parent()->text(0).toLatin1()),
Q_ARG(QString, item->text(0)));
Q_ASSERT(success);
}
diff --git a/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.cpp b/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.cpp
index 6c8889a17d..fe6aa548fb 100644
--- a/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.cpp
+++ b/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.cpp
@@ -1,777 +1,777 @@
/*
Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
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 "proxymodeltest.h"
#include "dynamictreemodel.h"
#include <QItemSelectionModel>
#include <QSortFilterProxyModel>
#include "modelspy.h"
ProxyModelTest::ProxyModelTest(QObject *parent)
: QObject(parent),
m_rootModel(new DynamicTreeModel(this)),
m_sourceModel(m_rootModel),
m_proxyModel(0),
m_intermediateProxyModel(0),
m_modelSpy(new ModelSpy(this)),
m_modelCommander(new ModelCommander(m_rootModel, this))
{
}
void ProxyModelTest::setLazyPersistence(Persistence persistence)
{
m_modelSpy->setLazyPersistence(persistence == LazyPersistence);
}
void ProxyModelTest::setUseIntermediateProxy(SourceModel sourceModel)
{
if (sourceModel == DynamicTree)
return;
m_intermediateProxyModel = new QSortFilterProxyModel(this);
m_intermediateProxyModel->setSourceModel(m_rootModel);
m_sourceModel = m_intermediateProxyModel;
}
static bool hasMetaMethodStartingWith(QObject *object, const QString &checkedSignature)
{
const QMetaObject *mo = object->metaObject();
bool found = false;
for (int methodIndex = 0; methodIndex < mo->methodCount(); ++methodIndex) {
QMetaMethod mm = mo->method(methodIndex);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QString signature = QString::fromLatin1( mm.signature() );
#else
QString signature = QString::fromLatin1( mm.methodSignature() );
#endif
if (signature.startsWith(checkedSignature)) {
found = true;
break;
}
}
return found;
}
void ProxyModelTest::initRootModel(DynamicTreeModel *rootModel, const QString &currentTest, const QString &currentTag)
{
Q_UNUSED(rootModel)
// Get the model into the state it is expected to be in.
if (!hasMetaMethodStartingWith(m_modelCommander, "init_" + currentTest))
return;
- QMetaObject::invokeMethod(m_modelCommander, QString("init_" + currentTest).toAscii(), Q_ARG(QString, currentTag));
+ QMetaObject::invokeMethod(m_modelCommander, QString("init_" + currentTest).toLatin1(), Q_ARG(QString, currentTag));
}
void ProxyModelTest::verifyExecutedTests()
{
if (m_dataTags.contains(ProxyModelTestData::failTag()))
return;
QSet<QString> unimplemented = m_modelCommanderTags.toSet().subtract(m_dataTags.toSet());
QString unimplementedTestsString("(");
Q_FOREACH(const QString &test, unimplemented)
unimplementedTestsString.append(test + ",");
unimplementedTestsString.append(")");
if (!unimplemented.isEmpty())
{
QString failString = QString("Some tests in %1 were not implemented: %2").arg(m_currentTest, unimplementedTestsString);
m_dataTags.clear();
m_currentTest = QTest::currentTestFunction();
- QFAIL(failString.toAscii());
+ QFAIL(failString.toLatin1());
}
}
void ProxyModelTest::init()
{
QVERIFY(m_modelSpy->isEmpty());
m_rootModel->clear();
const char *currentTest = QTest::currentTestFunction();
const char *currentTag = QTest::currentDataTag();
QVERIFY(currentTest != 0);
initRootModel(m_rootModel, currentTest, currentTag);
Q_ASSERT(sourceModel());
QAbstractProxyModel *proxyModel = getProxy();
Q_ASSERT(proxyModel);
// Don't set the sourceModel in getProxy.
Q_ASSERT(!proxyModel->sourceModel());
connectProxy(proxyModel);
// Get the model into the state it is expected to be in.
m_modelSpy->startSpying();
QVERIFY(m_modelSpy->isEmpty());
if (m_currentTest != currentTest)
{
verifyExecutedTests();
m_dataTags.clear();
QString metaMethod = QString("execute_" + QLatin1String(currentTest));
if (!hasMetaMethodStartingWith(m_modelCommander, metaMethod))
return;
- QMetaObject::invokeMethod(m_modelCommander, metaMethod.toAscii(), Q_RETURN_ARG(QStringList, m_modelCommanderTags), Q_ARG(QString, QString()));
+ QMetaObject::invokeMethod(m_modelCommander, metaMethod.toLatin1(), Q_RETURN_ARG(QStringList, m_modelCommanderTags), Q_ARG(QString, QString()));
m_currentTest = currentTest;
}
m_dataTags.append(currentTag);
}
void ProxyModelTest::cleanup()
{
QVERIFY(m_modelSpy->isEmpty());
m_modelSpy->stopSpying();
m_modelSpy->setModel(0);
m_proxyModel->setSourceModel(0);
delete m_proxyModel;
m_proxyModel = 0;
QVERIFY(m_modelSpy->isEmpty());
}
void ProxyModelTest::cleanupTestCase()
{
verifyExecutedTests();
m_modelCommanderTags.clear();
if (!m_intermediateProxyModel)
return;
m_sourceModel = m_rootModel;
delete m_intermediateProxyModel;
m_intermediateProxyModel = 0;
m_modelSpy->clear();
}
PersistentIndexChange ProxyModelTest::getChange(IndexFinder parentFinder, int start, int end, int difference, bool toInvalid)
{
Q_ASSERT(start <= end);
PersistentIndexChange change;
change.parentFinder = parentFinder;
change.startRow = start;
change.endRow = end;
change.difference = difference;
change.toInvalid = toInvalid;
return change;
}
void ProxyModelTest::handleSignal(QVariantList expected)
{
QVERIFY(!expected.isEmpty());
int signalType = expected.takeAt(0).toInt();
if (NoSignal == signalType)
return;
Q_ASSERT(!m_modelSpy->isEmpty());
QVariantList result = getResultSignal();
QCOMPARE(result.takeAt(0).toInt(), signalType);
// Check that the signal we expected to recieve was emitted exactly.
switch (signalType)
{
case RowsAboutToBeInserted:
case RowsInserted:
case RowsAboutToBeRemoved:
case RowsRemoved:
{
QVERIFY( expected.size() == 3 );
IndexFinder parentFinder = qvariant_cast<IndexFinder>(expected.at(0));
parentFinder.setModel(m_proxyModel);
QModelIndex parent = parentFinder.getIndex();
// This is where is usually goes wrong...
#if 0
qDebug() << qvariant_cast<QModelIndex>(result.at(0)) << parent;
qDebug() << result.at(1) << expected.at(1);
qDebug() << result.at(2) << expected.at(2);
#endif
QCOMPARE(qvariant_cast<QModelIndex>(result.at(0)), parent );
QCOMPARE(result.at(1), expected.at(1) );
QCOMPARE(result.at(2), expected.at(2) );
break;
}
case LayoutAboutToBeChanged:
case LayoutChanged:
{
QVERIFY( expected.size() == 0 );
QVERIFY( result.size() == 0 );
break;
}
case RowsAboutToBeMoved:
case RowsMoved:
{
QVERIFY( expected.size() == 5 );
IndexFinder scrParentFinder = qvariant_cast<IndexFinder>(expected.at(0));
scrParentFinder.setModel(m_proxyModel);
QModelIndex srcParent = scrParentFinder.getIndex();
QCOMPARE(qvariant_cast<QModelIndex>(result.at(0)), srcParent );
QCOMPARE(result.at(1), expected.at(1) );
QCOMPARE(result.at(2), expected.at(2) );
IndexFinder destParentFinder = qvariant_cast<IndexFinder>(expected.at(3));
destParentFinder.setModel(m_proxyModel);
QModelIndex destParent = destParentFinder.getIndex();
QCOMPARE(qvariant_cast<QModelIndex>(result.at(3)), destParent );
QCOMPARE(result.at(4), expected.at(4) );
break;
}
case DataChanged:
{
QVERIFY( expected.size() == 2 );
IndexFinder topLeftFinder = qvariant_cast<IndexFinder>(expected.at(0));
topLeftFinder.setModel(m_proxyModel);
QModelIndex topLeft = topLeftFinder.getIndex();
IndexFinder bottomRightFinder = qvariant_cast<IndexFinder>(expected.at(1));
bottomRightFinder.setModel(m_proxyModel);
QModelIndex bottomRight = bottomRightFinder.getIndex();
QVERIFY(topLeft.isValid() && bottomRight.isValid());
#if 0
qDebug() << qvariant_cast<QModelIndex>(result.at(0)) << topLeft;
qDebug() << qvariant_cast<QModelIndex>(result.at(1)) << bottomRight;
#endif
QCOMPARE(qvariant_cast<QModelIndex>(result.at(0)), topLeft );
QCOMPARE(qvariant_cast<QModelIndex>(result.at(1)), bottomRight );
}
}
}
QVariantList ProxyModelTest::getResultSignal()
{
return m_modelSpy->takeFirst();
}
void ProxyModelTest::testEmptyModel()
{
Q_ASSERT(sourceModel());
QAbstractProxyModel *proxyModel = getProxy();
// Many of these just check that the proxy does not crash when it does not have a source model.
QCOMPARE(proxyModel->rowCount(), 0);
QCOMPARE(proxyModel->columnCount(), 0);
QVERIFY(!proxyModel->index(0,0).isValid());
QVERIFY(!proxyModel->data(QModelIndex()).isValid());
QVERIFY(!proxyModel->parent(QModelIndex()).isValid());
QVERIFY(!proxyModel->mapToSource(QModelIndex()).isValid());
QVERIFY(!proxyModel->mapFromSource(QModelIndex()).isValid());
QVERIFY(!proxyModel->headerData(0, Qt::Horizontal, Qt::DisplayRole).isValid());
QVERIFY(!proxyModel->headerData(0, Qt::Vertical, Qt::DisplayRole).isValid());
Qt::ItemFlags flags = proxyModel->flags ( QModelIndex() );
QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 );
QVERIFY(proxyModel->itemData(QModelIndex()).isEmpty());
QVERIFY(proxyModel->mapSelectionToSource(QItemSelection()).isEmpty());
QVERIFY(proxyModel->mapSelectionFromSource(QItemSelection()).isEmpty());
proxyModel->revert();
QVERIFY(proxyModel->submit());
QVERIFY(!proxyModel->sourceModel());
QVERIFY(!proxyModel->canFetchMore(QModelIndex()));
proxyModel->fetchMore(QModelIndex());
QMimeData *data = new QMimeData();
QVERIFY(!proxyModel->dropMimeData( data, Qt::CopyAction, 0, 0, QModelIndex()));
delete data;
QVERIFY(!proxyModel->hasChildren());
QVERIFY(!proxyModel->hasIndex(0, 0, QModelIndex()));
proxyModel->supportedDragActions();
proxyModel->supportedDropActions();
delete proxyModel;
}
void ProxyModelTest::testSourceReset()
{
m_modelSpy->stopSpying();
ModelInsertCommand *ins = new ModelInsertCommand(m_rootModel, this);
ins->setStartRow(0);
ins->interpret(
"- 1"
"- 2"
"- - 3"
"- - 4"
"- 5"
"- 6"
"- 7"
"- 8"
"- 9"
"- - 10"
);
ins->doCommand();
// The proxymodel should reset any internal state it holds when the source model is reset.
QPersistentModelIndex pmi = m_proxyModel->index(0, 0);
testMappings();
m_rootModel->clear(); // Resets the model.
testMappings(); // Calls some rowCount() etc which should test internal structures in the proxy.
m_proxyModel->setSourceModel(0);
m_modelSpy->startSpying();
}
void ProxyModelTest::testDestroyModel()
{
QAbstractItemModel *currentSourceModel = m_sourceModel;
DynamicTreeModel *rootModel = new DynamicTreeModel(this);
m_sourceModel = rootModel;
ModelInsertCommand *ins = new ModelInsertCommand(rootModel, this);
ins->setStartRow(0);
ins->interpret(
" - 1"
" - 1"
" - - 1"
" - 1"
" - 1"
" - 1"
" - 1"
" - 1"
" - - 1"
);
ins->doCommand();
QAbstractProxyModel *proxyModel = getProxy();
connectProxy(proxyModel);
if(proxyModel->hasChildren())
{
m_modelSpy->startSpying();
delete m_sourceModel;
m_sourceModel = 0;
m_modelSpy->stopSpying();
testMappings();
// QCOMPARE(m_modelSpy->size(), 1);
// QVERIFY(m_modelSpy->takeFirst().first() == ModelReset);
}
m_sourceModel = currentSourceModel;
}
void ProxyModelTest::doTestMappings(const QModelIndex &parent)
{
if (!m_proxyModel)
return;
QModelIndex idx;
QModelIndex srcIdx;
for (int column = 0; column < m_proxyModel->columnCount(parent); ++column)
{
for (int row = 0; row < m_proxyModel->rowCount(parent); ++row)
{
idx = m_proxyModel->index(row, column, parent);
QVERIFY(idx.isValid());
QVERIFY(idx.row() == row);
QVERIFY(idx.column() == column);
QVERIFY(idx.parent() == parent);
QVERIFY(idx.model() == m_proxyModel);
srcIdx = m_proxyModel->mapToSource(idx);
QVERIFY(srcIdx.isValid());
QVERIFY(srcIdx.model() == m_proxyModel->sourceModel());
QVERIFY(m_sourceModel == m_proxyModel->sourceModel());
QVERIFY(idx.data() == srcIdx.data());
QVERIFY(m_proxyModel->mapFromSource(srcIdx) == idx);
if (m_proxyModel->hasChildren(idx))
doTestMappings(idx);
}
}
}
void ProxyModelTest::testMappings()
{
doTestMappings(QModelIndex());
}
void ProxyModelTest::verifyModel(const QModelIndex& parent, int start, int end)
{
Q_UNUSED(start);
Q_UNUSED(end);
QVERIFY(parent.model() == m_proxyModel || !parent.isValid());
}
void ProxyModelTest::verifyModel(const QModelIndex& parent, int start, int end, const QModelIndex& destParent, int dest)
{
Q_UNUSED(start);
Q_UNUSED(end);
Q_UNUSED(dest);
QVERIFY(parent.model() == m_proxyModel || !parent.isValid());
QVERIFY(destParent.model() == m_proxyModel || !destParent.isValid());
}
void ProxyModelTest::verifyModel(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
QVERIFY(topLeft.model() == m_proxyModel || !topLeft.isValid());
QVERIFY(bottomRight.model() == m_proxyModel || !bottomRight.isValid());
}
void ProxyModelTest::connectProxy(QAbstractProxyModel *proxyModel)
{
if (m_proxyModel)
{
disconnect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(layoutAboutToBeChanged()),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(layoutChanged()),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(testMappings()));
disconnect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
this, SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
disconnect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
this, SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
disconnect(m_proxyModel, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(verifyModel(QModelIndex,int,int)));
disconnect(m_proxyModel, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
this, SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
disconnect(m_proxyModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
this, SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
disconnect(m_proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(verifyModel(QModelIndex,QModelIndex)));
}
m_proxyModel = proxyModel;
QVERIFY(m_modelSpy->isEmpty());
m_modelSpy->setModel(m_proxyModel);
QVERIFY(m_modelSpy->isEmpty());
m_modelSpy->startSpying();
m_proxyModel->setSourceModel(m_sourceModel);
m_modelSpy->stopSpying();
QVERIFY(m_modelSpy->size() == 2);
QVERIFY(m_modelSpy->takeFirst().first() == ModelAboutToBeReset);
QVERIFY(m_modelSpy->takeFirst().first() == ModelReset);
QVERIFY(m_modelSpy->isEmpty());
testMappings();
connect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(layoutAboutToBeChanged()),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(layoutChanged()),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
SLOT(testMappings()));
connect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
connect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
connect(m_proxyModel, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
SLOT(verifyModel(QModelIndex,int,int)));
connect(m_proxyModel, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
connect(m_proxyModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(verifyModel(QModelIndex,int,int,QModelIndex,int)));
connect(m_proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
SLOT(verifyModel(QModelIndex,QModelIndex)));
}
void ProxyModelTest::doTest()
{
QFETCH( SignalList, signalList );
QFETCH( PersistentChangeList, changeList );
QVERIFY(m_modelSpy->isEmpty());
QString testName = QTest::currentTestFunction();
QString testDataTag = QTest::currentDataTag();
if (testDataTag == ProxyModelTestData::failTag())
return;
if ((signalList.size() == 1 && signalList.first().size() == 1)
&& signalList.first().first().toString() == "skip")
{
return;
}
static int numTests = 0;
if (qApp->arguments().contains("-count"))
qDebug() << "numTests" << ++numTests;
m_modelSpy->preTestPersistIndexes(changeList);
// Run the test.
Q_ASSERT(m_modelSpy->isEmpty());
m_modelSpy->startSpying();
- QMetaObject::invokeMethod(m_modelCommander, QString("execute_" + testName).toAscii(), Q_ARG(QString, testDataTag));
+ QMetaObject::invokeMethod(m_modelCommander, QString("execute_" + testName).toLatin1(), Q_ARG(QString, testDataTag));
m_modelSpy->stopSpying();
if (modelSpy()->isEmpty())
QVERIFY(signalList.isEmpty());
// Make sure we didn't get any signals we didn't expect.
if (signalList.isEmpty())
QVERIFY(modelSpy()->isEmpty());
const bool isLayoutChange = signalList.contains(QVariantList() << LayoutAboutToBeChanged);
while (!signalList.isEmpty())
{
// Process each signal we recieved as a result of running the test.
QVariantList expected = signalList.takeAt(0);
handleSignal(expected);
}
// Make sure we didn't get any signals we didn't expect.
QVERIFY(m_modelSpy->isEmpty());
// Persistent indexes should change by the amount described in change objects.
Q_FOREACH (PersistentIndexChange change, m_modelSpy->getChangeList())
{
for (int i = 0; i < change.indexes.size(); i++)
{
QModelIndex idx = change.indexes.at(i);
QPersistentModelIndex persistentIndex = change.persistentIndexes.at(i);
// Persistent indexes go to an invalid state if they are removed from the model.
if (change.toInvalid)
{
QVERIFY(!persistentIndex.isValid());
continue;
}
#if 0
qDebug() << idx << idx.data() << change.difference << change.toInvalid << persistentIndex.row();
#endif
QCOMPARE(idx.row() + change.difference, persistentIndex.row());
QCOMPARE(idx.column(), persistentIndex.column());
if (!isLayoutChange)
QCOMPARE(idx.parent(), persistentIndex.parent());
}
for (int i = 0; i < change.descendantIndexes.size(); i++)
{
QModelIndex idx = change.descendantIndexes.at(i);
QPersistentModelIndex persistentIndex = change.persistentDescendantIndexes.at(i);
// The descendant indexes of indexes which were removed should now also be invalid.
if (change.toInvalid)
{
QVERIFY(!persistentIndex.isValid());
continue;
}
// Otherwise they should be unchanged.
QCOMPARE(idx.row(), persistentIndex.row());
QCOMPARE(idx.column(), persistentIndex.column());
if (!isLayoutChange)
QCOMPARE(idx.parent(), persistentIndex.parent());
}
}
QModelIndexList unchangedIndexes = m_modelSpy->getUnchangedIndexes();
QList<QPersistentModelIndex> unchangedPersistentIndexes = m_modelSpy->getUnchangedPersistentIndexes();
// Indexes unaffected by the signals should be unchanged.
for (int i = 0; i < unchangedIndexes.size(); ++i)
{
QModelIndex unchangedIdx = unchangedIndexes.at(i);
QPersistentModelIndex persistentIndex = unchangedPersistentIndexes.at(i);
QCOMPARE(unchangedIdx.row(), persistentIndex.row());
QCOMPARE(unchangedIdx.column(), persistentIndex.column());
if (!isLayoutChange)
QCOMPARE(unchangedIdx.parent(), persistentIndex.parent());
}
m_modelSpy->clearTestData();
}
void ProxyModelTest::connectTestSignals(QObject *reciever)
{
if (!reciever)
return;
for (int methodIndex = 0; methodIndex < metaObject()->methodCount(); ++methodIndex) {
QMetaMethod mm = metaObject()->method(methodIndex);
if (mm.methodType() == QMetaMethod::Signal
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
&& QString(mm.signature()).startsWith("test")
#else
&& QString(mm.methodSignature()).startsWith("test")
#endif
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
&& QString(mm.signature()).endsWith("Data()"))
#else
&& QString(mm.methodSignature()).endsWith("Data()"))
#endif
{
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
int slotIndex = reciever->metaObject()->indexOfSlot(mm.signature());
#else
int slotIndex = reciever->metaObject()->indexOfSlot(mm.methodSignature());
#endif
Q_ASSERT(slotIndex >= 0);
metaObject()->connect(this, methodIndex, reciever, slotIndex);
}
}
}
void ProxyModelTest::disconnectTestSignals(QObject *reciever)
{
if (!reciever)
return;
for (int methodIndex = 0; methodIndex < metaObject()->methodCount(); ++methodIndex) {
QMetaMethod mm = metaObject()->method(methodIndex);
if (mm.methodType() == QMetaMethod::Signal
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
&& QString(mm.signature()).startsWith("test")
#else
&& QString(mm.methodSignature()).startsWith("test")
#endif
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
&& QString(mm.signature()).endsWith("Data()"))
#else
&& QString(mm.methodSignature()).endsWith("Data()"))
#endif
{
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
int slotIndex = reciever->metaObject()->indexOfSlot(mm.signature());
#else
int slotIndex = reciever->metaObject()->indexOfSlot(mm.methodSignature());
#endif
Q_ASSERT(slotIndex >= 0);
metaObject()->disconnect(this, methodIndex, reciever, slotIndex);
}
}
}
uint qHash( const QVariant & var )
{
if ( !var.isValid() || var.isNull() )
return -1;
switch ( var.type() )
{
case QVariant::Int:
return qHash( var.toInt() );
break;
case QVariant::UInt:
return qHash( var.toUInt() );
break;
case QVariant::Bool:
return qHash( var.toUInt() );
break;
case QVariant::Double:
return qHash( var.toUInt() );
break;
case QVariant::LongLong:
return qHash( var.toLongLong() );
break;
case QVariant::ULongLong:
return qHash( var.toULongLong() );
break;
case QVariant::String:
return qHash( var.toString() );
break;
case QVariant::Char:
return qHash( var.toChar() );
break;
case QVariant::StringList:
return qHash( var.toString() );
break;
case QVariant::ByteArray:
return qHash( var.toByteArray() );
break;
case QVariant::Date:
case QVariant::Time:
case QVariant::DateTime:
case QVariant::Url:
case QVariant::Locale:
case QVariant::RegExp:
return qHash( var.toString() );
break;
case QVariant::Map:
case QVariant::List:
case QVariant::BitArray:
case QVariant::Size:
case QVariant::SizeF:
case QVariant::Rect:
case QVariant::LineF:
case QVariant::Line:
case QVariant::RectF:
case QVariant::Point:
case QVariant::PointF:
// not supported yet
break;
case QVariant::UserType:
case QVariant::Invalid:
default:
return -1;
}
// could not generate a hash for the given variant
Q_ASSERT(0);
return -1;
}
diff --git a/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.h b/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.h
index 95572cd15b..7c323b9b4d 100644
--- a/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.h
+++ b/tier1/itemmodels/autotests/proxymodeltestsuite/proxymodeltest.h
@@ -1,681 +1,681 @@
/*
Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
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 PROXY_MODEL_TEST_H
#define PROXY_MODEL_TEST_H
#include <QtTest>
#include <QtCore>
#include <QTestEvent>
#include <QItemSelectionRange>
#include <QAbstractProxyModel>
#include "dynamictreemodel.h"
#include "indexfinder.h"
#include "modelcommander.h"
#include "modelspy.h"
#include "persistentchangelist.h"
#include "proxymodeltestsuite_export.h"
typedef QList<QVariantList> SignalList;
Q_DECLARE_METATYPE( SignalList )
enum Persistence
{
LazyPersistence,
ImmediatePersistence
};
enum SourceModel
{
DynamicTree,
IntermediateProxy
};
class PROXYMODELTESTSUITE_EXPORT BuiltinTestDataInterface
{
public:
virtual ~BuiltinTestDataInterface() { }
private:
virtual void testInsertWhenEmptyData() = 0;
virtual void testInsertInRootData() = 0;
virtual void testInsertInTopLevelData() = 0;
virtual void testInsertInSecondLevelData() = 0;
virtual void testRemoveFromRootData() = 0;
virtual void testRemoveFromTopLevelData() = 0;
virtual void testRemoveFromSecondLevelData() = 0;
virtual void testMoveFromRootData() = 0;
virtual void testMoveFromTopLevelData() = 0;
virtual void testMoveFromSecondLevelData() = 0;
virtual void testModifyInRootData() = 0;
virtual void testModifyInTopLevelData() = 0;
virtual void testModifyInSecondLevelData() = 0;
};
class PROXYMODELTESTSUITE_EXPORT BuiltinTestInterface : BuiltinTestDataInterface
{
public:
virtual ~BuiltinTestInterface() { }
private:
virtual void testInsertWhenEmpty_data() = 0;
virtual void testInsertWhenEmpty() = 0;
virtual void testInsertInRoot_data() = 0;
virtual void testInsertInRoot() = 0;
virtual void testInsertInTopLevel_data() = 0;
virtual void testInsertInTopLevel() = 0;
virtual void testInsertInSecondLevel_data() = 0;
virtual void testInsertInSecondLevel() = 0;
virtual void testRemoveFromRoot_data() = 0;
virtual void testRemoveFromRoot() = 0;
virtual void testRemoveFromTopLevel_data() = 0;
virtual void testRemoveFromTopLevel() = 0;
virtual void testRemoveFromSecondLevel_data() = 0;
virtual void testRemoveFromSecondLevel() = 0;
virtual void testMoveFromRoot_data() = 0;
virtual void testMoveFromRoot() = 0;
virtual void testMoveFromTopLevel_data() = 0;
virtual void testMoveFromTopLevel() = 0;
virtual void testMoveFromSecondLevel_data() = 0;
virtual void testMoveFromSecondLevel() = 0;
virtual void testModifyInRoot_data() = 0;
virtual void testModifyInRoot() = 0;
virtual void testModifyInTopLevel_data() = 0;
virtual void testModifyInTopLevel() = 0;
virtual void testModifyInSecondLevel_data() = 0;
virtual void testModifyInSecondLevel() = 0;
};
class PROXYMODELTESTSUITE_EXPORT ProxyModelTest : public QObject, protected BuiltinTestInterface
{
Q_OBJECT
public:
ProxyModelTest(QObject *parent = 0);
virtual ~ProxyModelTest() {}
void setLazyPersistence(Persistence persistence);
void setUseIntermediateProxy(SourceModel sourceModel);
DynamicTreeModel* rootModel() const { return m_rootModel; }
QAbstractItemModel* sourceModel() const { return m_sourceModel; }
QAbstractProxyModel* proxyModel() const { return m_proxyModel; }
ModelSpy* modelSpy() const { return m_modelSpy; }
PersistentIndexChange getChange(IndexFinder sourceFinder, int start, int end, int difference, bool toInvalid = false);
QVariantList noSignal() const { return QVariantList() << NoSignal; }
QVariantList getSignal(SignalType type, IndexFinder parentFinder, int start, int end) const
{ return QVariantList() << type << QVariant::fromValue(parentFinder) << start << end; }
QVariantList getSignal(SignalType type, IndexFinder srcFinder, int start, int end, IndexFinder destFinder, int destStart) const
{ return QVariantList() << type << QVariant::fromValue(srcFinder) << start << end << QVariant::fromValue(destFinder) << destStart; }
QVariantList getSignal(SignalType type, IndexFinder topLeftFinder, IndexFinder bottomRightFinder) const
{ return QVariantList() << type << QVariant::fromValue(topLeftFinder) << QVariant::fromValue(bottomRightFinder); }
protected:
virtual QAbstractProxyModel* getProxy() = 0;
void doCleanupTestCase() { cleanupTestCase(); }
void doCleanup() { cleanup(); }
Q_SIGNALS:
void testInsertWhenEmptyData();
void testInsertInRootData();
void testInsertInTopLevelData();
void testInsertInSecondLevelData();
void testRemoveFromRootData();
void testRemoveFromTopLevelData();
void testRemoveFromSecondLevelData();
void testMoveFromRootData();
void testMoveFromTopLevelData();
void testMoveFromSecondLevelData();
void testModifyInRootData();
void testModifyInTopLevelData();
void testModifyInSecondLevelData();
protected Q_SLOTS:
void testMappings();
void verifyModel(const QModelIndex &parent, int start, int end);
void verifyModel(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int dest);
void verifyModel(const QModelIndex &topLeft, const QModelIndex &bottomRight);
private Q_SLOTS:
void init();
void cleanup();
void cleanupTestCase();
void testEmptyModel();
void testSourceReset();
void testDestroyModel();
void testInsertWhenEmpty_data() { testInsertWhenEmptyData(); }
void testInsertWhenEmpty() { doTest(); }
void testInsertInRoot_data() { testInsertInRootData(); }
void testInsertInRoot() { doTest(); }
void testInsertInTopLevel_data() { testInsertInTopLevelData(); }
void testInsertInTopLevel() { doTest(); }
void testInsertInSecondLevel_data() { testInsertInSecondLevelData(); }
void testInsertInSecondLevel() { doTest(); }
void testRemoveFromRoot_data() { testRemoveFromRootData(); }
void testRemoveFromRoot() { doTest(); }
void testRemoveFromTopLevel_data() { testRemoveFromTopLevelData(); }
void testRemoveFromTopLevel() { doTest(); }
void testRemoveFromSecondLevel_data() { testRemoveFromSecondLevelData(); }
void testRemoveFromSecondLevel() { doTest(); }
void testMoveFromRoot_data() { testMoveFromRootData(); }
void testMoveFromRoot() { doTest(); }
void testMoveFromTopLevel_data() { testMoveFromTopLevelData(); }
void testMoveFromTopLevel() { doTest(); }
void testMoveFromSecondLevel_data() { testMoveFromSecondLevelData(); }
void testMoveFromSecondLevel() { doTest(); }
void testModifyInRoot_data() { testModifyInRootData(); }
void testModifyInRoot() { doTest(); }
void testModifyInTopLevel_data() { testModifyInTopLevelData(); }
void testModifyInTopLevel() { doTest(); }
void testModifyInSecondLevel_data() { testModifyInSecondLevelData(); }
void testModifyInSecondLevel() { doTest(); }
protected:
void connectTestSignals(QObject *reciever);
void disconnectTestSignals(QObject *reciever);
void connectProxy(QAbstractProxyModel *proxyModel);
void doTestMappings(const QModelIndex &parent);
void initRootModel(DynamicTreeModel *rootModel, const QString &currentTest, const QString &currentTag);
void doTest();
void handleSignal(QVariantList expected);
QVariantList getResultSignal();
int getChange(bool sameParent, int start, int end, int currentPosition, int destinationStart);
QStringList dataTags() const { return m_dataTags; }
void verifyExecutedTests();
private:
DynamicTreeModel *m_rootModel;
QAbstractItemModel *m_sourceModel;
QAbstractProxyModel *m_proxyModel;
QAbstractProxyModel *m_intermediateProxyModel;
ModelSpy *m_modelSpy;
ModelCommander *m_modelCommander;
QStringList m_dataTags;
QStringList m_modelCommanderTags;
QString m_currentTest;
};
class PROXYMODELTESTSUITE_EXPORT ProxyModelTestData : public QObject, BuiltinTestDataInterface
{
Q_OBJECT
public:
ProxyModelTestData(ProxyModelTest *parent = 0)
: QObject(parent), m_proxyModelTest(parent)
{
}
static const char* failTag() { return "fail01"; }
protected:
void dummyTestData()
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
QTest::newRow(failTag()) << SignalList() << PersistentChangeList();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
QSKIP("Test not implemented");
#else
QSKIP("Test not implemented", SkipSingle);
#endif
}
void skipTestData(const QString &name)
{
processTestName(name);
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
- QTest::newRow(name.toAscii()) << (SignalList() << (QVariantList() << "skip") ) << PersistentChangeList();
+ QTest::newRow(name.toLatin1()) << (SignalList() << (QVariantList() << "skip") ) << PersistentChangeList();
}
void processTestName(const QString &name)
{
if (m_currentTestFunction != QTest::currentTestFunction())
{
m_testNames.clear();
m_currentTestFunction = QTest::currentTestFunction();
}
m_testNames.insert(name);
}
QStringList namedTests()
{
return m_testNames.toList();
}
void noopTest(const QString &name)
{
processTestName(name);
- QTest::newRow(name.toAscii()) << SignalList() << PersistentChangeList();
+ QTest::newRow(name.toLatin1()) << SignalList() << PersistentChangeList();
}
void noopLayoutChangeTest(const QString &name)
{
processTestName(name);
SignalList signalList;
signalList << ( QVariantList() << LayoutAboutToBeChanged );
signalList << ( QVariantList() << LayoutChanged );
- QTest::newRow(name.toAscii()) << signalList << PersistentChangeList();
+ QTest::newRow(name.toLatin1()) << signalList << PersistentChangeList();
}
void testForwardingInsertData(const IndexFinder &indexFinder)
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
newInsertTest("insert01", indexFinder, 0, 0, 10);
newInsertTest("insert02", indexFinder, 0, 9, 10);
newInsertTest("insert03", indexFinder, 10, 10, 10);
newInsertTest("insert04", indexFinder, 10, 19, 10);
newInsertTest("insert05", indexFinder, 4, 4, 10);
newInsertTest("insert06", indexFinder, 4, 13, 10);
newInsertTest("insert07", indexFinder, 0, 0, 10);
newInsertTest("insert08", indexFinder, 10, 10, 10);
newInsertTest("insert09", indexFinder, 4, 4, 10);
newInsertTest("insert10", indexFinder, 0, 4, 10);
newInsertTest("insert11", indexFinder, 10, 14, 10);
newInsertTest("insert12", indexFinder, 4, 8, 10);
QList<int> rows = indexFinder.rows();
rows.append(0);
newInsertTest("insert13", rows, 0, 0, 0);
newInsertTest("insert14", rows, 0, 9, 0);
newInsertTest("insert15", rows, 0, 4, 0);
rows = indexFinder.rows();
rows.append(9);
newInsertTest("insert16", rows, 0, 0, 0);
newInsertTest("insert17", rows, 0, 9, 0);
newInsertTest("insert18", rows, 0, 4, 0);
}
void testForwardingRemoveData(const IndexFinder &indexFinder)
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
newRemoveTest("remove01", indexFinder, 0, 0, 10);
newRemoveTest("remove02", indexFinder, 0, 4, 10);
newRemoveTest("remove03", indexFinder, 9, 9, 10);
}
void testForwardingMoveData(const IndexFinder &srcFinder, const IndexFinder &destFinder)
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
// noopLayoutChangeTest("move01");
// noopLayoutChangeTest("move02");
// noopLayoutChangeTest("move03");
// noopLayoutChangeTest("move04");
// noopLayoutChangeTest("move05");
newMoveTest("move01", srcFinder, 0, 0, 10, destFinder, 5);
newMoveTest("move02", srcFinder, 4, 4, 10, destFinder, 0);
newMoveTest("move03", srcFinder, 4, 4, 10, destFinder, 10);
newMoveTest("move04", srcFinder, 9, 9, 10, destFinder, 4);
newMoveTest("move05", srcFinder, 9, 9, 10, destFinder, 0);
}
void testForwardingModifyData(const IndexFinder &parentFinder)
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
newModifyTest("modify01", parentFinder, 0, 0);
newModifyTest("modify02", parentFinder, 0, 4);
newModifyTest("modify03", parentFinder, 9, 9);
newModifyTest("modify04", parentFinder, 6, 9);
newModifyTest("modify05", parentFinder, 4, 4);
newModifyTest("modify06", parentFinder, 3, 7);
newModifyTest("modify07", parentFinder, 0, 9);
}
void newInsertTest(const QString &name, const IndexFinder &indexFinder, int start, int end, int rowCount)
{
processTestName(name);
SignalList signalList;
PersistentChangeList persistentList;
signalList << m_proxyModelTest->getSignal(RowsAboutToBeInserted, indexFinder, start, end);
signalList << m_proxyModelTest->getSignal(RowsInserted, indexFinder, start, end);
if (rowCount - 1 + ( end - start + 1 ) > end)
persistentList << m_proxyModelTest->getChange( indexFinder, start, rowCount - 1, end - start + 1 );
- QTest::newRow(name.toAscii()) << signalList << persistentList;
+ QTest::newRow(name.toLatin1()) << signalList << persistentList;
}
void newRemoveTest(const QString &name, const IndexFinder &indexFinder, int start, int end, int rowCount)
{
processTestName(name);
SignalList signalList;
PersistentChangeList persistentList;
signalList << m_proxyModelTest->getSignal(RowsAboutToBeRemoved, indexFinder, start, end);
signalList << m_proxyModelTest->getSignal(RowsRemoved, indexFinder, start, end);
persistentList << m_proxyModelTest->getChange( indexFinder, start, end, -1, true );
if (rowCount - 1 != end)
{
persistentList << m_proxyModelTest->getChange( indexFinder, end + 1, rowCount - 1, -1 * (end - start + 1) );
}
- QTest::newRow(name.toAscii()) << signalList << persistentList;
+ QTest::newRow(name.toLatin1()) << signalList << persistentList;
}
void newMoveTest(const QString &name, const IndexFinder &srcFinder, int start, int end, int rowCount, const IndexFinder &destFinder, int destStart)
{
Q_UNUSED(rowCount)
processTestName(name);
SignalList signalList;
PersistentChangeList persistentList;
// signalList << m_proxyModelTest->getSignal(RowsAboutToBeMoved, srcFinder, start, end, destFinder, destStart);
// signalList << ( QVariantList() << LayoutAboutToBeChanged );
// signalList << m_proxyModelTest->getSignal(RowsMoved, srcFinder, start, end, destFinder, destStart);
// signalList << ( QVariantList() << LayoutChanged );
signalList << ( QVariantList() << LayoutAboutToBeChanged );
signalList << ( QVariantList() << LayoutChanged );
const bool sameParent = (srcFinder == destFinder);
const bool movingUp = (start > destStart);
if ( sameParent )
{
if ( movingUp )
{
persistentList << m_proxyModelTest->getChange( srcFinder, destStart, start -1, end - start + 1 );
persistentList << m_proxyModelTest->getChange( srcFinder, start, end, -1 * (start - destStart) );
} else {
persistentList << m_proxyModelTest->getChange( srcFinder, start, end, destStart - end - 1 );
persistentList << m_proxyModelTest->getChange( srcFinder, end + 1, destStart - 1, -1 * (end - start + 1) );
}
} else {
if ( movingUp )
{
// TODO
}
}
- QTest::newRow(name.toAscii()) << signalList << persistentList;
+ QTest::newRow(name.toLatin1()) << signalList << persistentList;
}
void newModifyTest(const QString &name, const IndexFinder &parentFinder, int top, int bottom)
{
processTestName(name);
SignalList signalList;
IndexFinder topLeftFinder = parentFinder;
topLeftFinder.appendRow( top );
IndexFinder bottomRightFinder = parentFinder;
bottomRightFinder.appendRow( bottom );
signalList << m_proxyModelTest->getSignal(DataChanged, topLeftFinder, bottomRightFinder);
- QTest::newRow(name.toAscii()) << signalList << PersistentChangeList();
+ QTest::newRow(name.toLatin1()) << signalList << PersistentChangeList();
}
void noop_testInsertWhenEmptyData()
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
noopTest("insert01");
noopTest("insert02");
noopTest("insert03");
}
void noop_testInsertInRootData()
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
// These commands have no effect because this model shows children of selection.
noopTest("insert01");
noopTest("insert02");
noopTest("insert03");
noopTest("insert04");
noopTest("insert05");
noopTest("insert06");
noopTest("insert07");
noopTest("insert08");
noopTest("insert09");
noopTest("insert10");
noopTest("insert11");
noopTest("insert12");
noopTest("insert13");
noopTest("insert14");
noopTest("insert15");
noopTest("insert16");
noopTest("insert17");
noopTest("insert18");
}
void noop_testInsertInTopLevelData()
{
// Same test names etc.
noop_testInsertInRootData();
}
void noop_testInsertInSecondLevelData()
{
noop_testInsertInRootData();
}
void noop_testRemoveFromRootData()
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
// These commands have no effect because this model shows children of selection.
noopTest("remove01");
noopTest("remove02");
noopTest("remove03");
}
void noop_testRemoveFromTopLevelData()
{
// Same test names etc.
noop_testRemoveFromRootData();
}
void noop_testRemoveFromSecondLevelData()
{
noop_testRemoveFromRootData();
}
void noop_testMoveFromRootData()
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
// These commands have no effect because this model shows children of selection.
noopLayoutChangeTest("move01");
noopLayoutChangeTest("move02");
noopLayoutChangeTest("move03");
noopLayoutChangeTest("move04");
noopLayoutChangeTest("move05");
}
void noop_testMoveFromTopLevelData()
{
// Same test names etc.
noop_testMoveFromRootData();
}
void noop_testMoveFromSecondLevelData()
{
noop_testMoveFromRootData();
}
void noop_testModifyInRootData()
{
QTest::addColumn<SignalList>("signalList");
QTest::addColumn<PersistentChangeList>("changeList");
noopTest("modify01");
noopTest("modify02");
noopTest("modify03");
noopTest("modify04");
noopTest("modify05");
noopTest("modify06");
noopTest("modify07");
}
void noop_testModifyInTopLevelData()
{
// Same test names etc.
noop_testModifyInRootData();
}
void noop_testModifyInSecondLevelData()
{
noop_testModifyInRootData();
}
ProxyModelTest *m_proxyModelTest;
QString m_currentTestFunction;
QSet<QString> m_testNames;
};
PROXYMODELTESTSUITE_EXPORT uint qHash( const QVariant & var );
#define PROXYMODELTEST(TestData, TemplateArg, IntermediateProxy, LazyPersistence, Config) \
if (testObjects.isEmpty() || testObjects.contains(testNum)) { \
proxyModelTestClass->setTestData(new TestData<TemplateArg>(proxyModelTestClass)); \
proxyModelTestClass->setUseIntermediateProxy(IntermediateProxy); \
proxyModelTestClass->setLazyPersistence(LazyPersistence); \
- qDebug() << "\n Running" << proxyModelTestClass->objectName().toAscii() << testNum << ":\n" \
+ qDebug() << "\n Running" << proxyModelTestClass->objectName().toLatin1() << testNum << ":\n" \
<< " Source Model: " << #IntermediateProxy << "\n" \
<< " Persistence: " << #LazyPersistence << "\n" \
Config; \
result = QTest::qExec(proxyModelTestClass, arguments); \
if (result != 0) \
return result; \
} \
++testNum; \
#define PROXYMODELTEST_CUSTOM(TestData, IntermediateProxy, LazyPersistence, Config) \
if (testObjects.isEmpty() || testObjects.contains(testNum)) { \
proxyModelTestClass->setTestData(TestData); \
proxyModelTestClass->setUseIntermediateProxy(IntermediateProxy); \
proxyModelTestClass->setLazyPersistence(LazyPersistence); \
- qDebug() << "\n Running" << proxyModelTestClass->objectName().toAscii() << testNum << ":\n" \
+ qDebug() << "\n Running" << proxyModelTestClass->objectName().toLatin1() << testNum << ":\n" \
<< " Source Model: " << #IntermediateProxy << "\n" \
<< " Persistence: " << #LazyPersistence << "\n" \
Config; \
result = QTest::qExec(proxyModelTestClass, arguments); \
if (result != 0) \
return result; \
} \
++testNum; \
// The DynamicTreeModel uses a unique internalId for the first column of each row.
// In the QSortFilterProxyModel the internalId is shared between all rows of the same parent.
// We test the proxy on top of both so that we know it is not using the internalId of its source model
// which will be different each time the test is run.
#define COMPLETETEST(TestData, TemplateArg, Config) \
PROXYMODELTEST(TestData, TemplateArg, DynamicTree, ImmediatePersistence, Config) \
PROXYMODELTEST(TestData, TemplateArg, IntermediateProxy, ImmediatePersistence, Config) \
PROXYMODELTEST(TestData, TemplateArg, DynamicTree, LazyPersistence, Config) \
PROXYMODELTEST(TestData, TemplateArg, IntermediateProxy, LazyPersistence, Config) \
#define PROXYMODELTEST_MAIN(TestClass, Body) \
int main(int argc, char *argv[]) \
{ \
QApplication app(argc, argv); \
QList<int> testObjects; \
QStringList arguments; \
bool ok; \
Q_FOREACH(const QString &arg, app.arguments()) \
{ \
int testObject = arg.toInt(&ok); \
if (arg == "-count") \
continue; \
if (!ok) \
{ \
arguments.append(arg); \
continue; \
} \
testObjects.append(testObject); \
} \
TestClass *proxyModelTestClass = new TestClass(); \
proxyModelTestClass->setObjectName( #TestClass ); \
int result = 0; \
int testNum = 1; \
\
Body \
\
delete proxyModelTestClass; \
return result; \
} \
#endif
diff --git a/tier1/kjs/autotests/ecmatest.cpp b/tier1/kjs/autotests/ecmatest.cpp
index 0cb8bf895f..161dc1f59d 100644
--- a/tier1/kjs/autotests/ecmatest.cpp
+++ b/tier1/kjs/autotests/ecmatest.cpp
@@ -1,359 +1,359 @@
/*
* This file is part of the KDE libraries
*
* Copyright (C) 2012 Rolf Eike Beer <kde@opensource.sf-tec.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/
#include "ecmatest.h"
#include <QtTest>
#include "../../kde_qt5_compat.h" // QSKIP_PORTING
#include <wtf/HashTraits.h>
#include "JSLock.h"
#include "object.h"
#include "JSVariableObject.h"
#include "Parser.h"
#include <QtCore/QMap>
#include <QtCore/QDebug>
// from khtml/ecma/kjs_binding.cpp"
KJS::UString::UString(const QString &d)
{
unsigned int len = d.length();
KJS::UChar *dat = static_cast<KJS::UChar*>(fastMalloc(sizeof(KJS::UChar)*len));
memcpy(dat, d.unicode(), len * sizeof(KJS::UChar));
m_rep = KJS::UString::Rep::create(dat, len);
}
QString KJS::UString::qstring() const
{
return QString((QChar*) data(), size());
}
// from khtml/ecma/debugger/value2string.cpp
QString valueToString(KJS::JSValue* value)
{
switch(value->type())
{
case KJS::NumberType:
{
double v = 0.0;
value->getNumber(v);
return QString::number(v);
}
case KJS::BooleanType:
return value->getBoolean() ? "true" : "false";
case KJS::StringType:
{
KJS::UString s;
value->getString(s);
return '"' + s.qstring() + '"';
}
case KJS::UndefinedType:
return "undefined";
case KJS::NullType:
return "null";
case KJS::ObjectType:
return "[object " + static_cast<KJS::JSObject*>(value)->className().qstring() +"]";
case KJS::GetterSetterType:
case KJS::UnspecifiedType:
default:
return QString();
}
}
// from khtml/ecma/debugger/debugwindow.cpp
static QString exceptionToString(KJS::ExecState* exec, KJS::JSValue* exceptionObj)
{
QString exceptionMsg = valueToString(exceptionObj);
// Since we purposefully bypass toString, we need to figure out
// string serialization ourselves.
//### might be easier to export class info for ErrorInstance ---
KJS::JSObject* valueObj = exceptionObj->getObject();
KJS::JSValue* protoObj = valueObj ? valueObj->prototype() : 0;
bool exception = false;
bool syntaxError = false;
if (protoObj == exec->lexicalInterpreter()->builtinSyntaxErrorPrototype())
{
exception = true;
syntaxError = true;
}
if (protoObj == exec->lexicalInterpreter()->builtinErrorPrototype() ||
protoObj == exec->lexicalInterpreter()->builtinEvalErrorPrototype() ||
protoObj == exec->lexicalInterpreter()->builtinReferenceErrorPrototype() ||
protoObj == exec->lexicalInterpreter()->builtinRangeErrorPrototype() ||
protoObj == exec->lexicalInterpreter()->builtinTypeErrorPrototype() ||
protoObj == exec->lexicalInterpreter()->builtinURIErrorPrototype())
{
exception = true;
}
if (!exception)
return exceptionMsg;
// Clear exceptions temporarily so we can get/call a few things.
// We memorize the old exception first, of course. Note that
// This is not always the same as exceptionObj since we may be
// asked to translate a non-active exception
KJS::JSValue* oldExceptionObj = exec->exception();
exec->clearException();
// We want to serialize the syntax errors ourselves, to provide the line number.
// The URL is in "sourceURL" and the line is in "line"
// ### TODO: Perhaps we want to use 'sourceId' in case of eval contexts.
if (syntaxError)
{
KJS::JSValue* lineValue = valueObj->get(exec, "line");
KJS::JSValue* urlValue = valueObj->get(exec, "sourceURL");
int line = lineValue->toNumber(exec);
QString url = urlValue->toString(exec).qstring();
exceptionMsg = QString::fromLatin1("Parse error at %1 line %2").arg(url).arg(line + 1);
}
else
{
// ### it's still not 100% safe to call toString here, even on
// native exception objects, since someone might have changed the toString property
// of the exception prototype, but I'll punt on this case for now.
exceptionMsg = exceptionObj->toString(exec).qstring();
}
exec->setException(oldExceptionObj);
return exceptionMsg;
}
class GlobalImp : public KJS::JSGlobalObject {
public:
virtual KJS::UString className() const { return "global"; }
};
static GlobalImp* global;
static QString basedir( "" );
static QByteArray testrunner;
static QMap<QByteArray, QByteArray> includes;
static QStringList expectedBroken; // list of tests we know that will fail
/**
* load the given file from the harness directory
* @param fn filename
* @return if operation succeeded
*
* Will load the given file into the "includes" map
*/
static bool loadInclude( const QByteArray &fn )
{
QFile runnerfile( basedir + QLatin1String( "test/harness/" ) + QString::fromLatin1( fn.constData() ) );
if ( !runnerfile.open( QIODevice::ReadOnly ) )
return false;
includes[ fn ] = runnerfile.readAll();
return true;
}
QTEST_MAIN(ECMAscriptTest)
void ECMAscriptTest::initTestCase()
{
basedir = QString::fromUtf8( qgetenv( "ECMATEST_BASEDIR" ).constData() );
if ( basedir.isEmpty() )
qFatal( "ECMATEST_BASEDIR not set" );
if ( !basedir.endsWith( QLatin1Char( '/' ) ) )
basedir += QLatin1Char( '/' );
QVERIFY( loadInclude( "sta.js" ) );
QVERIFY( loadInclude( "ed.js" ) );
testrunner = includes[ "sta.js" ] + includes[ "ed.js" ] + '\n';
- const QString brokenFn = QString::fromAscii( qgetenv( "ECMATEST_BROKEN" ).constData() );
+ const QString brokenFn = QString::fromLatin1( qgetenv( "ECMATEST_BROKEN" ).constData() );
if ( !brokenFn.isEmpty() ) {
QFile brokenF( brokenFn );
if ( !brokenF.open( QIODevice::ReadOnly ) ) {
const QByteArray errmsg = QByteArray("cannot open ") + QFile::encodeName(brokenFn);
QWARN( errmsg.constData() );
} else {
- expectedBroken = QString::fromAscii( brokenF.readAll().constData() ).split( QLatin1Char( '\n' ) )
+ expectedBroken = QString::fromLatin1( brokenF.readAll().constData() ).split( QLatin1Char( '\n' ) )
.filter( QRegExp( "^[^#].*" ) );
}
}
m_passed = 0;
}
static QByteArray getTextProperty( const QByteArray &property, const QByteArray &code )
{
int from = code.indexOf( property );
if ( from == -1 )
return QByteArray();
from += property.length();
while ( code[ from ] == ' ' )
from++;
int to = code.indexOf( '\n', from );
if (code[to - 1] == '\r')
to--;
// poor mans escaping
return code.mid( from, to - from ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" );
}
#define ECMATEST_VERIFY( expr ) \
do { \
const bool tmp_result = ( expr ); \
if ( tmp_result ) \
m_passed++; \
else \
m_failed++; \
if ( knownBroken ) \
QEXPECT_FAIL(QTest::currentDataTag(), "It is known that KJS doesn't pass this test", Abort); \
QVERIFY( tmp_result ); \
} while (0)
static QMap< QByteArray, QByteArray > skips;
void ECMAscriptTest::runAllTests()
{
static const QByteArray include = "$INCLUDE(\"";
QFETCH(QString, filename);
QByteArray expectedError;
QFile input( filename );
Q_FOREACH( const QByteArray &skip, skips.keys() ) {
if ( skip == QTest::currentDataTag() )
QSKIP_PORTING( skips[ skip ].constData(), SkipSingle );
}
QVERIFY( input.open( QIODevice::ReadOnly ) );
const QByteArray testdata = input.readAll();
QVERIFY( ! testdata.isEmpty() );
RefPtr<KJS::Interpreter> interp = new KJS::Interpreter(global);
KJS::Interpreter::setShouldPrintExceptions(true);
QByteArray testscript;
// test is expected to fail
if ( testdata.indexOf( "@negative" ) >= 0 ) {
expectedError = getTextProperty( "@negative", testdata );
if ( expectedError.isEmpty() )
expectedError = ".";
}
int from = 0;
while ( ( from = testdata.indexOf( include, from ) ) >= 0 ) {
int endq = testdata.indexOf( "\"", from + include.length() );
QVERIFY( endq >= 0 );
const QByteArray includeFile = testdata.mid( from + include.length(), endq - from - include.length() );
if ( ! includes.contains( includeFile ) )
QVERIFY( loadInclude( includeFile ) );
testscript += includes[ includeFile ];
from = endq;
}
testscript += testrunner;
testscript += testdata;
const QFileInfo info( input );
const QString scriptutf = QString::fromUtf8( testscript.constData() );
- KJS::Completion completion = interp->evaluate(info.fileName().toAscii().constData(), 0, scriptutf);
+ KJS::Completion completion = interp->evaluate(info.fileName().toLatin1().constData(), 0, scriptutf);
- const bool knownBroken = expectedBroken.contains( QString::fromAscii( QTest::currentDataTag() ) );
+ const bool knownBroken = expectedBroken.contains( QString::fromLatin1( QTest::currentDataTag() ) );
if ( expectedError.isEmpty() ) {
ECMATEST_VERIFY( completion.complType() != KJS::Throw );
} else {
if ( knownBroken && completion.complType() != KJS::Throw ) {
QEXPECT_FAIL(QTest::currentDataTag(), "It is known that KJS doesn't pass this test", Abort);
m_failed++;
}
QCOMPARE( completion.complType(), KJS::Throw );
QVERIFY( completion.value() != NULL );
const QString eMsg = exceptionToString( interp->execState(), completion.value() );
if ( expectedError == "^((?!NotEarlyError).)*$" ) {
ECMATEST_VERIFY( eMsg.indexOf( "NotEarlyError" ) == -1 );
} else if ( expectedError == "." ) {
// means "every exception passes
} else {
ECMATEST_VERIFY( eMsg.indexOf( expectedError ) >= 0 );
}
}
}
void ECMAscriptTest::runAllTests_data()
{
global = new GlobalImp();
QTest::addColumn<QString>( "filename" );
const QStringList js( QLatin1String( "*.js" ) );
const QStringList all( QLatin1String( "*" ) );
- const QString chapter = QString::fromAscii( qgetenv( "ECMATEST_CHAPTER" ).constData() );
+ const QString chapter = QString::fromLatin1( qgetenv( "ECMATEST_CHAPTER" ).constData() );
if ( !chapter.isEmpty() )
- QWARN( QByteArray("===> Testing chapter " + chapter.toAscii()).constData() );
+ QWARN( QByteArray("===> Testing chapter " + chapter.toLatin1()).constData() );
// some tests fail when the suite is run as a whole
if ( chapter.isEmpty() || chapter.startsWith("ch15") ) {
const QByteArray endlessLoop = "this test causes an endless loop, avoid it for the moment";
skips[ "S15.1.2.3_A6" ] = endlessLoop;
skips[ "S15.1.3.1_A2.5_T1" ] = endlessLoop;
skips[ "S15.1.3.2_A2.4_T1" ] = endlessLoop;
skips[ "S15.1.3.2_A2.5_T1" ] = endlessLoop;
skips[ "15.2.3.4-4-1" ] = "this test causes a crash when run as part of the whole suite";
}
QDirIterator it( basedir + QLatin1String("test/suite/") + chapter, QDirIterator::Subdirectories);
while ( it.hasNext() ) {
it.next();
const QFileInfo info = it.fileInfo();
if ( !info.isFile() )
continue;
QString filename = info.fileName();
filename.chop(3); // .js
- QTest::newRow( filename.toAscii().constData() ) << info.absoluteFilePath();
+ QTest::newRow( filename.toLatin1().constData() ) << info.absoluteFilePath();
}
}
void ECMAscriptTest::cleanup()
{
global->clearProperties();
}
void ECMAscriptTest::cleanupTestCase()
{
qDebug() << "passed testcases:" << m_passed << "failed testcases:" << m_failed;
}
#include "ecmatest.moc"
diff --git a/tier1/kwidgetsaddons/src/kruler.cpp b/tier1/kwidgetsaddons/src/kruler.cpp
index 3366f7682b..e0fc31c241 100644
--- a/tier1/kwidgetsaddons/src/kruler.cpp
+++ b/tier1/kwidgetsaddons/src/kruler.cpp
@@ -1,774 +1,774 @@
/* This file is part of the KDE libraries
Copyright (C) 1998 Jörg Habenicht (j.habenicht@europemail.com)
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 "kruler.h"
#include <QFont>
#include <QPolygon>
#include <QStylePainter>
#define INIT_VALUE 0
#define INIT_MIN_VALUE 0
#define INIT_MAX_VALUE 100
#define INIT_TINY_MARK_DISTANCE 1
#define INIT_LITTLE_MARK_DISTANCE 5
#define INIT_MIDDLE_MARK_DISTANCE (INIT_LITTLE_MARK_DISTANCE * 2)
#define INIT_BIG_MARK_DISTANCE (INIT_LITTLE_MARK_DISTANCE * 10)
#define INIT_SHOW_TINY_MARK false
#define INIT_SHOW_LITTLE_MARK true
#define INIT_SHOW_MEDIUM_MARK true
#define INIT_SHOW_BIG_MARK true
#define INIT_SHOW_END_MARK true
#define INIT_SHOW_POINTER true
#define INIT_SHOW_END_LABEL true
#define INIT_PIXEL_PER_MARK (double)10.0 /* distance between 2 base marks in pixel */
#define INIT_OFFSET (-20)
#define INIT_LENGTH_FIX true
#define INIT_END_OFFSET 0
#define FIX_WIDTH 20 /* widget width in pixel */
#define LINE_END (FIX_WIDTH - 3)
#define END_MARK_LENGTH (FIX_WIDTH - 6)
#define END_MARK_X2 LINE_END
#define END_MARK_X1 (END_MARK_X2 - END_MARK_LENGTH)
#define BIG_MARK_LENGTH (END_MARK_LENGTH*3/4)
#define BIG_MARK_X2 LINE_END
#define BIG_MARK_X1 (BIG_MARK_X2 - BIG_MARK_LENGTH)
#define MIDDLE_MARK_LENGTH (END_MARK_LENGTH/2)
#define MIDDLE_MARK_X2 LINE_END
#define MIDDLE_MARK_X1 (MIDDLE_MARK_X2 - MIDDLE_MARK_LENGTH)
#define LITTLE_MARK_LENGTH (MIDDLE_MARK_LENGTH/2)
#define LITTLE_MARK_X2 LINE_END
#define LITTLE_MARK_X1 (LITTLE_MARK_X2 - LITTLE_MARK_LENGTH)
#define BASE_MARK_LENGTH (LITTLE_MARK_LENGTH/2)
#define BASE_MARK_X2 LINE_END
#define BASE_MARK_X1 (BASE_MARK_X2 - BASE_MARK_LENGTH)
#define LABEL_SIZE 8
#define END_LABEL_X 4
#define END_LABEL_Y (END_LABEL_X + LABEL_SIZE - 2)
#undef PROFILING
#ifdef PROFILING
# include <qdatetime.h>
#endif
class KRuler::KRulerPrivate
{
public:
int endOffset_length; /* marks the offset at the end of the ruler
* i.e. right side at horizontal and down side
* at vertical rulers.
* the ruler end mark is moved endOffset_length
* ticks away from the widget end.
* positive offset moves end mark inside the ruler.
* if lengthFix is true, endOffset_length holds the
* length of the ruler.
*/
int fontWidth; // ONLY valid for vertical rulers
QAbstractSlider range;
Qt::Orientation dir;
int tmDist;
int lmDist;
int mmDist;
int bmDist;
int offset;
bool showtm : 1; /* show tiny, little, medium, big, endmarks */
bool showlm : 1;
bool showmm : 1;
bool showbm : 1;
bool showem : 1;
bool showpointer : 1;
bool showEndL : 1;
bool lengthFix : 1;
double ppm; /* pixel per mark */
QString endlabel;
};
KRuler::KRuler(QWidget *parent)
: QAbstractSlider(parent)
, d( new KRulerPrivate )
{
setRange(INIT_MIN_VALUE, INIT_MAX_VALUE);
setPageStep(10);
setValue(INIT_VALUE);
initWidget(Qt::Horizontal);
setFixedHeight(FIX_WIDTH);
}
KRuler::KRuler(Qt::Orientation orient,
- QWidget *parent, Qt::WFlags f)
+ QWidget *parent, Qt::WindowFlags f)
: QAbstractSlider(parent)
, d( new KRulerPrivate )
{
setRange(INIT_MIN_VALUE, INIT_MAX_VALUE);
setPageStep(10);
setValue(INIT_VALUE);
setWindowFlags(f);
initWidget(orient);
if (orient == Qt::Horizontal)
setFixedHeight(FIX_WIDTH);
else
setFixedWidth(FIX_WIDTH);
}
KRuler::KRuler(Qt::Orientation orient, int widgetWidth,
- QWidget *parent, Qt::WFlags f)
+ QWidget *parent, Qt::WindowFlags f)
: QAbstractSlider(parent)
, d( new KRulerPrivate )
{
setRange(INIT_MIN_VALUE, INIT_MAX_VALUE);
setPageStep(10);
setValue(INIT_VALUE);
setWindowFlags(f);
initWidget(orient);
if (orient == Qt::Horizontal)
setFixedHeight(widgetWidth);
else
setFixedWidth(widgetWidth);
}
void KRuler::initWidget(Qt::Orientation orientation)
{
#ifdef __GNUC__
#warning FIXME setFrameStyle(WinPanel | Raised);
#endif
d->showpointer = INIT_SHOW_POINTER;
d->showEndL = INIT_SHOW_END_LABEL;
d->lengthFix = INIT_LENGTH_FIX;
d->endOffset_length = INIT_END_OFFSET;
d->tmDist = INIT_TINY_MARK_DISTANCE;
d->lmDist = INIT_LITTLE_MARK_DISTANCE;
d->mmDist = INIT_MIDDLE_MARK_DISTANCE;
d->bmDist = INIT_BIG_MARK_DISTANCE;
d->offset= INIT_OFFSET;
d->showtm = INIT_SHOW_TINY_MARK;
d->showlm = INIT_SHOW_LITTLE_MARK;
d->showmm = INIT_SHOW_MEDIUM_MARK;
d->showbm = INIT_SHOW_BIG_MARK;
d->showem = INIT_SHOW_END_MARK;
d->ppm = INIT_PIXEL_PER_MARK;
d->dir = orientation;
}
KRuler::~KRuler()
{
delete d;
}
#ifndef KDE_NO_DEPRECATED
void
KRuler::setMinValue(int value)
{
setMinimum(value);
}
#endif
#ifndef KDE_NO_DEPRECATED
int
KRuler::minValue() const
{ return minimum(); }
#endif
#ifndef KDE_NO_DEPRECATED
void
KRuler::setMaxValue(int value)
{
setMaximum(value);
}
#endif
#ifndef KDE_NO_DEPRECATED
int
KRuler::maxValue() const
{ return maximum(); }
#endif
void
KRuler::setTinyMarkDistance(int dist)
{
if (dist != d->tmDist) {
d->tmDist = dist;
update(contentsRect());
}
}
int
KRuler::tinyMarkDistance() const
{ return d->tmDist; }
void
KRuler::setLittleMarkDistance(int dist)
{
if (dist != d->lmDist) {
d->lmDist = dist;
update(contentsRect());
}
}
int
KRuler::littleMarkDistance() const
{ return d->lmDist; }
void
KRuler::setMediumMarkDistance(int dist)
{
if (dist != d->mmDist) {
d->mmDist = dist;
update(contentsRect());
}
}
int
KRuler::mediumMarkDistance() const
{ return d->mmDist; }
void
KRuler::setBigMarkDistance(int dist)
{
if (dist != d->bmDist) {
d->bmDist = dist;
update(contentsRect());
}
}
int
KRuler::bigMarkDistance() const
{ return d->bmDist; }
void
KRuler::setShowTinyMarks(bool show)
{
if (show != d->showtm) {
d->showtm = show;
update(contentsRect());
}
}
bool
KRuler::showTinyMarks() const
{
return d->showtm;
}
void
KRuler::setShowLittleMarks(bool show)
{
if (show != d->showlm) {
d->showlm = show;
update(contentsRect());
}
}
bool
KRuler::showLittleMarks() const
{
return d->showlm;
}
void
KRuler::setShowMediumMarks(bool show)
{
if (show != d->showmm) {
d->showmm = show;
update(contentsRect());
}
}
bool
KRuler::showMediumMarks() const
{
return d->showmm;
}
void
KRuler::setShowBigMarks(bool show)
{
if (show != d->showbm) {
d->showbm = show;
update(contentsRect());
}
}
bool
KRuler::showBigMarks() const
{
return d->showbm;
}
void
KRuler::setShowEndMarks(bool show)
{
if (show != d->showem) {
d->showem = show;
update(contentsRect());
}
}
bool
KRuler::showEndMarks() const
{
return d->showem;
}
void
KRuler::setShowPointer(bool show)
{
if (show != d->showpointer) {
d->showpointer = show;
update(contentsRect());
}
}
bool
KRuler::showPointer() const
{
return d->showpointer;
}
void
KRuler::setFrameStyle(int)
{
#ifdef __GNUC__
#warning implement me (jowenn)
#endif
}
void
KRuler::setShowEndLabel(bool show)
{
if (d->showEndL != show) {
d->showEndL = show;
update(contentsRect());
}
}
bool
KRuler::showEndLabel() const
{
return d->showEndL;
}
void
KRuler::setEndLabel(const QString& label)
{
d->endlabel = label;
// premeasure the fontwidth and save it
if (d->dir == Qt::Vertical) {
QFont font = this->font();
font.setPointSize(LABEL_SIZE);
QFontMetrics fm(font);
d->fontWidth = fm.width(d->endlabel);
}
update(contentsRect());
}
QString KRuler::endLabel() const
{
return d->endlabel;
}
void
KRuler::setRulerMetricStyle(KRuler::MetricStyle style)
{
switch (style) {
default: /* fall through */
case Custom:
return;
case Pixel:
setLittleMarkDistance(1);
setMediumMarkDistance(5);
setBigMarkDistance(10);
setShowTinyMarks(false);
setShowLittleMarks(true);
setShowMediumMarks(true);
setShowBigMarks(true);
setShowEndMarks(true);
update(contentsRect());
setPixelPerMark(10.0);
break;
case Inch:
setTinyMarkDistance(1);
setLittleMarkDistance(2);
setMediumMarkDistance(4);
setBigMarkDistance(8);
setShowTinyMarks(true);
setShowLittleMarks(true);
setShowMediumMarks(true);
setShowBigMarks(true);
setShowEndMarks(true);
update(contentsRect());
setPixelPerMark(9.0);
break;
case Millimetres: /* fall through */
case Centimetres: /* fall through */
case Metres:
setLittleMarkDistance(1);
setMediumMarkDistance(5);
setBigMarkDistance(10);
setShowTinyMarks(false);
setShowLittleMarks(true);
setShowMediumMarks(true);
setShowBigMarks(true);
setShowEndMarks(true);
update(contentsRect());
setPixelPerMark(3.0);
}
switch (style) {
case Pixel:
setEndLabel(QLatin1String("pixel"));
break;
case Inch:
setEndLabel(QLatin1String("inch"));
break;
case Millimetres:
setEndLabel(QLatin1String("mm"));
break;
case Centimetres:
setEndLabel(QLatin1String("cm"));
break;
case Metres:
setEndLabel(QLatin1String("m"));
default: /* never reached, see above switch */
/* empty command */;
}
// if the style changes one of the values,
// update would have been called inside the methods
// -> no update() call needed here !
}
void
KRuler::setPixelPerMark(double rate)
{ // never compare floats against each other :)
d->ppm = rate;
update(contentsRect());
}
double
KRuler::pixelPerMark() const
{ return d->ppm; }
void
KRuler::setLength(int length)
{
int tmp;
if (d->lengthFix) {
tmp = length;
}
else {
tmp = width() - length;
}
if (tmp != d->endOffset_length) {
d->endOffset_length = tmp;
update(contentsRect());
}
}
int
KRuler::length() const
{
if (d->lengthFix) {
return d->endOffset_length;
}
return (width() - d->endOffset_length);
}
void
KRuler::setLengthFixed(bool fix)
{
d->lengthFix = fix;
}
bool
KRuler::lengthFixed() const
{
return d->lengthFix;
}
void
KRuler::setOffset(int _offset)
{// debug("set offset %i", _offset);
if (d->offset != _offset) {
d->offset = _offset;
update(contentsRect());
}
}
int
KRuler::offset() const
{ return d->offset; }
int
KRuler::endOffset() const
{
if (d->lengthFix) {
return (width() - d->endOffset_length);
}
return d->endOffset_length;
}
void
KRuler::slideUp(int count)
{
if (count) {
d->offset += count;
update(contentsRect());
}
}
void
KRuler::slideDown(int count)
{
if (count) {
d->offset -= count;
update(contentsRect());
}
}
void
KRuler::slotNewValue(int _value)
{
int oldvalue = value();
if (oldvalue == _value) {
return;
}
// setValue(_value);
setValue(_value);
if (value() == oldvalue) {
return;
}
// get the rectangular of the old and the new ruler pointer
// and repaint only him
if (d->dir == Qt::Horizontal) {
QRect oldrec(-5+oldvalue,10, 11,6);
QRect newrec(-5+_value,10, 11,6);
repaint( oldrec.united(newrec) );
}
else {
QRect oldrec(10,-5+oldvalue, 6,11);
QRect newrec(10,-5+_value, 6,11);
repaint( oldrec.united(newrec) );
}
}
void
KRuler::slotNewOffset(int _offset)
{
if (d->offset != _offset) {
//setOffset(_offset);
d->offset = _offset;
repaint(contentsRect());
}
}
void
KRuler::slotEndOffset(int offset)
{
int tmp;
if (d->lengthFix) {
tmp = width() - offset;
}
else {
tmp = offset;
}
if (d->endOffset_length != tmp) {
d->endOffset_length = tmp;
repaint(contentsRect());
}
}
void
KRuler::paintEvent(QPaintEvent * /*e*/)
{
// debug ("KRuler::drawContents, %s",(horizontal==dir)?"horizontal":"vertical");
QStylePainter p(this);
#ifdef PROFILING
QTime time;
time.start();
for (int profile=0; profile<10; profile++) {
#endif
int value = this->value(),
minval = minimum(),
maxval;
if (d->dir == Qt::Horizontal) {
maxval = maximum()
+ d->offset
- (d->lengthFix?(height()-d->endOffset_length):d->endOffset_length);
}
else
{
maxval = maximum()
+ d->offset
- (d->lengthFix?(width()-d->endOffset_length):d->endOffset_length);
}
//ioffsetval = value-offset;
// pixelpm = (int)ppm;
// left = clip.left(),
// right = clip.right();
double f, fend,
offsetmin=(double)(minval-d->offset),
offsetmax=(double)(maxval-d->offset),
fontOffset = (((double)minval)>offsetmin)?(double)minval:offsetmin;
// draw labels
QFont font = p.font();
font.setPointSize(LABEL_SIZE);
p.setFont( font );
// draw littlemarklabel
// draw mediummarklabel
// draw bigmarklabel
// draw endlabel
if (d->showEndL) {
if (d->dir == Qt::Horizontal) {
p.translate( fontOffset, 0 );
p.drawText( END_LABEL_X, END_LABEL_Y, d->endlabel );
}
else { // rotate text +pi/2 and move down a bit
//QFontMetrics fm(font);
#ifdef KRULER_ROTATE_TEST
p.rotate( -90.0 + rotate );
p.translate( -8.0 - fontOffset - d->fontWidth + xtrans,
ytrans );
#else
p.rotate( -90.0 );
p.translate( -8.0 - fontOffset - d->fontWidth, 0.0 );
#endif
p.drawText( END_LABEL_X, END_LABEL_Y, d->endlabel );
}
p.resetMatrix();
}
// draw the tiny marks
if (d->showtm) {
fend = d->ppm*d->tmDist;
for ( f=offsetmin; f<offsetmax; f+=fend ) {
if (d->dir == Qt::Horizontal) {
p.drawLine((int)f, BASE_MARK_X1, (int)f, BASE_MARK_X2);
}
else {
p.drawLine(BASE_MARK_X1, (int)f, BASE_MARK_X2, (int)f);
}
}
}
if (d->showlm) {
// draw the little marks
fend = d->ppm*d->lmDist;
for ( f=offsetmin; f<offsetmax; f+=fend ) {
if (d->dir == Qt::Horizontal) {
p.drawLine((int)f, LITTLE_MARK_X1, (int)f, LITTLE_MARK_X2);
}
else {
p.drawLine(LITTLE_MARK_X1, (int)f, LITTLE_MARK_X2, (int)f);
}
}
}
if (d->showmm) {
// draw medium marks
fend = d->ppm*d->mmDist;
for ( f=offsetmin; f<offsetmax; f+=fend ) {
if (d->dir == Qt::Horizontal) {
p.drawLine((int)f, MIDDLE_MARK_X1, (int)f, MIDDLE_MARK_X2);
}
else {
p.drawLine(MIDDLE_MARK_X1, (int)f, MIDDLE_MARK_X2, (int)f);
}
}
}
if (d->showbm) {
// draw big marks
fend = d->ppm*d->bmDist;
for ( f=offsetmin; f<offsetmax; f+=fend ) {
if (d->dir == Qt::Horizontal) {
p.drawLine((int)f, BIG_MARK_X1, (int)f, BIG_MARK_X2);
}
else {
p.drawLine(BIG_MARK_X1, (int)f, BIG_MARK_X2, (int)f);
}
}
}
if (d->showem) {
// draw end marks
if (d->dir == Qt::Horizontal) {
p.drawLine(minval-d->offset, END_MARK_X1, minval-d->offset, END_MARK_X2);
p.drawLine(maxval-d->offset, END_MARK_X1, maxval-d->offset, END_MARK_X2);
}
else {
p.drawLine(END_MARK_X1, minval-d->offset, END_MARK_X2, minval-d->offset);
p.drawLine(END_MARK_X1, maxval-d->offset, END_MARK_X2, maxval-d->offset);
}
}
// draw pointer
if (d->showpointer) {
QPolygon pa(4);
if (d->dir == Qt::Horizontal) {
pa.setPoints(3, value-5, 10, value+5, 10, value/*+0*/,15);
}
else {
pa.setPoints(3, 10, value-5, 10, value+5, 15, value/*+0*/);
}
p.setBrush( p.background().color() );
p.drawPolygon( pa );
}
#ifdef PROFILING
}
int elapsed = time.elapsed();
debug("paint time %i",elapsed);
#endif
}
diff --git a/tier1/kwidgetsaddons/src/kruler.h b/tier1/kwidgetsaddons/src/kruler.h
index 87a54429e2..30a3854a5b 100644
--- a/tier1/kwidgetsaddons/src/kruler.h
+++ b/tier1/kwidgetsaddons/src/kruler.h
@@ -1,431 +1,431 @@
/* -*- c++ -*- */
/* This file is part of the KDE libraries
Copyright (C) 1998 Jörg Habenicht (j.habenicht@europemail.com)
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 KRULER_H
#define KRULER_H
#include <kwidgetsaddons_export.h>
#include <QAbstractSlider>
/**
* A ruler widget.
*
* The vertical ruler looks similar to this:
*
*\code
* meters inches
*
* ------ <--- end mark ---> ------
* -- -
* -- <---little mark---> --
* -- -
* -- ---
* --- <---medium mark -
* -- --
* -- tiny mark----> -
* -- ----
* -- -
* ---- <-----big mark --
* -- -
* |>-- <--ruler pointer--> |>--
*
* \endcode
*
* There are tiny marks, little marks, medium marks, and big marks along the
* ruler.
*
* To receive mouse clicks or mouse moves,
* the class has to be overloaded.
*
* For performance reasons, the public methods don't call QWidget::repaint().
* (Slots do, see documentation below.)
* All the changed settings will be painted once after leaving
* to the main event loop.
* For performance painting the slot methods should be used,
* they do a fast QWidget::repaint() call after changing the values.
* For setting multiple values like minValue(), maxValue(), offset() etc.
* using the public methods is recommended
* so the widget will be painted only once when entering the main event loop.
*
* \image html kruler.png "KDE Ruler Widget"
*
* @short A ruler widget.
* @author Jörg Habenicht
*/
class KWIDGETSADDONS_EXPORT KRuler : public QAbstractSlider
{
Q_OBJECT
Q_PROPERTY( bool showTinyMarks READ showTinyMarks WRITE setShowTinyMarks )
Q_PROPERTY( bool showLittleMarks READ showLittleMarks WRITE setShowLittleMarks )
Q_PROPERTY( bool showMediumMarks READ showMediumMarks WRITE setShowMediumMarks )
Q_PROPERTY( bool showBigMarks READ showBigMarks WRITE setShowBigMarks )
Q_PROPERTY( bool showPointer READ showPointer WRITE setShowPointer )
Q_PROPERTY( bool showEndLabel READ showEndLabel WRITE setShowEndLabel )
Q_PROPERTY( int tinyMarkDistance READ tinyMarkDistance WRITE setTinyMarkDistance )
Q_PROPERTY( int littleMarkDistance READ littleMarkDistance WRITE setLittleMarkDistance )
Q_PROPERTY( int mediumMarkDistance READ mediumMarkDistance WRITE setBigMarkDistance )
Q_PROPERTY( int bigMarkDistance READ bigMarkDistance WRITE setBigMarkDistance )
Q_PROPERTY( double pixelPerMark READ pixelPerMark WRITE setPixelPerMark )
Q_PROPERTY( bool lengthFixed READ lengthFixed WRITE setLengthFixed )
Q_PROPERTY( QString endLabel READ endLabel WRITE setEndLabel )
Q_ENUMS( MetricStyle )
Q_PROPERTY( int length READ length WRITE setLength )
Q_PROPERTY( int offset READ offset )
Q_PROPERTY( int endOffset READ endOffset )
public:
/*
#define KRULER_ROTATE_TEST KRULER_ROTATE_TEST
#undef KRULER_ROTATE_TEST
#ifdef KRULER_ROTATE_TEST
double xtrans, ytrans, rotate;
# warning tmporaer variablen eingeschaltet
#endif
*/
/**
* The types of units used.
**/
enum MetricStyle { Custom=0, Pixel, Inch, Millimetres, Centimetres, Metres };
/**
* The style (or look) of the ruler.
**/
// enum PaintStyle { Flat, Raised, Sunken };
/**
* Constructs a horizontal ruler.
*/
explicit KRuler(QWidget *parent=0);
/**
* Constructs a ruler with orientation @p orient.
*
* @p parent and @p f are passed to QFrame.
* The default look is a raised widget
* but may be changed with the inherited QFrame methods.
*
* @param orient Orientation of the ruler.
* @param parent Will be handed over to QFrame.
* @param f Will be handed over to QFrame.
*
**/
- explicit KRuler(Qt::Orientation orient, QWidget *parent=0, Qt::WFlags f=0);
+ explicit KRuler(Qt::Orientation orient, QWidget *parent=0, Qt::WindowFlags f=0);
/**
* Constructs a ruler with orientation @p orient and initial width @p widgetWidth.
*
* The width sets the fixed width of the widget. This is useful if you
* want to draw the ruler bigger or smaller than the default size.
* Note: The size of the marks doesn't change.
* @p parent and @p f are passed to QFrame.
*
* @param orient Orientation of the ruler.
* @param widgetWidth Fixed width of the widget.
* @param parent Will be handed over to QFrame.
* @param f Will be handed over to QFrame.
*
*/
KRuler(Qt::Orientation orient, int widgetWidth, QWidget *parent=0,
- Qt::WFlags f=0);
+ Qt::WindowFlags f=0);
/**
* Destructor.
*/
~KRuler();
/**
* Sets the minimal value of the ruler pointer (default is 0).
*
* This method calls update() so that the widget is painted after leaving
* to the main event loop.
*
**/
#ifndef KDE_NO_DEPRECATED
KWIDGETSADDONS_DEPRECATED void setMinValue(int);
#endif
/**
* Returns the minimal value of the ruler pointer.
**/
#ifndef KDE_NO_DEPRECATED
KWIDGETSADDONS_DEPRECATED int minValue() const;
#endif
/**
* Sets the maximum value of the ruler pointer (default is 100).
*
* This method calls update() so that the widget is painted after leaving
* to the main event loop.
*/
#ifndef KDE_NO_DEPRECATED
KWIDGETSADDONS_DEPRECATED void setMaxValue(int);
#endif
/**
* Returns the maximal value of the ruler pointer.
*/
#ifndef KDE_NO_DEPRECATED
KWIDGETSADDONS_DEPRECATED int maxValue() const;
#endif
/**
* Sets the distance between tiny marks.
*
* This is mostly used in the English system (inches) with distance of 1.
*/
void setTinyMarkDistance(int);
/**
* Returns the distance between tiny marks.
**/
int tinyMarkDistance() const;
/**
* Sets the distance between little marks.
*
* The default value is 1 in the metric system and 2 in the English (inches) system.
*/
void setLittleMarkDistance(int);
/**
* Returns the distance between little marks.
*/
int littleMarkDistance() const;
/**
* Sets the distance between medium marks.
*
* For English (inches) styles it defaults to twice the little mark distance.
* For metric styles it defaults to five times the little mark distance.
**/
void setMediumMarkDistance(int);
int mediumMarkDistance() const;
/**
* Sets distance between big marks.
*
* For English (inches) or metric styles it is twice the medium mark distance.
**/
void setBigMarkDistance(int);
/**
* Returns the distance between big marks.
**/
int bigMarkDistance() const;
/**
* Shows/hides tiny marks.
**/
void setShowTinyMarks(bool);
bool showTinyMarks() const;
/**
* Shows/hides little marks.
**/
void setShowLittleMarks(bool);
bool showLittleMarks() const;
/**
* Shows/hides medium marks.
**/
void setShowMediumMarks(bool);
bool showMediumMarks() const;
/**
* Shows/hides big marks.
**/
void setShowBigMarks(bool);
bool showBigMarks() const;
/**
* Shows/hides end marks.
**/
void setShowEndMarks(bool);
bool showEndMarks() const;
/**
* Shows/hides the pointer.
*/
void setShowPointer(bool);
bool showPointer() const;
void setFrameStyle(int);
/**
* Show/hide number values of the little marks.
*
* Default is @p false.
**/
// void setShowLittleMarkLabel(bool);
/**
* Show/hide number values of the medium marks.
*
* Default is @p false.
**/
// void setShowMediumMarkLabel(bool);
/**
* Show/hide number values of the big marks.
*
* Default is @p false.
**/
// void showBigMarkLabel(bool);
/**
* Show/hide number values of the end marks.
*
* Default is @p false.
**/
void setShowEndLabel(bool);
bool showEndLabel() const;
/**
* Sets the label this is drawn at the beginning of the visible part
* of the ruler to @p label
**/
void setEndLabel(const QString&);
QString endLabel() const;
/**
* Sets up the necessary tasks for the provided styles.
*
* A convenience method.
**/
void setRulerMetricStyle(KRuler::MetricStyle);
/**
* Sets the number of pixels between two base marks.
*
* Calling this method stretches or shrinks your ruler.
*
* For pixel display ( MetricStyle) the value is 10.0 marks
* per pixel ;-)
* For English (inches) it is 9.0, and for centimetres ~2.835 -> 3.0 .
* If you want to magnify your part of display, you have to
* adjust the mark distance @p here.
* Notice: The double type is only supported to give the possibility
* of having some double values.
* It should be used with care. Using values below 10.0
* shows visible jumps of markpositions (e.g. 2.345).
* Using whole numbers is highly recommended.
* To use @p int values use setPixelPerMark((int)your_int_value);
* default: 1 mark per 10 pixels
*/
void setPixelPerMark(double rate);
/**
* Returns the number of pixels between two base marks.
**/
double pixelPerMark() const;
/**
* Sets the length of the ruler, i.e. the difference between
* the begin mark and the end mark of the ruler.
*
* Same as (width() - offset())
*
* when the length is not locked, it gets adjusted with the
* length of the widget.
*/
void setLength(int);
int length() const;
/**
* Locks the length of the ruler, i.e. the difference between
* the two end marks doesn't change when the widget is resized.
*
* @param fix fixes the length, if true
*/
void setLengthFixed(bool fix);
bool lengthFixed() const;
/**
* Sets the number of pixels by which the ruler may slide up or left.
* The number of pixels moved is realive to the previous position.
* The Method makes sense for updating a ruler, which is working with
* a scrollbar.
*
* This doesn't affect the position of the ruler pointer.
* Only the visible part of the ruler is moved.
*
* @param count Number of pixel moving up or left relative to the previous position
**/
void slideUp(int count = 1);
/**
* Sets the number of pixels by which the ruler may slide down or right.
* The number of pixels moved is realive to the previous position.
* The Method makes sense for updating a ruler, which is working with
* a scrollbar.
*
* This doesn't affect the position of the ruler pointer.
* Only the visible part of the ruler is moved.
*
* @param count Number of pixel moving up or left relative to the previous position
**/
void slideDown(int count = 1);
/**
* Sets the ruler slide offset.
*
* This is like slideup() or slidedown() with an absolute offset
* from the start of the ruler.
*
* @param offset Number of pixel to move the ruler up or left from the beginning
**/
void setOffset(int offset);
/**
* Returns the current ruler offset.
**/
int offset() const;
int endOffset() const;
public Q_SLOTS:
/**
* Sets the pointer to a new position.
*
* The offset is NOT updated.
* QWidget::repaint() is called afterwards.
**/
void slotNewValue(int);
/**
* Sets the ruler marks to a new position.
*
* The pointer is NOT updated.
* QWidget::repaint() is called afterwards.
**/
void slotNewOffset(int);
void slotEndOffset(int);
protected:
virtual void paintEvent(QPaintEvent *);
private:
void initWidget(Qt::Orientation orientation);
private:
class KRulerPrivate;
KRulerPrivate * const d;
};
#endif
diff --git a/tier1/kwidgetsaddons/src/kseparator.cpp b/tier1/kwidgetsaddons/src/kseparator.cpp
index 5f53ea6098..26a35d97e5 100644
--- a/tier1/kwidgetsaddons/src/kseparator.cpp
+++ b/tier1/kwidgetsaddons/src/kseparator.cpp
@@ -1,61 +1,61 @@
/*
* Copyright (C) 1997 Michael Roth <mroth@wirlweb.de>
*
* This program 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 program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "kseparator.h"
#include <QStyle>
#include <QStyleOption>
#include <QPainter>
-KSeparator::KSeparator(QWidget* parent, Qt::WFlags f) : QFrame(parent, f)
+KSeparator::KSeparator(QWidget* parent, Qt::WindowFlags f) : QFrame(parent, f)
{
setLineWidth(1);
setMidLineWidth(0);
setOrientation( Qt::Horizontal );
}
-KSeparator::KSeparator(Qt::Orientation orientation, QWidget* parent, Qt::WFlags f)
+KSeparator::KSeparator(Qt::Orientation orientation, QWidget* parent, Qt::WindowFlags f)
: QFrame(parent, f)
{
setLineWidth(1);
setMidLineWidth(0);
setOrientation( orientation );
}
void KSeparator::setOrientation(Qt::Orientation orientation)
{
if (orientation == Qt::Vertical) {
setFrameShape ( QFrame::VLine );
setFrameShadow( QFrame::Sunken );
setMinimumSize(2, 0);
}
else {
setFrameShape ( QFrame::HLine );
setFrameShadow( QFrame::Sunken );
setMinimumSize(0, 2);
}
updateGeometry();
}
Qt::Orientation KSeparator::orientation() const
{
return ((frameStyle() & VLine) == VLine) ? Qt::Vertical : Qt::Horizontal;
}
diff --git a/tier1/kwidgetsaddons/src/kseparator.h b/tier1/kwidgetsaddons/src/kseparator.h
index dadf7b4083..6c10de064a 100644
--- a/tier1/kwidgetsaddons/src/kseparator.h
+++ b/tier1/kwidgetsaddons/src/kseparator.h
@@ -1,74 +1,74 @@
/*
* Copyright (C) 1997 Michael Roth <mroth@wirlweb.de>
*
* This program 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 program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef KSEPARATOR_H
#define KSEPARATOR_H
#include <kwidgetsaddons_export.h>
#include <QFrame>
/**
* Standard horizontal or vertical separator.
*
* \image html kseparator-horizontal.png "KDE Separator with horizontal orientation"
* \image html kseparator-vertical.png "KDE Separator with vertical orientation"
*
* @author Michael Roth <mroth@wirlweb.de>
*/
class KWIDGETSADDONS_EXPORT KSeparator : public QFrame
{
Q_OBJECT
Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation )
public:
/**
* Constructor.
* @param parent parent object.
* @param f extra QWidget flags.
**/
- explicit KSeparator(QWidget* parent=0, Qt::WFlags f=0);
+ explicit KSeparator(QWidget* parent=0, Qt::WindowFlags f=0);
/**
* Constructor.
* @param orientation Set the orientation of the separator.
* Possible values are Horizontal or Vertical.
* @param parent parent object.
* @param f extra QWidget flags.
**/
- explicit KSeparator(Qt::Orientation orientation, QWidget* parent=0, Qt::WFlags f=0);
+ explicit KSeparator(Qt::Orientation orientation, QWidget* parent=0, Qt::WindowFlags f=0);
/**
* Returns the orientation of the separator.
* @return int Possible values Horizontal or Vertical.
**/
Qt::Orientation orientation() const;
/**
* Set the orientation of the separator to @p orientation
*
* @param orientation Possible values are Vertical and Horizontal.
*/
void setOrientation(Qt::Orientation orientation);
private:
class KSeparatorPrivate* d;
};
#endif // KSEPARATOR_H
diff --git a/tier1/kwindowsystem/src/kwindowinfo_mac.cpp b/tier1/kwindowsystem/src/kwindowinfo_mac.cpp
index 9756254b33..1bf0876435 100644
--- a/tier1/kwindowsystem/src/kwindowinfo_mac.cpp
+++ b/tier1/kwindowsystem/src/kwindowinfo_mac.cpp
@@ -1,355 +1,355 @@
/*
This file is part of the KDE libraries
Copyright (C) 2008 Marijn Kruisselbrink (m.kruisselbrink@student.tue.nl)
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 "kwindowinfo_mac_p.h"
#include "kwindowinfo.h"
#include "kwindowsystem.h"
#include <QDebug>
#include <kxerrorhandler.h>
#include <netwm.h>
#include <QBitmap>
#include <QDesktopWidget>
#include <QDialog>
#include <QtDBus/QtDBus>
KWindowInfo::Private::Private()
: ref(0), win(0), isLocal(false), loadedData(false), m_axWin(0), parent(), m_pid(-1)
{
}
void KWindowInfo::Private::setAxElement(const AXUIElementRef& axWin)
{
m_axWin = axWin;
CFRetain(m_axWin);
}
void KWindowInfo::Private::setProcessSerialNumber(const ProcessSerialNumber& psn)
{
m_psn = psn;
GetProcessPID(&psn, &m_pid);
}
KWindowInfo::Private::~Private()
{
CFRelease(m_axWin);
}
void KWindowInfo::Private::updateData()
{
ProcessInfoRec pinfo;
char processName[512];
#ifdef Q_OS_MAC32
FSSpec appSpec;
#else
FSRef ref;
#endif
pinfo.processInfoLength = sizeof pinfo;
pinfo.processName = (unsigned char*) processName;
#ifdef Q_OS_MAC32
pinfo.processAppSpec = &appSpec;
#else
pinfo.processAppRef = &ref;
#endif
GetProcessInformation(&m_psn, &pinfo);
- name = QString::fromAscii(processName+1, processName[0]);
+ name = QString::fromLatin1(processName+1, processName[0]);
if (m_axWin) {
CFStringRef title;
if (AXUIElementCopyAttributeValue(m_axWin, kAXTitleAttribute, (CFTypeRef*)&title) == noErr) {
CFStringGetCString(title, processName, sizeof processName, kCFStringEncodingUTF8);
name = QString::fromUtf8(processName);
}
}
#ifdef Q_OS_MAC32
iconSpec = appSpec;
FSRef ref;
FSpMakeFSRef(&appSpec, &ref);
#else
iconSpec = ref;
#endif
// check if it is in an application bundle (foo.app/Contents/MacOS/plasma)
HFSUniStr255 name;
FSRef parentRef;
FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, 0, &parentRef);
ref = parentRef;
FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, 0, &parentRef);
if (QString::fromUtf16(name.unicode, name.length) == "MacOS") {
ref = parentRef;
FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, 0, &parentRef);
if (QString::fromUtf16(name.unicode, name.length) == "Contents") {
#ifdef Q_OS_MAC32
FSSpec spec;
ref = parentRef;
FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, &name, &spec, &parentRef);
iconSpec = spec;
#else
iconSpec = parentRef;
#endif
}
}
loadedData = true;
}
KWindowInfo::KWindowInfo( WId win, unsigned long, unsigned long ) : d(new Private)
{
d->ref = 1;
d->win = win;
d->isLocal = true;
if (!win) {
d->win = (WId) d;
d->isLocal = false;
}
}
// this one is only to make QList<> or similar happy
KWindowInfo::KWindowInfo()
: d( NULL )
{
}
KWindowInfo::~KWindowInfo()
{
if( d != NULL ) {
if( --d->ref == 0 ) {
delete d;
}
}
}
KWindowInfo::KWindowInfo( const KWindowInfo& wininfo )
: d( wininfo.d )
{
if( d != NULL )
++d->ref;
}
KWindowInfo& KWindowInfo::operator=( const KWindowInfo& wininfo )
{
if( d != wininfo.d ) {
if( d != NULL )
if( --d->ref == 0 )
delete d;
d = wininfo.d;
if( d != NULL )
++d->ref;
}
return *this;
}
bool KWindowInfo::valid( bool withdrawn_is_valid ) const
{
return d->pid() >= 0;
}
WId KWindowInfo::win() const
{
return d->win;
}
unsigned long KWindowInfo::state() const
{
return 0;
}
bool KWindowInfo::hasState( unsigned long s ) const
{
return false;
}
bool KWindowInfo::isMinimized() const
{
if (d->axElement()) {
CFBooleanRef val;
if (AXUIElementCopyAttributeValue(d->axElement(), kAXMinimizedAttribute, (CFTypeRef*)&val) == noErr) {
return CFBooleanGetValue(val);
} else {
return false;
}
} else {
return false;
}
}
NET::MappingState KWindowInfo::mappingState() const
{
return (NET::MappingState) 0;
}
NETExtendedStrut KWindowInfo::extendedStrut() const
{
NETExtendedStrut ext;
return ext;
}
NET::WindowType KWindowInfo::windowType( int supported_types ) const
{
return (NET::WindowType) 0;
}
QString KWindowInfo::visibleNameWithState() const
{
QString s = visibleName();
if ( isMinimized() ) {
s.prepend(QLatin1Char('('));
s.append(QLatin1Char(')'));
}
return s;
}
QString KWindowInfo::visibleName() const
{
return name();
}
QString KWindowInfo::name() const
{
if (!d->loadedData) {
d->updateData();
}
return d->name;
}
QString KWindowInfo::visibleIconNameWithState() const
{
QString s = visibleIconName();
if ( isMinimized() ) {
s.prepend(QLatin1Char('('));
s.append(QLatin1Char(')'));
}
return s;
}
QString KWindowInfo::visibleIconName() const
{
return visibleName();
}
QString KWindowInfo::iconName() const
{
return name();
}
bool KWindowInfo::isOnCurrentDesktop() const
{
return isOnDesktop( KWindowSystem::currentDesktop());
}
bool KWindowInfo::isOnDesktop( int _desktop ) const
{
return true;
}
bool KWindowInfo::onAllDesktops() const
{
return false;
}
int KWindowInfo::desktop() const
{
return 0;
}
QRect KWindowInfo::geometry() const
{
return QRect();
}
QRect KWindowInfo::frameGeometry() const
{
return QRect();
}
bool KWindowInfo::actionSupported( NET::Action action ) const
{
return true; // no idea if it's supported or not -> pretend it is
}
#if 0
WId KWindowInfo::transientFor() const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2TransientFor ) == 0, 176 )
<< "Pass NET::WM2TransientFor to KWindowInfo";
return d->info->transientFor();
}
WId KWindowInfo::groupLeader() const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2GroupLeader ) == 0, 176 )
<< "Pass NET::WM2GroupLeader to KWindowInfo";
return d->info->groupLeader();
}
QByteArray KWindowInfo::windowClassClass() const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2WindowClass ) == 0, 176 )
<< "Pass NET::WM2WindowClass to KWindowInfo";
return d->info->windowClassClass();
}
QByteArray KWindowInfo::windowClassName() const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2WindowClass ) == 0, 176 )
<< "Pass NET::WM2WindowClass to KWindowInfo";
return d->info->windowClassName();
}
QByteArray KWindowInfo::windowRole() const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2WindowRole ) == 0, 176 )
<< "Pass NET::WM2WindowRole to KWindowInfo";
return d->info->windowRole();
}
QByteArray KWindowInfo::clientMachine() const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2ClientMachine ) == 0, 176 )
<< "Pass NET::WM2ClientMachine to KWindowInfo";
return d->info->clientMachine();
}
bool KWindowInfo::actionSupported( NET::Action action ) const
{
kWarning(( d->info->passedProperties()[ NETWinInfo::PROTOCOLS2 ] & NET::WM2AllowedActions ) == 0, 176 )
<< "Pass NET::WM2AllowedActions to KWindowInfo";
if( KWindowSystem::allowedActionsSupported())
return d->info->allowedActions() & action;
else
return true; // no idea if it's supported or not -> pretend it is
}
// see NETWM spec section 7.6
bool KWindowInfo::isMinimized() const
{
if( mappingState() != NET::Iconic )
return false;
// NETWM 1.2 compliant WM - uses NET::Hidden for minimized windows
if(( state() & NET::Hidden ) != 0
&& ( state() & NET::Shaded ) == 0 ) // shaded may have NET::Hidden too
return true;
// older WMs use WithdrawnState for other virtual desktops
// and IconicState only for minimized
return KWindowSystem::icccmCompliantMappingState() ? false : true;
}
#endif
diff --git a/tier1/solid/src/solid/backends/udev/udevaudiointerface_p.cpp b/tier1/solid/src/solid/backends/udev/udevaudiointerface_p.cpp
index a04d079c3f..3c4518fdac 100644
--- a/tier1/solid/src/solid/backends/udev/udevaudiointerface_p.cpp
+++ b/tier1/solid/src/solid/backends/udev/udevaudiointerface_p.cpp
@@ -1,331 +1,331 @@
/*
Copyright 2010 Alex Fiestas <alex@eyeos.org>
Copyright 2010 UFO Coders <info@ufocoders.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QString>
#include <QFile>
#include <QDebug>
#include "udevdevice.h"
#include "udevaudiointerface.h"
#include "udevaudiointerface_p.h"
using namespace Solid::Backends::UDev;
UdevAudioInterfacePrivate::UdevAudioInterfacePrivate(UDevDevice *device) : m_device(device)
{
m_cardnum = -1;
m_devicenum = -1;
m_soundcardType = Solid::AudioInterface::InternalSoundcard;
m_driver = Solid::AudioInterface::UnknownAudioDriver;
m_type = Solid::AudioInterface::UnknownAudioInterfaceType;
QString path = m_device->deviceName();
int lastSlash = path.length() - path.lastIndexOf(QLatin1String("/")) -1;
- QByteArray lastElement = path.right(lastSlash).toAscii();
+ QByteArray lastElement = path.right(lastSlash).toLatin1();
const char *lastElementAscii = lastElement.constData();
if (isHardware(lastElementAscii)) {
return;
}
if (isAlsaControl(lastElementAscii)) {
return;
}
if (isAlsaPcm(lastElementAscii)) {
return;
}
if (isAlsaHw(lastElementAscii)) {
return;
}
if (isAlsaMidi(lastElementAscii)) {
return;
}
if (isAlsaTimer(lastElementAscii)) {
return;
}
if (isAlsaSequencer(lastElementAscii)) {
return;
}
if (isOSSSequencer(lastElement)) {
return;
}
if (isOSSDevice(lastElement, lastElementAscii)) {
return;
}
}
Solid::AudioInterface::SoundcardType UdevAudioInterfacePrivate::soundcardType()
{
UdevQt::Device device = m_device->udevDevice();
UdevQt::Device parentDevice = device.parent();
if (parentDevice.isValid()) {
QString productName = parentDevice.sysfsProperty("product").toString();
QString deviceName = m_name;
if (productName.contains("headset", Qt::CaseInsensitive) ||
productName.contains("headphone", Qt::CaseInsensitive) ||
deviceName.contains("headset", Qt::CaseInsensitive) ||
deviceName.contains("headphone", Qt::CaseInsensitive))
{
m_soundcardType = Solid::AudioInterface::Headset;
}
else if (productName.contains("modem", Qt::CaseInsensitive) ||
deviceName.contains("modem", Qt::CaseInsensitive))
{
m_soundcardType = Solid::AudioInterface::Modem;
}
else
{
QString busName = parentDevice.subsystem();
QString driverName = parentDevice.driver();
if (busName == "ieee1394")
{
m_soundcardType = Solid::AudioInterface::FirewireSoundcard;
}
else if (busName == "usb" || busName == "usb_device" || driverName.contains("usb", Qt::CaseInsensitive))
{
m_soundcardType = Solid::AudioInterface::UsbSoundcard;
}
else
{
m_soundcardType = Solid::AudioInterface::InternalSoundcard;
}
}
}
return m_soundcardType;
}
bool UdevAudioInterfacePrivate::isHardware(const char* lastElement)
{
//Root devices like /sys/devices/pci0000:00/0000:00:1b.0/sound/card0 only have sound capability
//in hal, so ATM just report it as unknown
if (sscanf(lastElement, "card%d", &m_cardnum) == 1) {
m_driver = Solid::AudioInterface::UnknownAudioDriver;
m_name = m_device->property("ID_MODEL_FROM_DATABASE").toString();
m_type = Solid::AudioInterface::UnknownAudioInterfaceType;
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isAlsaControl(const char* lastElement)
{
if (sscanf (lastElement, "controlC%d", &m_cardnum) == 1) {
m_deviceFile = m_device->property("DEVNAME").toString();
m_name = cardNumberToName();
m_driver = Solid::AudioInterface::Alsa;
m_type = Solid::AudioInterface::AudioControl;
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isAlsaPcm(const char* lastElement)
{
char type;
if (sscanf (lastElement, "pcmC%dD%d%c", &m_cardnum, &m_devicenum, &type) == 3) {
m_driver = Solid::AudioInterface::Alsa;
m_name = cardNumberToName();
QString name = deviceName(type);
if (!name.isEmpty()) {
m_name.append(QLatin1String(" (") + name + ')');
}
if (type == 'p') {
m_type = Solid::AudioInterface::AudioOutput;
} else if(type == 'c') {
m_type = Solid::AudioInterface::AudioInput;
} else {
m_type = Solid::AudioInterface::UnknownAudioInterfaceType;
}
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isAlsaHw(const char* lastElement)
{
if (sscanf(lastElement, "hwC%dD%d", &m_cardnum, &m_devicenum) == 2) {
m_driver = Solid::AudioInterface::Alsa;
m_name = cardNumberToName();
m_name.append(QLatin1String("(HDA Intel ALSA hardware specific Device)"));
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isAlsaMidi(const char* lastElement)
{
if (sscanf(lastElement, "midiC%dD%d", &m_cardnum, &m_devicenum) == 2) {
m_driver = Solid::AudioInterface::Alsa;
m_name = cardNumberToName();
m_name.append(QLatin1String("(ALSA MIDI Device)"));
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isAlsaTimer(const char* lastElement)
{
if(lastElement == QLatin1String("timer")) {
/* ALSA Global timer device */
m_driver = Solid::AudioInterface::Alsa;
m_name = QLatin1String("ALSA Timer Device");
m_deviceFile = m_device->property("DEVNAME").toString();
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isAlsaSequencer(const char* lastElement)
{
if (lastElement == QLatin1String("seq")) {
/* ALSA global sequencer devices */
m_driver = Solid::AudioInterface::Alsa;
m_name = QLatin1String("ALSA Sequencer Device");
m_deviceFile = m_device->property("DEVNAME").toString();
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isOSSSequencer(const QByteArray& lastElement)
{
if (lastElement.startsWith("sequencer")) {
/* OSS global sequencer devices */
m_driver = Solid::AudioInterface::OpenSoundSystem;
m_name = QLatin1String("OSS Sequencer Device");
m_deviceFile = m_device->property("DEVNAME").toString();
return true;
}
return false;
}
bool UdevAudioInterfacePrivate::isOSSDevice(const QByteArray& lastElement, const char* lastElementAscii)
{
m_driver = Solid::AudioInterface::UnknownAudioDriver;
m_type = Solid::AudioInterface::UnknownAudioInterfaceType;
m_cardnum = 0;
m_deviceFile = m_device->property("DEVNAME").toString();
if (lastElement.startsWith("dsp")) {
m_driver = Solid::AudioInterface::OpenSoundSystem;
sscanf (lastElementAscii, "dsp%d", &m_cardnum);
}
if (lastElement.startsWith("adsp")) {
m_driver = Solid::AudioInterface::OpenSoundSystem;
sscanf (lastElementAscii, "adsp%d", &m_cardnum);
}
if (lastElement.startsWith("midi")) {
m_driver = Solid::AudioInterface::OpenSoundSystem;
sscanf (lastElementAscii, "midi%d", &m_cardnum);
}
if (lastElement.startsWith("amidi")) {
m_driver = Solid::AudioInterface::OpenSoundSystem;
sscanf (lastElementAscii, "amidi%d", &m_cardnum);
}
if (lastElement.startsWith("audio")) {
m_driver = Solid::AudioInterface::OpenSoundSystem;
sscanf (lastElementAscii, "audio%d", &m_cardnum);
}
if (lastElement.startsWith("mixer")) {
m_driver = Solid::AudioInterface::OpenSoundSystem;
m_type = Solid::AudioInterface::AudioControl;
sscanf (lastElementAscii, "mixer%d", &m_cardnum);
}
if (m_driver != Solid::AudioInterface::UnknownAudioDriver) {
m_name = cardNumberToName();
QString path;
path.sprintf("/proc/asound/card%d/pcm0p/info", m_cardnum);
QByteArray cardName = grepHelper(path, "name: ");
if (!cardName.isEmpty()) {
m_name.append(QLatin1String(" (") + cardName + ')');
} else {
m_name.append(QLatin1String(" (OSS Device)"));
}
return true;
}
return false;
}
QString UdevAudioInterfacePrivate::cardNumberToName()
{
QString toFind;
toFind.sprintf("%2d [", m_cardnum);
- QByteArray line = grepHelper(QLatin1String("/proc/asound/cards"), toFind.toAscii());
+ QByteArray line = grepHelper(QLatin1String("/proc/asound/cards"), toFind.toLatin1());
int cut = line.length() - line.lastIndexOf(" - ") - 3;
QString name = line.right(cut);
if (!name.isEmpty()) {
return name;
}
return QString();
}
QString UdevAudioInterfacePrivate::deviceName(char type)
{
QString path;
path.sprintf("/proc/asound/card%d/pcm%d%c/info",m_cardnum, m_devicenum, type);
return grepHelper(path, "name: ");
}
QByteArray UdevAudioInterfacePrivate::grepHelper(const QString& path, const QByteArray& grepValue)
{
QFile file(path);
if (file.exists()) {
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray line = file.readLine();
while(!line.isNull()) {
if (line.startsWith(grepValue)) {
line.remove(0, grepValue.length());
return line.trimmed();
}
line = file.readLine();
}
} else {
qDebug() << "grepHelper: Cannot open file: " << path;
}
} else {
qDebug() << "grepHelper: File does not exists: " << path;
}
return QByteArray();
}
diff --git a/tier1/solid/src/solid/backends/udev/udevmanager.cpp b/tier1/solid/src/solid/backends/udev/udevmanager.cpp
index 5e71c70fdf..0faf9b38b4 100644
--- a/tier1/solid/src/solid/backends/udev/udevmanager.cpp
+++ b/tier1/solid/src/solid/backends/udev/udevmanager.cpp
@@ -1,211 +1,211 @@
/*
Copyright 2010 Rafael Fernández López <ereslibre@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "udevmanager.h"
#include "udev.h"
#include "udevdevice.h"
#include "../shared/rootdevice.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QSet>
#include <QtCore/QFile>
#include <QtCore/QDebug>
#define DETAILED_OUTPUT 0
using namespace Solid::Backends::UDev;
using namespace Solid::Backends::Shared;
class UDevManager::Private
{
public:
Private();
~Private();
bool isOfInterest(const UdevQt::Device &device);
UdevQt::Client *m_client;
QSet<Solid::DeviceInterface::Type> m_supportedInterfaces;
};
UDevManager::Private::Private()
{
QStringList subsystems;
subsystems << "processor";
subsystems << "sound";
subsystems << "tty";
subsystems << "dvb";
subsystems << "video4linux";
subsystems << "net";
subsystems << "usb";
m_client = new UdevQt::Client(subsystems);
}
UDevManager::Private::~Private()
{
delete m_client;
}
bool UDevManager::Private::isOfInterest(const UdevQt::Device &device)
{
#if DETAILED_OUTPUT
qDebug() << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
qDebug() << "Path:" << device.sysfsPath();
qDebug() << "Properties:" << device.deviceProperties();
Q_FOREACH (const QString &key, device.deviceProperties()) {
qDebug() << "\t" << key << ":" << device.deviceProperty(key).toString();
}
qDebug() << "Driver:" << device.driver();
qDebug() << "Subsystem:" << device.subsystem();
qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
#endif
if (device.driver() == QLatin1String("processor")) {
// Linux ACPI reports processor slots, rather than processors.
// Empty slots will not have a system device associated with them.
return QFile::exists(device.sysfsPath() + "/sysdev");
}
if (device.subsystem() == QLatin1String("sound") &&
device.deviceProperty("SOUND_FORM_FACTOR").toString() != "internal") {
return true;
}
if (device.subsystem() == QLatin1String("tty")) {
QString path = device.deviceProperty("DEVPATH").toString();
int lastSlash = path.length() - path.lastIndexOf(QLatin1String("/")) -1;
- QByteArray lastElement = path.right(lastSlash).toAscii();
+ QByteArray lastElement = path.right(lastSlash).toLatin1();
if (lastElement.startsWith("tty") && !path.startsWith("/devices/virtual")) {
return true;
}
}
return device.subsystem() == QLatin1String("dvb") ||
device.subsystem() == QLatin1String("video4linux") ||
device.subsystem() == QLatin1String("net") ||
device.deviceProperty("ID_MEDIA_PLAYER").toString().isEmpty() == false || // media-player-info recognized devices
device.deviceProperty("ID_GPHOTO2").toInt() == 1; // GPhoto2 cameras
}
UDevManager::UDevManager(QObject *parent)
: Solid::Ifaces::DeviceManager(parent),
d(new Private)
{
connect(d->m_client, SIGNAL(deviceAdded(UdevQt::Device)), this, SLOT(slotDeviceAdded(UdevQt::Device)));
connect(d->m_client, SIGNAL(deviceRemoved(UdevQt::Device)), this, SLOT(slotDeviceRemoved(UdevQt::Device)));
d->m_supportedInterfaces << Solid::DeviceInterface::GenericInterface
<< Solid::DeviceInterface::Processor
<< Solid::DeviceInterface::AudioInterface
<< Solid::DeviceInterface::NetworkInterface
<< Solid::DeviceInterface::SerialInterface
<< Solid::DeviceInterface::Camera
<< Solid::DeviceInterface::PortableMediaPlayer
<< Solid::DeviceInterface::DvbInterface
<< Solid::DeviceInterface::Block
<< Solid::DeviceInterface::Video;
}
UDevManager::~UDevManager()
{
delete d;
}
QString UDevManager::udiPrefix() const
{
return QString::fromLatin1(UDEV_UDI_PREFIX);
}
QSet<Solid::DeviceInterface::Type> UDevManager::supportedInterfaces() const
{
return d->m_supportedInterfaces;
}
QStringList UDevManager::allDevices()
{
QStringList res;
const UdevQt::DeviceList deviceList = d->m_client->allDevices();
Q_FOREACH (const UdevQt::Device &device, deviceList) {
if (d->isOfInterest(device)) {
res << udiPrefix() + device.sysfsPath();
}
}
return res;
}
QStringList UDevManager::devicesFromQuery(const QString &parentUdi,
Solid::DeviceInterface::Type type)
{
QStringList allDev = allDevices();
QStringList result;
if (!parentUdi.isEmpty()) {
Q_FOREACH (const QString &udi, allDev) {
UDevDevice device(d->m_client->deviceBySysfsPath(udi.right(udi.size() - udiPrefix().size())));
if (device.queryDeviceInterface(type) && device.parentUdi() == parentUdi) {
result << udi;
}
}
return result;
} else if (type != Solid::DeviceInterface::Unknown) {
Q_FOREACH (const QString &udi, allDev) {
UDevDevice device(d->m_client->deviceBySysfsPath(udi.right(udi.size() - udiPrefix().size())));
if (device.queryDeviceInterface(type)) {
result << udi;
}
}
return result;
} else {
return allDev;
}
}
QObject *UDevManager::createDevice(const QString &udi_)
{
if (udi_ == udiPrefix()) {
RootDevice *const device = new RootDevice(UDEV_UDI_PREFIX);
device->setProduct(QCoreApplication::translate("", "Devices"));
device->setDescription(QCoreApplication::translate("", "Devices declared in your system"));
device->setIcon("computer");
return device;
}
const QString udi = udi_.right(udi_.size() - udiPrefix().size());
UdevQt::Device device = d->m_client->deviceBySysfsPath(udi);
if (d->isOfInterest(device) || QFile::exists(udi)) {
return new UDevDevice(device);
}
return 0;
}
void UDevManager::slotDeviceAdded(const UdevQt::Device &device)
{
if (d->isOfInterest(device)) {
Q_EMIT deviceAdded(udiPrefix() + device.sysfsPath());
}
}
void UDevManager::slotDeviceRemoved(const UdevQt::Device &device)
{
if (d->isOfInterest(device)) {
Q_EMIT deviceRemoved(udiPrefix() + device.sysfsPath());
}
}
diff --git a/tier1/solid/src/solid/backends/udev/udevnetworkinterface.cpp b/tier1/solid/src/solid/backends/udev/udevnetworkinterface.cpp
index 5cb75dfd1e..9a97a76f87 100644
--- a/tier1/solid/src/solid/backends/udev/udevnetworkinterface.cpp
+++ b/tier1/solid/src/solid/backends/udev/udevnetworkinterface.cpp
@@ -1,117 +1,117 @@
/*
Copyright 2010 Alex Fiestas <alex@eyeos.org>
Copyright 2010 UFO Coders <info@ufocoders.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "udevnetworkinterface.h"
#include "udevdevice.h"
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <QFile>
#include <QFileInfo>
#include <QtCore/QStringList>
#include <QDebug>
using namespace Solid::Backends::UDev;
NetworkInterface::NetworkInterface(UDevDevice *device)
: DeviceInterface(device)
{
}
NetworkInterface::~NetworkInterface()
{
}
QString NetworkInterface::ifaceName() const
{
return m_device->property("INTERFACE").toString();
}
bool NetworkInterface::isWireless() const
{
QFile typeFile(m_device->deviceName() + "/type");
if (!typeFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "UdevNetworkInterface: typeFile can't be opened";
return false;
}
int mediaType = typeFile.readAll().trimmed().toInt();
if (mediaType == ARPHRD_ETHER) {
struct iwreq iwr;
int ioctl_fd = socket (PF_INET, SOCK_DGRAM, 0);
- strncpy (iwr.ifr_ifrn.ifrn_name, ifaceName().toAscii().constData(), IFNAMSIZ);
+ strncpy (iwr.ifr_ifrn.ifrn_name, ifaceName().toLatin1().constData(), IFNAMSIZ);
QFileInfo phyDir(m_device->deviceName() + "/phy80211");
if ((ioctl (ioctl_fd, SIOCGIWNAME, &iwr) == 0) || phyDir.isDir()) {
return true;
}
}
return false;
}
QString NetworkInterface::hwAddress() const
{
bool addr_len = false;
QFile lenFile(m_device->deviceName() + "/addr_len");
if (lenFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
lenFile.readAll().trimmed().toInt(&addr_len);
if (addr_len) {
QFile addrFile(m_device->deviceName() + "/address");
if (addrFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray addr = addrFile.readAll().trimmed();
if (!addr.isEmpty()) {
- return QString::fromAscii(addr);
+ return QString::fromLatin1(addr);
}
}
}
}
return QString(QLatin1String("00:00:00:00:00:00"));
}
qulonglong NetworkInterface::macAddress() const
{
QString hwAddr = hwAddress();
qulonglong mac_address = 0;
if (hwAddr != QLatin1String("00:00:00:00:00:00")) {
unsigned int a5, a4, a3, a2, a1, a0;
- if (sscanf (hwAddr.toAscii().constData(), "%x:%x:%x:%x:%x:%x",
+ if (sscanf (hwAddr.toLatin1().constData(), "%x:%x:%x:%x:%x:%x",
&a5, &a4, &a3, &a2, &a1, &a0) == 6) {
mac_address =
((qulonglong)a5<<40) |
((qulonglong)a4<<32) |
((qulonglong)a3<<24) |
((qulonglong)a2<<16) |
((qulonglong)a1<< 8) |
((qulonglong)a0<< 0);
}
}
return mac_address;
}
diff --git a/tier1/solid/src/solid/backends/udev/udevserialinterface.cpp b/tier1/solid/src/solid/backends/udev/udevserialinterface.cpp
index 97889caa56..ebd8e0420f 100644
--- a/tier1/solid/src/solid/backends/udev/udevserialinterface.cpp
+++ b/tier1/solid/src/solid/backends/udev/udevserialinterface.cpp
@@ -1,70 +1,70 @@
/*
Copyright 2009 Harald Fernengel <harry@kdevelop.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which sudevl
act as a proxy defined in Section 6 of version 3 of the license.
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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "udevserialinterface.h"
#include "udevdevice.h"
#include <stdio.h>
#include <QString>
using namespace Solid::Backends::UDev;
SerialInterface::SerialInterface(UDevDevice *device)
: DeviceInterface(device)
{
m_portnum = -1;
m_type = Solid::SerialInterface::Unknown;
QString path = m_device->deviceName();
int lastSlash = path.length() - path.lastIndexOf(QLatin1String("/")) -1;
- QByteArray lastElement = path.right(lastSlash).toAscii();
+ QByteArray lastElement = path.right(lastSlash).toLatin1();
const char *lastElementAscii = lastElement.constData();
if (sscanf (lastElementAscii, "ttyS%d", &m_portnum) == 1) {
m_type = Solid::SerialInterface::Platform;
} else if (sscanf (lastElementAscii, "ttyUSB%d", &m_portnum) == 1) {
m_type = Solid::SerialInterface::Usb;
}
}
SerialInterface::~SerialInterface()
{
}
QVariant SerialInterface::driverHandle() const
{
return m_device->property("DEVNAME");
}
Solid::SerialInterface::SerialType SerialInterface::serialType() const
{
return m_type;
}
int SerialInterface::port() const
{
return m_portnum;
}
diff --git a/tier1/solid/src/solid/predicateparse.cpp b/tier1/solid/src/solid/predicateparse.cpp
index d51c2ff5ac..31f8c4fcd4 100644
--- a/tier1/solid/src/solid/predicateparse.cpp
+++ b/tier1/solid/src/solid/predicateparse.cpp
@@ -1,243 +1,243 @@
/*
Copyright 2006 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
extern "C"
{
#include "predicateparse.h"
void PredicateParse_mainParse(const char *_code);
}
#include "predicate.h"
#include "soliddefs_p.h"
#include <stdlib.h>
#include <QtCore/QStringList>
#include <QtCore/QThreadStorage>
namespace Solid
{
namespace PredicateParse
{
struct ParsingData
{
ParsingData()
: result(0)
{}
Solid::Predicate *result;
QByteArray buffer;
};
}
}
SOLID_GLOBAL_STATIC(QThreadStorage<Solid::PredicateParse::ParsingData *>, s_parsingData)
Solid::Predicate Solid::Predicate::fromString(const QString &predicate)
{
Solid::PredicateParse::ParsingData *data = new Solid::PredicateParse::ParsingData();
s_parsingData->setLocalData(data);
- data->buffer = predicate.toAscii();
+ data->buffer = predicate.toLatin1();
PredicateParse_mainParse(data->buffer.constData());
Predicate result;
if (data->result)
{
result = Predicate(*data->result);
delete data->result;
}
s_parsingData->setLocalData(0);
return result;
}
void PredicateParse_setResult(void *result)
{
Solid::PredicateParse::ParsingData *data = s_parsingData->localData();
data->result = (Solid::Predicate *) result;
}
void PredicateParse_errorDetected(const char* s)
{
qWarning("ERROR from solid predicate parser: %s", s);
s_parsingData->localData()->result = 0;
}
void PredicateParse_destroy(void *pred)
{
Solid::PredicateParse::ParsingData *data = s_parsingData->localData();
Solid::Predicate *p = (Solid::Predicate *) pred;
if (p != data->result) {
delete p;
}
}
void *PredicateParse_newAtom(char *interface, char *property, void *value)
{
QString iface(interface);
QString prop(property);
QVariant *val = (QVariant *)value;
Solid::Predicate *result = new Solid::Predicate(iface, prop, *val);
delete val;
free(interface);
free(property);
return result;
}
void *PredicateParse_newMaskAtom(char *interface, char *property, void *value)
{
QString iface(interface);
QString prop(property);
QVariant *val = (QVariant *)value;
Solid::Predicate *result = new Solid::Predicate(iface, prop, *val, Solid::Predicate::Mask);
delete val;
free(interface);
free(property);
return result;
}
void *PredicateParse_newIsAtom(char *interface)
{
QString iface(interface);
Solid::Predicate *result = new Solid::Predicate(iface);
free(interface);
return result;
}
void *PredicateParse_newAnd(void *pred1, void *pred2)
{
Solid::Predicate *result = new Solid::Predicate();
Solid::PredicateParse::ParsingData *data = s_parsingData->localData();
Solid::Predicate *p1 = (Solid::Predicate *)pred1;
Solid::Predicate *p2 = (Solid::Predicate *)pred2;
if (p1==data->result || p2==data->result) {
data->result = 0;
}
*result = *p1 & *p2;
delete p1;
delete p2;
return result;
}
void *PredicateParse_newOr(void *pred1, void *pred2)
{
Solid::Predicate *result = new Solid::Predicate();
Solid::PredicateParse::ParsingData *data = s_parsingData->localData();
Solid::Predicate *p1 = (Solid::Predicate *)pred1;
Solid::Predicate *p2 = (Solid::Predicate *)pred2;
if (p1==data->result || p2==data->result) {
data->result = 0;
}
*result = *p1 | *p2;
delete p1;
delete p2;
return result;
}
void *PredicateParse_newStringValue(char *val)
{
QString s(val);
free(val);
return new QVariant(s);
}
void *PredicateParse_newBoolValue(int val)
{
bool b = (val != 0);
return new QVariant(b);
}
void *PredicateParse_newNumValue(int val)
{
return new QVariant(val);
}
void *PredicateParse_newDoubleValue(double val)
{
return new QVariant(val);
}
void *PredicateParse_newEmptyStringListValue()
{
return new QVariant(QStringList());
}
void *PredicateParse_newStringListValue(char *name)
{
QStringList list;
list << QString(name);
free(name);
return new QVariant(list);
}
void *PredicateParse_appendStringListValue(char *name, void *list)
{
QVariant *variant = (QVariant *)list;
QStringList new_list = variant->toStringList();
new_list << QString(name);
delete variant;
free(name);
return new QVariant(new_list);
}
void PredicateLexer_unknownToken(const char* text)
{
qWarning("ERROR from solid predicate parser: unrecognized token '%s' in predicate '%s'\n",
text, s_parsingData->localData()->buffer.constData());
}
diff --git a/tier1/threadweaver/autotests/AppendCharacterJob.h b/tier1/threadweaver/autotests/AppendCharacterJob.h
index 2e0d42dfa3..ad493e9940 100644
--- a/tier1/threadweaver/autotests/AppendCharacterJob.h
+++ b/tier1/threadweaver/autotests/AppendCharacterJob.h
@@ -1,82 +1,82 @@
#ifndef APPENDCHARACTER_JOB
#define APPENDCHARACTER_JOB
#include <QtCore/QObject>
#include <QtCore/QMutex>
#include <Job.h>
#include <DebuggingAids.h>
// define in test binary:
extern QMutex s_GlobalMutex;
class AppendCharacterJob : public ThreadWeaver::Job
{
Q_OBJECT
public:
AppendCharacterJob ( QChar c = QChar(), QString* stringref = 0 , QObject* parent = 0 )
: ThreadWeaver::Job ( parent )
{
setValues( c, stringref );
}
void setValues ( QChar c, QString* stringref )
{
m_c = c;
m_stringref = stringref;
setObjectName ( tr ( "Job_" ) + m_c );
}
void run()
{
QMutexLocker locker ( &s_GlobalMutex );
m_stringref->append( m_c );
using namespace ThreadWeaver;
debug( 3, "AppendCharacterJob::run: %c appended, result is %s.\n",
- m_c.toAscii(), qPrintable( *m_stringref ) );
+ m_c.toLatin1(), qPrintable( *m_stringref ) );
}
private:
QChar m_c;
QString* m_stringref;
};
class FailingAppendCharacterJob : public AppendCharacterJob
{
Q_OBJECT
public:
FailingAppendCharacterJob ( QChar c = QChar(), QString* stringref = 0, QObject* parent = 0 )
: AppendCharacterJob ( c, stringref, parent )
{
}
bool success () const
{
return false;
}
};
class BusyJob : public ThreadWeaver::Job
{
Q_OBJECT
public:
BusyJob( QObject* parent = 0 )
: ThreadWeaver::Job ( parent )
{
}
void run()
{
for (int i = 0; i < 100; ++i) {
int k = (i << 3) + (i >> 4);
Q_UNUSED( k );
}
}
};
#endif
diff --git a/tier1/threadweaver/examples/Jobs/WeaverObserverTest.cpp b/tier1/threadweaver/examples/Jobs/WeaverObserverTest.cpp
index 78cdd2c264..9523e22587 100644
--- a/tier1/threadweaver/examples/Jobs/WeaverObserverTest.cpp
+++ b/tier1/threadweaver/examples/Jobs/WeaverObserverTest.cpp
@@ -1,72 +1,72 @@
/* -*- C++ -*-
This file implements the WeaverObserverTest class.
$ Author: Mirko Boehm $
$ Copyright: (C) 2005, Mirko Boehm $
$ Contact: mirko@kde.org
http://www.kde.org
http://www.hackerbuero.org $
$ License: LGPL with the following explicit clarification:
This code may be linked against any version of the Qt toolkit
from Trolltech, Norway. $
$Id: WeaverObserverTest.cpp 31 2005-08-16 16:21:10Z mirko $
*/
#include "WeaverObserverTest.h"
#include "DebuggingAids.h"
#include "State.h"
#include "Thread.h"
namespace ThreadWeaver {
WeaverObserverTest::WeaverObserverTest ( QObject *parent )
: WeaverObserver ( parent )
{
connect ( this, SIGNAL (weaverStateChanged(ThreadWeaver::State*)),
SLOT (slotWeaverStateChanged(ThreadWeaver::State*)) );
connect ( this, SIGNAL (threadStarted(ThreadWeaver::Thread*)),
SLOT (slotThreadStarted(ThreadWeaver::Thread*)) );
connect ( this, SIGNAL (threadBusy(ThreadWeaver::Thread*,ThreadWeaver::Job*)),
SLOT (slotThreadBusy(ThreadWeaver::Thread*,ThreadWeaver::Job*)) );
connect ( this, SIGNAL (threadSuspended(ThreadWeaver::Thread*)),
SLOT (slotThreadSuspended(ThreadWeaver::Thread*)) );
connect ( this, SIGNAL (threadExited(ThreadWeaver::Thread*)),
SLOT (slotThreadExited(ThreadWeaver::Thread*)) );
}
void WeaverObserverTest::slotWeaverStateChanged ( State *state )
{
debug ( 0, "WeaverObserverTest: state changed to \"%s\".\n",
- state->stateName().toAscii().constData() );
+ state->stateName().toLatin1().constData() );
}
void WeaverObserverTest::slotThreadStarted ( Thread* th )
{
debug ( 0, "WeaverObserverTest: thread %i started.\n",
th->id() );
}
void WeaverObserverTest::slotThreadBusy ( Thread *th, Job* )
{
debug ( 0, "WeaverObserverTest: thread %i busy.\n",
th->id() );
}
void WeaverObserverTest::slotThreadSuspended ( Thread *th )
{
debug ( 0, "WeaverObserverTest: thread %i suspended.\n",
th->id() );
}
void WeaverObserverTest::slotThreadExited ( Thread *th )
{
debug ( 0, "WeaverObserverTest: thread %i exited.\n",
th->id() );
}
}
#include "WeaverObserverTest.moc"
diff --git a/tier1/threadweaver/src/Weaver/WeaverImpl.cpp b/tier1/threadweaver/src/Weaver/WeaverImpl.cpp
index 2559a42926..a36e764c9e 100644
--- a/tier1/threadweaver/src/Weaver/WeaverImpl.cpp
+++ b/tier1/threadweaver/src/Weaver/WeaverImpl.cpp
@@ -1,424 +1,424 @@
/* -*- C++ -*-
This file implements the WeaverImpl class.
$ Author: Mirko Boehm $
$ Copyright: (C) 2005, 2006 Mirko Boehm $
$ Contact: mirko@kde.org
http://www.kde.org
http://www.hackerbuero.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.
$Id: WeaverImpl.cpp 30 2005-08-16 16:16:04Z mirko $
*/
#include "WeaverImpl.h"
#include <QtCore/QObject>
#include <QtCore/QMutex>
#include <QtCore/QDebug>
#include "Job.h"
#include "State.h"
#include "Thread.h"
#include "ThreadWeaver.h"
#include "DebuggingAids.h"
#include "WeaverObserver.h"
#include "SuspendedState.h"
#include "SuspendingState.h"
#include "DestructedState.h"
#include "WorkingHardState.h"
#include "ShuttingDownState.h"
#include "InConstructionState.h"
using namespace ThreadWeaver;
WeaverImpl::WeaverImpl( QObject* parent )
: WeaverInterface(parent)
, m_active(0)
, m_inventoryMax( 4 )
, m_mutex ( new QMutex( QMutex::Recursive ) )
, m_finishMutex( new QMutex )
, m_jobAvailableMutex ( new QMutex )
, m_state (0)
{
// initialize state objects:
m_states[InConstruction] = new InConstructionState( this );
setState ( InConstruction );
m_states[WorkingHard] = new WorkingHardState( this );
m_states[Suspending] = new SuspendingState( this );
m_states[Suspended] = new SuspendedState( this );
m_states[ShuttingDown] = new ShuttingDownState( this );
m_states[Destructed] = new DestructedState( this );
// FIXME (0.7) this is supposedly unnecessary
connect ( this, SIGNAL (asyncThreadSuspended(ThreadWeaver::Thread*)),
SIGNAL (threadSuspended(ThreadWeaver::Thread*)),
Qt::QueuedConnection );
setState( WorkingHard );
}
WeaverImpl::~WeaverImpl()
{ // the constructor may only be called from the thread that owns this
// object (everything else would be what we professionals call "insane")
REQUIRE( QThread::currentThread() == thread() );
debug ( 3, "WeaverImpl dtor: destroying inventory.\n" );
setState ( ShuttingDown );
m_jobAvailable.wakeAll();
// problem: Some threads might not be asleep yet, just finding
// out if a job is available. Those threads will suspend
// waiting for their next job (a rare case, but not impossible).
// Therefore, if we encounter a thread that has not exited, we
// have to wake it again (which we do in the following for
// loop).
while (!m_inventory.isEmpty())
{
Thread* th=m_inventory.takeFirst();
if ( !th->isFinished() )
{
for ( ;; )
{
m_jobAvailable.wakeAll();
if ( th->wait( 100 ) ) break;
debug ( 1, "WeaverImpl::~WeaverImpl: thread %i did not exit as expected, "
"retrying.\n", th->id() );
}
}
Q_EMIT ( threadExited ( th ) );
delete th;
}
m_inventory.clear();
delete m_mutex;
delete m_finishMutex;
delete m_jobAvailableMutex;
debug ( 3, "WeaverImpl dtor: done\n" );
setState ( Destructed ); // m_state = Halted;
// FIXME: delete state objects. what sense does DestructedState make then?
// FIXME: make state objects static, since they are
}
void WeaverImpl::setState ( StateId id )
{
if ( m_state==0 || m_state->stateId() != id )
{
m_state = m_states[id];
debug ( 2, "WeaverImpl::setState: state changed to \"%s\".\n",
- m_state->stateName().toAscii().constData() );
+ m_state->stateName().toLatin1().constData() );
if ( id == Suspended )
{
Q_EMIT ( suspended() );
}
m_state->activated();
Q_EMIT ( stateChanged ( m_state ) );
}
}
const State& WeaverImpl::state() const
{
return *m_state;
}
void WeaverImpl::setMaximumNumberOfThreads( int cap )
{
Q_ASSERT_X ( cap > 0, "Weaver Impl", "Thread inventory size has to be larger than zero." );
QMutexLocker l (m_mutex);
m_inventoryMax = cap;
}
int WeaverImpl::maximumNumberOfThreads() const
{
QMutexLocker l (m_mutex);
return m_inventoryMax;
}
int WeaverImpl::currentNumberOfThreads () const
{
QMutexLocker l (m_mutex);
return m_inventory.count ();
}
void WeaverImpl::registerObserver ( WeaverObserver *ext )
{
connect ( this, SIGNAL (stateChanged(ThreadWeaver::State*)),
ext, SIGNAL (weaverStateChanged(ThreadWeaver::State*)) );
connect ( this, SIGNAL (threadStarted(ThreadWeaver::Thread*)),
ext, SIGNAL (threadStarted(ThreadWeaver::Thread*)) );
connect ( this, SIGNAL (threadBusy(ThreadWeaver::Thread*,ThreadWeaver::Job*)),
ext, SIGNAL (threadBusy(ThreadWeaver::Thread*,ThreadWeaver::Job*)) );
connect ( this, SIGNAL (threadSuspended(ThreadWeaver::Thread*)),
ext, SIGNAL (threadSuspended(ThreadWeaver::Thread*)) );
connect ( this, SIGNAL (threadExited(ThreadWeaver::Thread*)) ,
ext, SIGNAL (threadExited(ThreadWeaver::Thread*)) );
}
void WeaverImpl::enqueue(Job* job)
{
adjustInventory ( 1 );
if (job)
{
debug ( 3, "WeaverImpl::enqueue: queueing job %p of type %s.\n",
(void*)job, job->metaObject()->className() );
QMutexLocker l (m_mutex);
job->aboutToBeQueued ( this );
// find positiEon for insertion:;
// FIXME (after 0.6) optimize: factor out queue management into own class,
// and use binary search for insertion (not done yet because
// refactoring already planned):
int i = m_assignments.size();
if (i > 0)
{
while ( i > 0 && m_assignments.at(i - 1)->priority() < job->priority() ) --i;
m_assignments.insert( i, (job) );
} else {
m_assignments.append (job);
}
assignJobs();
}
}
void WeaverImpl::adjustInventory ( int numberOfNewJobs )
{
QMutexLocker l (m_mutex);
// no of threads that can be created:
const int reserve = m_inventoryMax - m_inventory.count();
if ( reserve > 0 )
{
for ( int i = 0; i < qMin ( reserve, numberOfNewJobs ); ++i )
{
Thread *th = createThread();
th->moveToThread( th ); // be sane from the start
m_inventory.append(th);
connect ( th, SIGNAL (jobStarted(ThreadWeaver::Thread*,ThreadWeaver::Job*)),
SIGNAL (threadBusy(ThreadWeaver::Thread*,ThreadWeaver::Job*)) );
connect ( th, SIGNAL (jobDone(ThreadWeaver::Job*)),
SIGNAL (jobDone(ThreadWeaver::Job*)) );
connect ( th, SIGNAL (started(ThreadWeaver::Thread*)),
SIGNAL (threadStarted(ThreadWeaver::Thread*)) );
th->start ();
debug ( 2, "WeaverImpl::adjustInventory: thread created, "
"%i threads in inventory.\n", currentNumberOfThreads() );
}
}
}
Thread* WeaverImpl::createThread()
{
return new Thread( this );
}
bool WeaverImpl::dequeue ( Job* job )
{
bool result;
{
QMutexLocker l (m_mutex);
int i = m_assignments.indexOf ( job );
if ( i != -1 )
{
job->aboutToBeDequeued( this );
m_assignments.removeAt( i );
result = true;
debug( 3, "WeaverImpl::dequeue: job %p dequeued, %i jobs left.\n",
(void*)job, m_assignments.size() );
} else {
debug( 3, "WeaverImpl::dequeue: job %p not found in queue.\n", (void*)job );
result = false;
}
}
// from the queues point of view, a job is just as finished if
// it gets dequeued:
m_jobFinished.wakeOne();
return result;
}
void WeaverImpl::dequeue ()
{
debug( 3, "WeaverImpl::dequeue: dequeueing all jobs.\n" );
QMutexLocker l (m_mutex);
for ( int index = 0; index < m_assignments.size(); ++index )
{
m_assignments.at( index )->aboutToBeDequeued( this );
}
m_assignments.clear();
ENSURE ( m_assignments.isEmpty() );
}
void WeaverImpl::suspend ()
{
m_state->suspend();
}
void WeaverImpl::resume ( )
{
m_state->resume();
}
void WeaverImpl::assignJobs()
{
m_jobAvailable.wakeAll();
}
bool WeaverImpl::isEmpty() const
{
QMutexLocker l (m_mutex);
return m_assignments.isEmpty();
}
void WeaverImpl::incActiveThreadCount()
{
adjustActiveThreadCount ( 1 );
}
void WeaverImpl::decActiveThreadCount()
{
adjustActiveThreadCount ( -1 );
// the done job could have freed another set of jobs, and we do not know how
// many - therefore we need to wake all threads:
m_jobFinished.wakeAll();
}
void WeaverImpl::adjustActiveThreadCount( int diff )
{
QMutexLocker l (m_mutex);
m_active += diff;
debug ( 4, "WeaverImpl::adjustActiveThreadCount: %i active threads (%i jobs"
" in queue).\n", m_active, queueLength() );
if ( m_assignments.isEmpty() && m_active == 0)
{
P_ASSERT ( diff < 0 ); // cannot reach Zero otherwise
Q_EMIT ( finished() );
}
}
int WeaverImpl::activeThreadCount()
{
QMutexLocker l (m_mutex);
return m_active;
}
Job* WeaverImpl::takeFirstAvailableJob()
{
QMutexLocker l (m_mutex);
Job *next = 0;
for (int index = 0; index < m_assignments.size(); ++index)
{
if ( m_assignments.at(index)->canBeExecuted() )
{
next = m_assignments.at(index);
m_assignments.removeAt (index);
break;
}
}
return next;
}
Job* WeaverImpl::applyForWork(Thread *th, Job* previous)
{
if (previous)
{ // cleanup and send events:
decActiveThreadCount();
}
return m_state->applyForWork ( th, 0 );
}
void WeaverImpl::waitForAvailableJob(Thread* th)
{
m_state->waitForAvailableJob ( th );
}
void WeaverImpl::blockThreadUntilJobsAreBeingAssigned ( Thread *th )
{ // th is the thread that calls this method:
Q_UNUSED ( th );
debug ( 4, "WeaverImpl::blockThread...: thread %i blocked.\n", th->id());
Q_EMIT asyncThreadSuspended ( th );
QMutexLocker l( m_jobAvailableMutex );
m_jobAvailable.wait( m_jobAvailableMutex );
debug ( 4, "WeaverImpl::blockThread...: thread %i resumed.\n", th->id());
}
int WeaverImpl::queueLength() const
{
QMutexLocker l (m_mutex);
return m_assignments.count();
}
bool WeaverImpl::isIdle () const
{
QMutexLocker l (m_mutex);
return isEmpty() && m_active == 0;
}
void WeaverImpl::finish()
{
#ifdef QT_NO_DEBUG
const int MaxWaitMilliSeconds = 200;
#else
const int MaxWaitMilliSeconds = 2000;
#endif
while ( !isIdle() )
{
debug (2, "WeaverImpl::finish: not done, waiting.\n" );
QMutexLocker l( m_finishMutex );
if ( m_jobFinished.wait( m_finishMutex, MaxWaitMilliSeconds ) == false )
{
debug ( 2, "WeaverImpl::finish: wait timed out, %i jobs left, waking threads.\n",
queueLength() );
m_jobAvailable.wakeAll();
}
}
debug (2, "WeaverImpl::finish: done.\n\n\n" );
}
void WeaverImpl::requestAbort()
{
QMutexLocker l (m_mutex);
for ( int i = 0; i<m_inventory.size(); ++i )
{
m_inventory[i]->requestAbort();
}
}
void WeaverImpl::dumpJobs()
{
QMutexLocker l (m_mutex);
debug( 0, "WeaverImpl::dumpJobs: current jobs:\n" );
for ( int index = 0; index < m_assignments.size(); ++index )
{
debug( 0, "--> %4i: %p %s (priority %i)\n", index, (void*)m_assignments.at( index ),
m_assignments.at( index )->metaObject()->className(),
m_assignments.at(index)->priority() );
}
}
#include "WeaverImpl.moc"
diff --git a/tier2/kauth/src/backends/dbus/DBusHelperProxy.cpp b/tier2/kauth/src/backends/dbus/DBusHelperProxy.cpp
index e516e8e733..2ec7a18fbe 100644
--- a/tier2/kauth/src/backends/dbus/DBusHelperProxy.cpp
+++ b/tier2/kauth/src/backends/dbus/DBusHelperProxy.cpp
@@ -1,342 +1,342 @@
/*
* Copyright (C) 2008 Nicola Gigante <nicola.gigante@gmail.com>
* Copyright (C) 2009-2010 Dario Freddi <drf@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .
*/
#include "DBusHelperProxy.h"
#include <QtCore/qplugin.h>
#include <QObject>
#include <QMap>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusConnection>
#include <QDebug>
#include <QTimer>
#include <syslog.h>
#include "BackendsManager.h"
#include "authadaptor.h"
Q_DECLARE_METATYPE(QTimer*)
namespace KAuth
{
static void debugMessageReceived(int t, const QString &message);
DBusHelperProxy::DBusHelperProxy()
: responder(0)
, m_stopRequest(false)
, m_busConnection(QDBusConnection::systemBus())
{
}
DBusHelperProxy::DBusHelperProxy(const QDBusConnection &busConnection)
: responder(0)
, m_stopRequest(false)
, m_busConnection(busConnection)
{
}
void DBusHelperProxy::stopAction(const QString &action, const QString &helperID)
{
QDBusMessage message;
message = QDBusMessage::createMethodCall(helperID, QLatin1String("/"), QLatin1String("org.kde.auth"), QLatin1String("stopAction"));
QList<QVariant> args;
args << action;
message.setArguments(args);
m_busConnection.asyncCall(message);
}
void DBusHelperProxy::executeAction(const QString &action, const QString &helperID, const QVariantMap &arguments)
{
QByteArray blob;
QDataStream stream(&blob, QIODevice::WriteOnly);
stream << arguments;
m_busConnection.interface()->startService(helperID);
if (!m_busConnection.connect(helperID, QLatin1String("/"), QLatin1String("org.kde.auth"), QLatin1String("remoteSignal"), this, SLOT(remoteSignalReceived(int,QString,QByteArray)))) {
ActionReply errorReply = ActionReply::DBusErrorReply();
errorReply.setErrorDescription(tr("DBus Backend error: connection to helper failed. ")
+ m_busConnection.lastError().message());
Q_EMIT actionPerformed(action, errorReply);
}
QDBusMessage message;
message = QDBusMessage::createMethodCall(helperID, QLatin1String("/"), QLatin1String("org.kde.auth"), QLatin1String("performAction"));
QList<QVariant> args;
args << action << BackendsManager::authBackend()->callerID() << blob;
message.setArguments(args);
m_actionsInProgress.push_back(action);
QDBusPendingCall pendingCall = m_busConnection.asyncCall(message);
if (pendingCall.reply().type() == QDBusMessage::ErrorMessage) {
ActionReply r = ActionReply::DBusErrorReply();
r.setErrorDescription(tr("DBus Backend error: could not contact the helper. "
"Connection error: ") + m_busConnection.lastError().message() + tr(". Message error: ") + pendingCall.reply().errorMessage());
qDebug() << pendingCall.reply().errorMessage();
Q_EMIT actionPerformed(action, r);
}
}
Action::AuthStatus DBusHelperProxy::authorizeAction(const QString& action, const QString& helperID)
{
if (!m_actionsInProgress.isEmpty()) {
return Action::ErrorStatus;
}
m_busConnection.interface()->startService(helperID);
QDBusMessage message;
message = QDBusMessage::createMethodCall(helperID, QLatin1String("/"), QLatin1String("org.kde.auth"), QLatin1String("authorizeAction"));
QList<QVariant> args;
args << action << BackendsManager::authBackend()->callerID();
message.setArguments(args);
m_actionsInProgress.push_back(action);
QEventLoop e;
QDBusPendingCall pendingCall = m_busConnection.asyncCall(message);
QDBusPendingCallWatcher watcher(pendingCall, this);
connect(&watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), &e, SLOT(quit()));
e.exec();
m_actionsInProgress.removeOne(action);
QDBusMessage reply = pendingCall.reply();
if (reply.type() == QDBusMessage::ErrorMessage || reply.arguments().size() != 1) {
return Action::ErrorStatus;
}
return static_cast<Action::AuthStatus>(reply.arguments().first().toUInt());
}
bool DBusHelperProxy::initHelper(const QString &name)
{
new AuthAdaptor(this);
if (!m_busConnection.registerService(name)) {
return false;
}
if (!m_busConnection.registerObject(QLatin1String("/"), this)) {
return false;
}
m_name = name;
return true;
}
void DBusHelperProxy::setHelperResponder(QObject *o)
{
responder = o;
}
void DBusHelperProxy::remoteSignalReceived(int t, const QString &action, QByteArray blob)
{
SignalType type = (SignalType)t;
QDataStream stream(&blob, QIODevice::ReadOnly);
if (type == ActionStarted) {
Q_EMIT actionStarted(action);
} else if (type == ActionPerformed) {
ActionReply reply = ActionReply::deserialize(blob);
m_actionsInProgress.removeOne(action);
Q_EMIT actionPerformed(action, reply);
} else if (type == DebugMessage) {
int level;
QString message;
stream >> level >> message;
debugMessageReceived(level, message);
} else if (type == ProgressStepIndicator) {
int step;
stream >> step;
Q_EMIT progressStep(action, step);
} else if (type == ProgressStepData) {
QVariantMap data;
stream >> data;
Q_EMIT progressStep(action, data);
}
}
void DBusHelperProxy::stopAction(const QString &action)
{
Q_UNUSED(action)
//#warning FIXME: The stop request should be action-specific rather than global
m_stopRequest = true;
}
bool DBusHelperProxy::hasToStopAction()
{
QEventLoop loop;
loop.processEvents(QEventLoop::AllEvents);
return m_stopRequest;
}
QByteArray DBusHelperProxy::performAction(const QString &action, const QByteArray &callerID, QByteArray arguments)
{
if (!responder) {
return ActionReply::NoResponderReply().serialized();
}
if (!m_currentAction.isEmpty()) {
return ActionReply::HelperBusyReply().serialized();
}
QVariantMap args;
QDataStream s(&arguments, QIODevice::ReadOnly);
s >> args;
m_currentAction = action;
Q_EMIT remoteSignal(ActionStarted, action, QByteArray());
QEventLoop e;
e.processEvents(QEventLoop::AllEvents);
ActionReply retVal;
QTimer *timer = responder->property("__KAuth_Helper_Shutdown_Timer").value<QTimer*>();
timer->stop();
if (BackendsManager::authBackend()->isCallerAuthorized(action, callerID)) {
QString slotname = action;
if (slotname.startsWith(m_name + QLatin1Char('.'))) {
slotname = slotname.right(slotname.length() - m_name.length() - 1);
}
slotname.replace(QLatin1Char('.'), QLatin1Char('_'));
- bool success = QMetaObject::invokeMethod(responder, slotname.toAscii().data(), Qt::DirectConnection,
+ bool success = QMetaObject::invokeMethod(responder, slotname.toLatin1().data(), Qt::DirectConnection,
Q_RETURN_ARG(ActionReply, retVal), Q_ARG(QVariantMap, args));
if (!success) {
retVal = ActionReply::NoSuchActionReply();
}
} else {
retVal = ActionReply::AuthorizationDeniedReply();
}
timer->start();
Q_EMIT remoteSignal(ActionPerformed, action, retVal.serialized());
e.processEvents(QEventLoop::AllEvents);
m_currentAction.clear();
m_stopRequest = false;
return retVal.serialized();
}
uint DBusHelperProxy::authorizeAction(const QString& action, const QByteArray& callerID)
{
if (!m_currentAction.isEmpty()) {
return static_cast<uint>(Action::ErrorStatus);
}
m_currentAction = action;
uint retVal;
QTimer *timer = responder->property("__KAuth_Helper_Shutdown_Timer").value<QTimer*>();
timer->stop();
if (BackendsManager::authBackend()->isCallerAuthorized(action, callerID)) {
retVal = static_cast<uint>(Action::AuthorizedStatus);
} else {
retVal = static_cast<uint>(Action::DeniedStatus);
}
timer->start();
m_currentAction.clear();
return retVal;
}
void DBusHelperProxy::sendDebugMessage(int level, const char *msg)
{
QByteArray blob;
QDataStream stream(&blob, QIODevice::WriteOnly);
stream << level << QString::fromLocal8Bit(msg);
Q_EMIT remoteSignal(DebugMessage, m_currentAction, blob);
}
void DBusHelperProxy::sendProgressStep(int step)
{
QByteArray blob;
QDataStream stream(&blob, QIODevice::WriteOnly);
stream << step;
Q_EMIT remoteSignal(ProgressStepIndicator, m_currentAction, blob);
}
void DBusHelperProxy::sendProgressStep(const QVariantMap &data)
{
QByteArray blob;
QDataStream stream(&blob, QIODevice::WriteOnly);
stream << data;
Q_EMIT remoteSignal(ProgressStepData, m_currentAction, blob);
}
void debugMessageReceived(int t, const QString &message)
{
QtMsgType type = (QtMsgType)t;
switch (type) {
case QtDebugMsg:
- qDebug("Debug message from helper: %s", message.toAscii().data());
+ qDebug("Debug message from helper: %s", message.toLatin1().data());
break;
case QtWarningMsg:
- qWarning("Warning from helper: %s", message.toAscii().data());
+ qWarning("Warning from helper: %s", message.toLatin1().data());
break;
case QtCriticalMsg:
- qCritical("Critical warning from helper: %s", message.toAscii().data());
+ qCritical("Critical warning from helper: %s", message.toLatin1().data());
break;
case QtFatalMsg:
- qFatal("Fatal error from helper: %s", message.toAscii().data());
+ qFatal("Fatal error from helper: %s", message.toLatin1().data());
break;
}
}
} // namespace Auth
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(kauth_helper_backend, KAuth::DBusHelperProxy)
#endif
diff --git a/tier2/kauth/src/backends/mac/kauth-policy-gen-mac.cpp b/tier2/kauth/src/backends/mac/kauth-policy-gen-mac.cpp
index 9ab8cc6071..b573905ec5 100644
--- a/tier2/kauth/src/backends/mac/kauth-policy-gen-mac.cpp
+++ b/tier2/kauth/src/backends/mac/kauth-policy-gen-mac.cpp
@@ -1,61 +1,61 @@
/*
* Copyright (C) 2008 Nicola Gigante <nicola.gigante@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .
*/
#include <auth/policy-gen/policy-gen.h>
#include <iostream>
#include <Security/Security.h>
using namespace std;
void output(QList<Action> actions, QHash<QString, QString> domain)
{
AuthorizationRef auth;
AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &auth);
OSStatus err;
foreach(const Action &action, actions) {
- err = AuthorizationRightGet(action.name.toAscii(), NULL);
+ err = AuthorizationRightGet(action.name.toLatin1(), NULL);
if (err == errAuthorizationDenied) {
QString rule;
if (action.policy == QLatin1String("yes"))
rule = QString::fromLatin1(kAuthorizationRuleClassAllow);
else if (action.policy == QLatin1String("no"))
rule = QString::fromLatin1(kAuthorizationRuleClassDeny);
else if (action.policy == QLatin1String("auth_self"))
rule = QString::fromLatin1(kAuthorizationRuleAuthenticateAsSessionUser);
else if (action.policy == QLatin1String("auth_admin"))
rule = QString::fromLatin1(kAuthorizationRuleAuthenticateAsAdmin);
- CFStringRef cfRule = CFStringCreateWithCString(NULL, rule.toAscii(), kCFStringEncodingASCII);
- CFStringRef cfPrompt = CFStringCreateWithCString(NULL, action.descriptions.value(QLatin1String("en")).toAscii(), kCFStringEncodingASCII);
+ CFStringRef cfRule = CFStringCreateWithCString(NULL, rule.toLatin1(), kCFStringEncodingASCII);
+ CFStringRef cfPrompt = CFStringCreateWithCString(NULL, action.descriptions.value(QLatin1String("en")).toLatin1(), kCFStringEncodingASCII);
- err = AuthorizationRightSet(auth, action.name.toAscii(), cfRule, cfPrompt, NULL, NULL);
+ err = AuthorizationRightSet(auth, action.name.toLatin1(), cfRule, cfPrompt, NULL, NULL);
if (err != noErr) {
cerr << "You don't have the right to edit the security database (try to run cmake with sudo): " << err << endl;
exit(1);
}
}
}
}
diff --git a/tier2/kauth/src/policy-gen/policy-gen.cpp b/tier2/kauth/src/policy-gen/policy-gen.cpp
index 84ff0fd93f..b00e09f300 100644
--- a/tier2/kauth/src/policy-gen/policy-gen.cpp
+++ b/tier2/kauth/src/policy-gen/policy-gen.cpp
@@ -1,155 +1,155 @@
/*
* Copyright (C) 2008 Nicola Gigante <nicola.gigante@gmail.com>
* Copyright (C) 2009 Dario Freddi <drf@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .
*/
#include "policy-gen.h"
#include <QFile>
#include <QCoreApplication>
#include <QSettings>
#include <QRegExp>
#include <QStringList>
#include <QDebug>
using namespace std;
QList<Action> parse(QSettings &ini);
QHash<QString, QString> parseDomain(QSettings &ini);
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (argc < 2) {
qCritical("Too few arguments");
return 1;
}
QSettings ini(QFile::decodeName(argv[1]), QSettings::IniFormat);
ini.setIniCodec("UTF-8");
if (ini.status()) {
qCritical("Error loading file: %s", argv[1]);
return 1;
}
output(parse(ini), parseDomain(ini));
}
QList<Action> parse(QSettings &ini)
{
QList<Action> actions;
QRegExp actionExp(QLatin1String("[0-9a-z]+(\\.[0-9a-z]+)*"));
QRegExp descriptionExp(QLatin1String("description(?:\\[(\\w+)\\])?"));
QRegExp nameExp(QLatin1String("name(?:\\[(\\w+)\\])?"));
QRegExp policyExp(QLatin1String("yes|no|auth_self|auth_admin"));
descriptionExp.setCaseSensitivity(Qt::CaseInsensitive);
nameExp.setCaseSensitivity(Qt::CaseInsensitive);
Q_FOREACH(const QString &name, ini.childGroups()) {
Action action;
if (name == QLatin1String("Domain")) {
continue;
}
if (!actionExp.exactMatch(name)) {
- qCritical("Wrong action syntax: %s\n", name.toAscii().data());
+ qCritical("Wrong action syntax: %s\n", name.toLatin1().data());
exit(1);
}
action.name = name;
ini.beginGroup(name);
Q_FOREACH(const QString &key, ini.childKeys()) {
if (descriptionExp.exactMatch(key)) {
QString lang = descriptionExp.capturedTexts().at(1);
if (lang.isEmpty())
lang = QString::fromLatin1("en");
action.descriptions.insert(lang, ini.value(key).toString());
} else if (nameExp.exactMatch(key)) {
QString lang = nameExp.capturedTexts().at(1);
if (lang.isEmpty())
lang = QString::fromLatin1("en");
action.messages.insert(lang, ini.value(key).toString());
} else if (key.toLower() == QLatin1String("policy")) {
QString policy = ini.value(key).toString();
if (!policyExp.exactMatch(policy)) {
- qCritical("Wrong policy: %s", policy.toAscii().data());
+ qCritical("Wrong policy: %s", policy.toLatin1().data());
exit(1);
}
action.policy = policy;
} else if (key.toLower() == QLatin1String("policyinactive")) {
QString policyInactive = ini.value(key).toString();
if (!policyExp.exactMatch(policyInactive)) {
- qCritical("Wrong policy: %s", policyInactive.toAscii().data());
+ qCritical("Wrong policy: %s", policyInactive.toLatin1().data());
exit(1);
}
action.policyInactive = policyInactive;
} else if (key.toLower() == QLatin1String("persistence")) {
QString persistence = ini.value(key).toString();
if (persistence != QLatin1String("session") && persistence != QLatin1String("always")) {
- qCritical("Wrong persistence: %s", persistence.toAscii().data());
+ qCritical("Wrong persistence: %s", persistence.toLatin1().data());
exit(1);
}
action.persistence = persistence;
}
}
if (action.policy.isEmpty() || action.messages.isEmpty() || action.descriptions.isEmpty()) {
- qCritical("Missing option in action: %s", name.toAscii().data());
+ qCritical("Missing option in action: %s", name.toLatin1().data());
exit(1);
}
ini.endGroup();
actions.append(action);
}
return actions;
}
QHash<QString, QString> parseDomain(QSettings& ini)
{
QHash<QString, QString> rethash;
if (ini.childGroups().contains(QString::fromLatin1("Domain"))) {
if (ini.contains(QString::fromLatin1("Domain/Name"))) {
rethash[QString::fromLatin1("vendor")] = ini.value(QString::fromLatin1("Domain/Name")).toString();
}
if (ini.contains(QString::fromLatin1("Domain/URL"))) {
rethash[QString::fromLatin1("vendorurl")] = ini.value(QString::fromLatin1("Domain/URL")).toString();
}
if (ini.contains(QString::fromLatin1("Domain/Icon"))) {
rethash[QString::fromLatin1("icon")] = ini.value(QString::fromLatin1("Domain/Icon")).toString();
}
}
return rethash;
}
diff --git a/tier2/kconfig/src/core/kconfig.cpp b/tier2/kconfig/src/core/kconfig.cpp
index 36ce550183..1d97bb9deb 100644
--- a/tier2/kconfig/src/core/kconfig.cpp
+++ b/tier2/kconfig/src/core/kconfig.cpp
@@ -1,907 +1,907 @@
/*
This file is part of the KDE libraries
Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
Copyright (c) 1999 Preston Brown <pbrown@kde.org>
Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@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.
*/
// Qt5 TODO: re-enable. No point in doing it before, it breaks on QString::fromUtf8(QByteArray), which exists in qt5.
#undef QT_NO_CAST_FROM_BYTEARRAY
#include "kconfig.h"
#include "kconfig_p.h"
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#include "kconfigbackend.h"
#include "kconfiggroup.h"
#include <qcoreapplication.h>
#include <qprocess.h>
#include <qstandardpaths.h>
#include <qbytearray.h>
#include <qfile.h>
#include <qlocale.h>
#include <qdir.h>
#include <QtCore/QProcess>
#include <QtCore/QSet>
bool KConfigPrivate::mappingsRegistered=false;
KConfigPrivate::KConfigPrivate(KConfig::OpenFlags flags,
QStandardPaths::StandardLocation resourceType)
: openFlags(flags), resourceType(resourceType), mBackend(0),
bDynamicBackend(true), bDirty(false), bReadDefaults(false),
bFileImmutable(false), bForceGlobal(false), bSuppressGlobal(false),
configState(KConfigBase::NoAccess)
{
sGlobalFileName = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/kdeglobals");
static int use_etc_kderc = -1;
if (use_etc_kderc < 0)
use_etc_kderc = getenv("KDE_SKIP_KDERC") != 0 ? 0 : 1; // for unit tests
if (use_etc_kderc) {
etc_kderc =
#ifdef Q_OS_WIN
QFile::decodeName( qgetenv("WINDIR") + "/kde4rc" );
#else
QLatin1String("/etc/kde4rc");
#endif
if (!QFileInfo(etc_kderc).isReadable()) {
etc_kderc.clear();
}
}
// if (!mappingsRegistered) {
// KEntryMap tmp;
// if (!etc_kderc.isEmpty()) {
// KSharedPtr<KConfigBackend> backend = KConfigBackend::create(etc_kderc, QLatin1String("INI"));
// backend->parseConfig( "en_US", tmp, KConfigBackend::ParseDefaults);
// }
// const QString kde4rc(QDir::home().filePath(".kde4rc"));
// if (KStandardDirs::checkAccess(kde4rc, R_OK)) {
// KSharedPtr<KConfigBackend> backend = KConfigBackend::create(kde4rc, QLatin1String("INI"));
// backend->parseConfig( "en_US", tmp, KConfigBackend::ParseOptions());
// }
// KConfigBackend::registerMappings(tmp);
// mappingsRegistered = true;
// }
#if 0 // KDE4 code
setLocale(KLocale::global()->language());
#else
setLocale(QLocale::system().name());
#endif
}
bool KConfigPrivate::lockLocal()
{
if (mBackend) {
return mBackend->lock();
}
// anonymous object - pretend we locked it
return true;
}
void KConfigPrivate::copyGroup(const QByteArray& source, const QByteArray& destination,
KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) const
{
KEntryMap& otherMap = otherGroup->config()->d_ptr->entryMap;
const int len = source.length();
const bool sameName = (destination == source);
// we keep this bool outside the foreach loop so that if
// the group is empty, we don't end up marking the other config
// as dirty erroneously
bool dirtied = false;
for (KEntryMap::ConstIterator entryMapIt( entryMap.constBegin() ); entryMapIt != entryMap.constEnd(); ++entryMapIt) {
const QByteArray& group = entryMapIt.key().mGroup;
if (!group.startsWith(source)) // nothing to do
continue;
// don't copy groups that start with the same prefix, but are not sub-groups
if (group.length() > len && group[len] != '\x1d')
continue;
KEntryKey newKey = entryMapIt.key();
if (flags & KConfigBase::Localized) {
newKey.bLocal = true;
}
if (!sameName)
newKey.mGroup.replace(0, len, destination);
KEntry entry = entryMap[ entryMapIt.key() ];
dirtied = entry.bDirty = flags & KConfigBase::Persistent;
if (flags & KConfigBase::Global) {
entry.bGlobal = true;
}
otherMap[newKey] = entry;
}
if (dirtied) {
otherGroup->config()->d_ptr->bDirty = true;
}
}
QString KConfigPrivate::expandString(const QString& value)
{
QString aValue = value;
// check for environment variables and make necessary translations
int nDollarPos = aValue.indexOf( QLatin1Char('$') );
while( nDollarPos != -1 && nDollarPos+1 < aValue.length()) {
// there is at least one $
if( aValue[nDollarPos+1] == QLatin1Char('(') ) {
int nEndPos = nDollarPos+1;
// the next character is not $
while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!=QLatin1Char(')')) )
nEndPos++;
nEndPos++;
QString cmd = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 );
QString result;
#if 0 // Removed in KDE Frameworks 5. No such concept anymore. Just set your PATH.
QByteArray oldpath = qgetenv( "PATH" );
QByteArray newpath;
if (KComponentData::hasMainComponent()) {
newpath = QFile::encodeName(KGlobal::dirs()->resourceDirs("exe").join(QChar::fromLatin1(KPATH_SEPARATOR)));
if (!newpath.isEmpty() && !oldpath.isEmpty())
newpath += KPATH_SEPARATOR;
}
newpath += oldpath;
qputenv( "PATH", newpath);
#endif
// FIXME: wince does not have pipes
#ifndef _WIN32_WCE
FILE *fs = popen(QFile::encodeName(cmd).data(), "r");
if (fs) {
QTextStream ts(fs, QIODevice::ReadOnly);
result = ts.readAll().trimmed();
pclose(fs);
}
#endif
#if 0 // Removed in KDE Frameworks 5, see above.
qputenv( "PATH", oldpath);
#endif
aValue.replace( nDollarPos, nEndPos-nDollarPos, result );
nDollarPos += result.length();
} else if( aValue[nDollarPos+1] != QLatin1Char('$') ) {
int nEndPos = nDollarPos+1;
// the next character is not $
QString aVarName;
if ( aValue[nEndPos] == QLatin1Char('{') ) {
while ( (nEndPos <= aValue.length()) && (aValue[nEndPos] != QLatin1Char('}')) )
nEndPos++;
nEndPos++;
aVarName = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 );
} else {
while ( nEndPos <= aValue.length() &&
(aValue[nEndPos].isNumber() ||
aValue[nEndPos].isLetter() ||
aValue[nEndPos] == QLatin1Char('_') ) )
nEndPos++;
aVarName = aValue.mid( nDollarPos+1, nEndPos-nDollarPos-1 );
}
QString env;
if (!aVarName.isEmpty()) {
#ifdef Q_OS_WIN
if (aVarName == QLatin1String("HOME"))
env = QDir::homePath();
else
#endif
{
- QByteArray pEnv = qgetenv(aVarName.toAscii().constData());
+ QByteArray pEnv = qgetenv(aVarName.toLatin1().constData());
if( !pEnv.isEmpty() )
env = QString::fromLocal8Bit(pEnv.constData());
}
aValue.replace(nDollarPos, nEndPos-nDollarPos, env);
nDollarPos += env.length();
} else
aValue.remove( nDollarPos, nEndPos-nDollarPos );
} else {
// remove one of the dollar signs
aValue.remove( nDollarPos, 1 );
nDollarPos++;
}
nDollarPos = aValue.indexOf( QLatin1Char('$'), nDollarPos );
}
return aValue;
}
KConfig::KConfig(const QString& file, OpenFlags mode,
QStandardPaths::StandardLocation resourceType)
: d_ptr(new KConfigPrivate(mode, resourceType))
{
d_ptr->changeFileName(file); // set the local file name
// read initial information off disk
reparseConfiguration();
}
KConfig::KConfig(const QString& file, const QString& backend, QStandardPaths::StandardLocation resourceType)
: d_ptr(new KConfigPrivate(SimpleConfig, resourceType))
{
d_ptr->mBackend = KConfigBackend::create(file, backend);
d_ptr->bDynamicBackend = false;
d_ptr->changeFileName(file); // set the local file name
// read initial information off disk
reparseConfiguration();
}
KConfig::KConfig(KConfigPrivate &d)
: d_ptr(&d)
{
}
KConfig::~KConfig()
{
Q_D(KConfig);
if (d->bDirty && d->mBackend.isUnique())
sync();
delete d;
}
QStringList KConfig::groupList() const
{
Q_D(const KConfig);
QSet<QString> groups;
for (KEntryMap::ConstIterator entryMapIt( d->entryMap.constBegin() ); entryMapIt != d->entryMap.constEnd(); ++entryMapIt) {
const KEntryKey& key = entryMapIt.key();
const QByteArray group = key.mGroup;
if (key.mKey.isNull() && !group.isEmpty() && group != "<default>" && group != "$Version") {
const QString groupname = QString::fromUtf8(group);
groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d')));
}
}
return groups.toList();
}
QStringList KConfigPrivate::groupList(const QByteArray& group) const
{
QByteArray theGroup = group + '\x1d';
QSet<QString> groups;
for (KEntryMap::ConstIterator entryMapIt( entryMap.constBegin() ); entryMapIt != entryMap.constEnd(); ++entryMapIt) {
const KEntryKey& key = entryMapIt.key();
if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) {
const QString groupname = QString::fromUtf8(key.mGroup.mid(theGroup.length()));
groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d')));
}
}
return groups.toList();
}
// List all sub groups, including subsubgroups
QSet<QByteArray> KConfigPrivate::allSubGroups(const QByteArray& parentGroup) const
{
QSet<QByteArray> groups;
QByteArray theGroup = parentGroup + '\x1d';
groups << parentGroup;
for (KEntryMap::const_iterator entryMapIt = entryMap.begin(); entryMapIt != entryMap.end(); ++entryMapIt) {
const KEntryKey& key = entryMapIt.key();
if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) {
groups << key.mGroup;
}
}
return groups;
}
bool KConfigPrivate::hasNonDeletedEntries(const QByteArray& group) const
{
const QSet<QByteArray> allGroups = allSubGroups(group);
Q_FOREACH(const QByteArray& aGroup, allGroups) {
// Could be optimized, let's use the slow way for now
// Check for any non-deleted entry
if (!keyListImpl(aGroup).isEmpty())
return true;
}
return false;
}
QStringList KConfigPrivate::keyListImpl(const QByteArray& theGroup) const
{
QStringList keys;
const KEntryMapConstIterator theEnd = entryMap.constEnd();
KEntryMapConstIterator it = entryMap.findEntry(theGroup);
if (it != theEnd) {
++it; // advance past the special group entry marker
QSet<QString> tmp;
for (; it != theEnd && it.key().mGroup == theGroup; ++it) {
const KEntryKey& key = it.key();
if (key.mGroup == theGroup && !key.mKey.isNull() && !it->bDeleted)
tmp << QString::fromUtf8(key.mKey);
}
keys = tmp.toList();
}
return keys;
}
QStringList KConfig::keyList(const QString& aGroup) const
{
Q_D(const KConfig);
const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
return d->keyListImpl(theGroup);
}
QMap<QString,QString> KConfig::entryMap(const QString& aGroup) const
{
Q_D(const KConfig);
QMap<QString, QString> theMap;
const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
const KEntryMapConstIterator theEnd = d->entryMap.constEnd();
KEntryMapConstIterator it = d->entryMap.findEntry(theGroup, 0, 0);
if (it != theEnd) {
++it; // advance past the special group entry marker
for (; it != theEnd && it.key().mGroup == theGroup; ++it) {
// leave the default values and deleted entries out
if (!it->bDeleted && !it.key().bDefault) {
const QString key = QString::fromUtf8(it.key().mKey.constData());
// the localized entry should come first, so don't overwrite it
// with the non-localized entry
if (!theMap.contains(key)) {
if (it->bExpand) {
theMap.insert(key,KConfigPrivate::expandString(QString::fromUtf8(it->mValue.constData())));
} else {
theMap.insert(key,QString::fromUtf8(it->mValue.constData()));
}
}
}
}
}
return theMap;
}
// TODO KDE5: return a bool value
void KConfig::sync()
{
Q_D(KConfig);
if (isImmutable() || name().isEmpty()) {
// can't write to an immutable or anonymous file.
return;
}
if (d->bDirty && d->mBackend) {
const QByteArray utf8Locale(locale().toUtf8());
// Create the containing dir, maybe it wasn't there
d->mBackend->createEnclosing();
// lock the local file
if (d->configState == ReadWrite && !d->lockLocal()) {
qWarning() << "couldn't lock local file";
return;
}
// Rewrite global/local config only if there is a dirty entry in it.
bool writeGlobals = false;
bool writeLocals = false;
Q_FOREACH (const KEntry& e, d->entryMap) {
if (e.bDirty) {
if (e.bGlobal) {
writeGlobals = true;
} else {
writeLocals = true;
}
if (writeGlobals && writeLocals) {
break;
}
}
}
d->bDirty = false; // will revert to true if a config write fails
if (d->wantGlobals() && writeGlobals) {
KSharedPtr<KConfigBackend> tmp = KConfigBackend::create(d->sGlobalFileName);
if (d->configState == ReadWrite && !tmp->lock()) {
qWarning() << "couldn't lock global file";
return;
}
if (!tmp->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteGlobal)) {
d->bDirty = true;
// TODO KDE5: return false? (to tell the app that writing wasn't possible, e.g.
// config file is immutable or disk full)
}
if (tmp->isLocked()) {
tmp->unlock();
}
}
if (writeLocals) {
if (!d->mBackend->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteOptions())) {
d->bDirty = true;
// TODO KDE5: return false? (to tell the app that writing wasn't possible, e.g.
// config file is immutable or disk full)
}
}
if (d->mBackend->isLocked()) {
d->mBackend->unlock();
}
}
}
void KConfig::markAsClean()
{
Q_D(KConfig);
d->bDirty = false;
// clear any dirty flags that entries might have set
const KEntryMapIterator theEnd = d->entryMap.end();
for (KEntryMapIterator it = d->entryMap.begin(); it != theEnd; ++it)
it->bDirty = false;
}
void KConfig::checkUpdate(const QString &id, const QString &updateFile)
{
const KConfigGroup cg(this, "$Version");
const QString cfg_id = updateFile+QLatin1Char(':')+id;
const QStringList ids = cg.readEntry("update_info", QStringList());
if (!ids.contains(cfg_id)) {
#if 0
KToolInvocation::kdeinitExecWait(QString::fromLatin1("kconf_update"), QStringList() << QString::fromLatin1("--check") << updateFile);
#else
QProcess::execute(QString::fromLatin1("kconf_update"), QStringList() << QString::fromLatin1("--check") << updateFile);
#endif
reparseConfiguration();
}
}
KConfig* KConfig::copyTo(const QString &file, KConfig *config) const
{
Q_D(const KConfig);
if (!config)
config = new KConfig(QString(), SimpleConfig, d->resourceType);
config->d_func()->changeFileName(file);
config->d_func()->entryMap = d->entryMap;
config->d_func()->bFileImmutable = false;
const KEntryMapIterator theEnd = config->d_func()->entryMap.end();
for (KEntryMapIterator it = config->d_func()->entryMap.begin(); it != theEnd; ++it)
it->bDirty = true;
config->d_ptr->bDirty = true;
return config;
}
QString KConfig::name() const
{
Q_D(const KConfig);
return d->fileName;
}
Q_GLOBAL_STATIC(QString, globalMainConfigName)
void KConfig::setMainConfigName(const QString& str)
{
*globalMainConfigName() = str;
}
QString KConfig::mainConfigName()
{
// --config on the command line overrides everything else
const QStringList args = QCoreApplication::arguments();
for (int i = 1; i < args.count() ; ++i) {
if (args.at(i) == QLatin1String("--config") && i < args.count()-1) {
return args.at(i+1);
}
}
const QString globalName = *globalMainConfigName();
if (!globalName.isEmpty())
return globalName;
QString appName = QCoreApplication::applicationName();
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
if (appName.isEmpty())
appName = qAppName();
#endif
return appName + QLatin1String("rc");
}
void KConfigPrivate::changeFileName(const QString& name)
{
fileName = name;
QString file;
if (name.isEmpty()) {
if (wantDefaults()) { // accessing default app-specific config "appnamerc"
fileName = KConfig::mainConfigName();
file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName;
} else if (wantGlobals()) { // accessing "kdeglobals" - XXX used anywhere?
resourceType = QStandardPaths::ConfigLocation;
fileName = QLatin1String("kdeglobals");
file = sGlobalFileName;
} else {
// anonymous config
openFlags = KConfig::SimpleConfig;
return;
}
} else if (QDir::isAbsolutePath(fileName)) {
fileName = QFileInfo(fileName).canonicalFilePath();
if (fileName.isEmpty()) // file doesn't exist (yet)
fileName = name;
file = fileName;
} else {
file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName;
}
Q_ASSERT(!file.isEmpty());
bSuppressGlobal = (file == sGlobalFileName);
if (bDynamicBackend || !mBackend) // allow dynamic changing of backend
mBackend = KConfigBackend::create(file);
else
mBackend->setFilePath(file);
configState = mBackend->accessMode();
}
void KConfig::reparseConfiguration()
{
Q_D(KConfig);
if (d->fileName.isEmpty()) {
return;
}
// Don't lose pending changes
if (!d->isReadOnly() && d->bDirty)
sync();
d->entryMap.clear();
d->bFileImmutable = false;
// Parse all desired files from the least to the most specific.
if (d->wantGlobals())
d->parseGlobalFiles();
d->parseConfigFiles();
}
QStringList KConfigPrivate::getGlobalFiles() const
{
QStringList globalFiles;
Q_FOREACH (const QString& dir1, QStandardPaths::locateAll(QStandardPaths::ConfigLocation, QLatin1String("kdeglobals")))
globalFiles.push_front(dir1);
Q_FOREACH (const QString& dir2, QStandardPaths::locateAll(QStandardPaths::ConfigLocation, QLatin1String("system.kdeglobals")))
globalFiles.push_front(dir2);
if (!etc_kderc.isEmpty())
globalFiles.push_front(etc_kderc);
return globalFiles;
}
void KConfigPrivate::parseGlobalFiles()
{
const QStringList globalFiles = getGlobalFiles();
// qDebug() << "parsing global files" << globalFiles;
// TODO: can we cache the values in etc_kderc / other global files
// on a per-application basis?
const QByteArray utf8Locale = locale.toUtf8();
Q_FOREACH(const QString& file, globalFiles) {
KConfigBackend::ParseOptions parseOpts = KConfigBackend::ParseGlobal|KConfigBackend::ParseExpansions;
if (file != sGlobalFileName)
parseOpts |= KConfigBackend::ParseDefaults;
KSharedPtr<KConfigBackend> backend = KConfigBackend::create(file);
if ( backend->parseConfig( utf8Locale, entryMap, parseOpts) == KConfigBackend::ParseImmutable)
break;
}
}
void KConfigPrivate::parseConfigFiles()
{
// can only read the file if there is a backend and a file name
if (mBackend && !fileName.isEmpty()) {
bFileImmutable = false;
QList<QString> files;
if (wantDefaults()) {
if (bSuppressGlobal) {
files = getGlobalFiles();
} else {
if (QDir::isAbsolutePath(fileName)) {
files << fileName;
} else {
Q_FOREACH (const QString& f, QStandardPaths::locateAll(resourceType, fileName))
files.prepend(f);
}
}
} else {
files << mBackend->filePath();
}
if (!isSimple())
files = extraFiles.toList() + files;
// qDebug() << "parsing local files" << files;
const QByteArray utf8Locale = locale.toUtf8();
Q_FOREACH(const QString& file, files) {
if (file == mBackend->filePath()) {
switch (mBackend->parseConfig(utf8Locale, entryMap, KConfigBackend::ParseExpansions)) {
case KConfigBackend::ParseOk:
break;
case KConfigBackend::ParseImmutable:
bFileImmutable = true;
break;
case KConfigBackend::ParseOpenError:
configState = KConfigBase::NoAccess;
break;
}
} else {
KSharedPtr<KConfigBackend> backend = KConfigBackend::create(file);
bFileImmutable = (backend->parseConfig(utf8Locale, entryMap,
KConfigBackend::ParseDefaults|KConfigBackend::ParseExpansions)
== KConfigBackend::ParseImmutable);
}
if (bFileImmutable)
break;
}
#pragma message("TODO: enable kiosk feature again (resource restrictions), but without KStandardDirs... Needs a class in the kconfig framework.")
#if 0
if (componentData.dirs()->isRestrictedResource(resourceType, fileName))
bFileImmutable = true;
#endif
}
}
KConfig::AccessMode KConfig::accessMode() const
{
Q_D(const KConfig);
return d->configState;
}
void KConfig::addConfigSources(const QStringList& files)
{
Q_D(KConfig);
Q_FOREACH(const QString& file, files) {
d->extraFiles.push(file);
}
if (!files.isEmpty()) {
reparseConfiguration();
}
}
QString KConfig::locale() const
{
Q_D(const KConfig);
return d->locale;
}
bool KConfigPrivate::setLocale(const QString& aLocale)
{
if (aLocale != locale) {
locale = aLocale;
return true;
}
return false;
}
bool KConfig::setLocale(const QString& locale)
{
Q_D(KConfig);
if (d->setLocale(locale)) {
reparseConfiguration();
return true;
}
return false;
}
void KConfig::setReadDefaults(bool b)
{
Q_D(KConfig);
d->bReadDefaults = b;
}
bool KConfig::readDefaults() const
{
Q_D(const KConfig);
return d->bReadDefaults;
}
bool KConfig::isImmutable() const
{
Q_D(const KConfig);
return d->bFileImmutable;
}
bool KConfig::isGroupImmutableImpl(const QByteArray& aGroup) const
{
Q_D(const KConfig);
return isImmutable() || d->entryMap.getEntryOption(aGroup, 0, 0, KEntryMap::EntryImmutable);
}
#ifndef KDE_NO_DEPRECATED
void KConfig::setForceGlobal(bool b)
{
Q_D(KConfig);
d->bForceGlobal = b;
}
#endif
#ifndef KDE_NO_DEPRECATED
bool KConfig::forceGlobal() const
{
Q_D(const KConfig);
return d->bForceGlobal;
}
#endif
KConfigGroup KConfig::groupImpl(const QByteArray &group)
{
return KConfigGroup(this, group.constData());
}
const KConfigGroup KConfig::groupImpl(const QByteArray &group) const
{
return KConfigGroup(this, group.constData());
}
KEntryMap::EntryOptions convertToOptions(KConfig::WriteConfigFlags flags)
{
KEntryMap::EntryOptions options=0;
if (flags&KConfig::Persistent)
options |= KEntryMap::EntryDirty;
if (flags&KConfig::Global)
options |= KEntryMap::EntryGlobal;
if (flags&KConfig::Localized)
options |= KEntryMap::EntryLocalized;
return options;
}
void KConfig::deleteGroupImpl(const QByteArray &aGroup, WriteConfigFlags flags)
{
Q_D(KConfig);
KEntryMap::EntryOptions options = convertToOptions(flags)|KEntryMap::EntryDeleted;
const QSet<QByteArray> groups = d->allSubGroups(aGroup);
Q_FOREACH (const QByteArray& group, groups) {
const QStringList keys = d->keyListImpl(group);
Q_FOREACH (const QString& _key, keys) {
const QByteArray &key = _key.toUtf8();
if (d->canWriteEntry(group, key.constData())) {
d->entryMap.setEntry(group, key, QByteArray(), options);
d->bDirty = true;
}
}
}
}
bool KConfig::isConfigWritable(bool warnUser)
{
Q_D(KConfig);
bool allWritable = (d->mBackend.isNull()? false: d->mBackend->isWritable());
if (warnUser && !allWritable) {
QString errorMsg;
if (!d->mBackend.isNull()) // TODO how can be it be null? Set errorMsg appropriately
errorMsg = d->mBackend->nonWritableErrorMessage();
// Note: We don't ask the user if we should not ask this question again because we can't save the answer.
errorMsg += QCoreApplication::translate("KConfig", "Please contact your system administrator.");
QString cmdToExec = QStandardPaths::findExecutable(QString::fromLatin1("kdialog"));
if (!cmdToExec.isEmpty())
{
QProcess::execute(cmdToExec, QStringList()
<< QString::fromLatin1("--title") << QCoreApplication::applicationName()
<< QString::fromLatin1("--msgbox") << errorMsg);
}
}
d->configState = allWritable ? ReadWrite : ReadOnly; // update the read/write status
return allWritable;
}
bool KConfig::hasGroupImpl(const QByteArray& aGroup) const
{
Q_D(const KConfig);
// No need to look for the actual group entry anymore, or for subgroups:
// a group exists if it contains any non-deleted entry.
return d->hasNonDeletedEntries(aGroup);
}
bool KConfigPrivate::canWriteEntry(const QByteArray& group, const char* key, bool isDefault) const
{
if (bFileImmutable ||
entryMap.getEntryOption(group, key, KEntryMap::SearchLocalized, KEntryMap::EntryImmutable))
return isDefault;
return true;
}
void KConfigPrivate::putData( const QByteArray& group, const char* key,
const QByteArray& value, KConfigBase::WriteConfigFlags flags, bool expand)
{
KEntryMap::EntryOptions options = convertToOptions(flags);
if (bForceGlobal)
options |= KEntryMap::EntryGlobal;
if (expand)
options |= KEntryMap::EntryExpansion;
if (value.isNull()) // deleting entry
options |= KEntryMap::EntryDeleted;
bool dirtied = entryMap.setEntry(group, key, value, options);
if (dirtied && (flags & KConfigBase::Persistent))
bDirty = true;
}
QByteArray KConfigPrivate::lookupData(const QByteArray& group, const char* key,
KEntryMap::SearchFlags flags) const
{
if (bReadDefaults)
flags |= KEntryMap::SearchDefaults;
const KEntryMapConstIterator it = entryMap.findEntry(group, key, flags);
if (it == entryMap.constEnd())
return QByteArray();
return it->mValue;
}
QString KConfigPrivate::lookupData(const QByteArray& group, const char* key,
KEntryMap::SearchFlags flags, bool *expand) const
{
if (bReadDefaults)
flags |= KEntryMap::SearchDefaults;
return entryMap.getEntry(group, key, QString(), flags, expand);
}
QStandardPaths::StandardLocation KConfig::locationType() const
{
Q_D(const KConfig);
return d->resourceType;
}
void KConfig::virtual_hook(int /*id*/, void* /*data*/)
{
/* nothing */
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 1, 10:00 AM (1 d, 19 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a0/9a/5e7840ae8d94faa609d7e76bb017
Default Alt Text
(2 MB)

Event Timeline