summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/io/io.pri2
-rw-r--r--src/corelib/io/qfilesystemiterator_symbian.cpp2
-rw-r--r--src/corelib/kernel/qcore_symbian_p.cpp104
-rw-r--r--src/corelib/kernel/qcore_symbian_p.h101
-rw-r--r--src/corelib/kernel/qeventdispatcher_symbian.cpp169
-rw-r--r--src/corelib/kernel/qeventdispatcher_symbian_p.h18
-rw-r--r--src/corelib/kernel/qsystemerror.cpp4
-rw-r--r--src/corelib/thread/qthread_symbian.cpp10
-rw-r--r--src/corelib/tools/qlocale.cpp14
-rw-r--r--src/corelib/tools/qlocale_icu.cpp224
-rw-r--r--src/corelib/tools/qstring.cpp35
-rw-r--r--src/corelib/tools/tools.pri5
12 files changed, 653 insertions, 35 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri
index 2b61192..f67600d 100644
--- a/src/corelib/io/io.pri
+++ b/src/corelib/io/io.pri
@@ -110,7 +110,7 @@ win32 {
SOURCES += io/qfilesystemwatcher_symbian.cpp
HEADERS += io/qfilesystemwatcher_symbian_p.h
INCLUDEPATH += $$MW_LAYER_SYSTEMINCLUDE
- LIBS += -lplatformenv
+ LIBS += -lplatformenv -lesock
}
}
integrity {
diff --git a/src/corelib/io/qfilesystemiterator_symbian.cpp b/src/corelib/io/qfilesystemiterator_symbian.cpp
index 23c726a..a39f9c3 100644
--- a/src/corelib/io/qfilesystemiterator_symbian.cpp
+++ b/src/corelib/io/qfilesystemiterator_symbian.cpp
@@ -88,8 +88,6 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &path, QDir::Fil
else if (symbianMask == 0) {
if ((filters & QDir::PermissionMask) == QDir::Writable)
symbianMask = KEntryAttMatchExclude | KEntryAttReadOnly;
- else if ((filters & QDir::PermissionMask) == QDir::Readable)
- symbianMask = KEntryAttMatchExclusive | KEntryAttReadOnly;
}
lastError = dirHandle.Open(fs, qt_QString2TPtrC(absPath), symbianMask);
diff --git a/src/corelib/kernel/qcore_symbian_p.cpp b/src/corelib/kernel/qcore_symbian_p.cpp
index 801d63f..c717cb9 100644
--- a/src/corelib/kernel/qcore_symbian_p.cpp
+++ b/src/corelib/kernel/qcore_symbian_p.cpp
@@ -44,6 +44,7 @@
#include <e32uid.h>
#include "qcore_symbian_p.h"
#include <string>
+#include <in_sock.h>
QT_BEGIN_NAMESPACE
@@ -203,6 +204,11 @@ private:
RFs iFs;
};
+uint qHash(const RSubSessionBase& key)
+{
+ return qHash(key.SubSessionHandle());
+}
+
Q_GLOBAL_STATIC(QS60RFsSession, qt_s60_RFsSession);
Q_CORE_EXPORT RFs& qt_s60GetRFs()
@@ -210,4 +216,102 @@ Q_CORE_EXPORT RFs& qt_s60GetRFs()
return qt_s60_RFsSession()->GetRFs();
}
+QSymbianSocketManager::QSymbianSocketManager() :
+ iNextSocket(0), iDefaultConnection(0)
+{
+ TSessionPref preferences;
+ // ### In future this could be changed to KAfInet6 when that is more common than IPv4
+ preferences.iAddrFamily = KAfInet;
+ preferences.iProtocol = KProtocolInetIp;
+ //use global message pool, as we do not know how many sockets app will use
+ //TODO: is this the right choice?
+ qt_symbian_throwIfError(iSocketServ.Connect(preferences, -1));
+ qt_symbian_throwIfError(iSocketServ.ShareAuto());
+}
+
+QSymbianSocketManager::~QSymbianSocketManager()
+{
+ iSocketServ.Close();
+ if(!socketMap.isEmpty()) {
+ qWarning("leaked %d sockets on exit", socketMap.count());
+ }
+}
+
+RSocketServ& QSymbianSocketManager::getSocketServer() {
+ return iSocketServ;
+}
+
+int QSymbianSocketManager::addSocket(const RSocket& socket) {
+ QHashableSocket sock(static_cast<const QHashableSocket &>(socket));
+ QMutexLocker l(&iMutex);
+ Q_ASSERT(!socketMap.contains(sock));
+ if(socketMap.contains(sock))
+ return socketMap.value(sock);
+ // allocate socket number
+ int guard = 0;
+ while(reverseSocketMap.contains(iNextSocket)) {
+ iNextSocket++;
+ iNextSocket %= max_sockets;
+ guard++;
+ if(guard > max_sockets)
+ return -1;
+ }
+ int id = iNextSocket;
+
+ socketMap[sock] = id;
+ reverseSocketMap[id] = sock;
+ return id + socket_offset;
+}
+
+bool QSymbianSocketManager::removeSocket(const RSocket &socket) {
+ QHashableSocket sock(static_cast<const QHashableSocket &>(socket));
+ QMutexLocker l(&iMutex);
+ if(!socketMap.contains(sock))
+ return false;
+ int id = socketMap.value(sock);
+ socketMap.remove(sock);
+ reverseSocketMap.remove(id);
+ return true;
+}
+
+int QSymbianSocketManager::lookupSocket(const RSocket& socket) const {
+ QHashableSocket sock(static_cast<const QHashableSocket &>(socket));
+ QMutexLocker l(&iMutex);
+ if(!socketMap.contains(sock))
+ return -1;
+ int id = socketMap.value(sock);
+ return id + socket_offset;
+}
+
+bool QSymbianSocketManager::lookupSocket(int fd, RSocket& socket) const {
+ QMutexLocker l(&iMutex);
+ int id = fd - socket_offset;
+ if(!reverseSocketMap.contains(id))
+ return false;
+ socket = reverseSocketMap.value(id);
+ return true;
+}
+
+void QSymbianSocketManager::setDefaultConnection(RConnection* con)
+{
+ iDefaultConnection = con;
+}
+
+RConnection* QSymbianSocketManager::defaultConnection() const
+{
+ return iDefaultConnection;
+}
+
+Q_GLOBAL_STATIC(QSymbianSocketManager, qt_symbianSocketManager);
+
+QSymbianSocketManager& QSymbianSocketManager::instance()
+{
+ return *(qt_symbianSocketManager());
+}
+
+Q_CORE_EXPORT RSocketServ& qt_symbianGetSocketServer()
+{
+ return QSymbianSocketManager::instance().getSocketServer();
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcore_symbian_p.h b/src/corelib/kernel/qcore_symbian_p.h
index 5b48689..cbe84a8 100644
--- a/src/corelib/kernel/qcore_symbian_p.h
+++ b/src/corelib/kernel/qcore_symbian_p.h
@@ -55,10 +55,12 @@
#include <e32std.h>
#include <QtCore/qglobal.h>
+#include <QtCore/qmutex.h>
#include <qstring.h>
#include <qrect.h>
#include <qhash.h>
#include <f32file.h>
+#include <es_sock.h>
#define QT_LSTRING2(x) L##x
#define QT_LSTRING(x) QT_LSTRING2(x)
@@ -154,6 +156,7 @@ enum S60PluginFuncOrdinals
Q_CORE_EXPORT TLibraryFunction qt_resolveS60PluginFunc(int ordinal);
Q_CORE_EXPORT RFs& qt_s60GetRFs();
+Q_CORE_EXPORT RSocketServ& qt_symbianGetSocketServer();
// Defined in qlocale_symbian.cpp.
Q_CORE_EXPORT QByteArray qt_symbianLocaleName(int code);
@@ -174,6 +177,104 @@ struct QScopedPointerRCloser
}
};
+//Wrapper for RSocket so it can be used as a key in QHash or QMap
+class QHashableSocket : public RSocket
+{
+public:
+ bool operator==(const QHashableSocket &other) const
+ {
+ return SubSessionHandle() == other.SubSessionHandle()
+ && Session().Handle() == other.Session().Handle();
+ }
+ bool operator<(const QHashableSocket &other) const
+ {
+ if (Session().Handle() == other.Session().Handle())
+ return SubSessionHandle() < other.SubSessionHandle();
+ return Session().Handle() < other.Session().Handle();
+ }
+};
+
+uint qHash(const RSubSessionBase& key);
+
+/*!
+ \internal
+ This class exists in QtCore for the benefit of QSocketNotifier, which uses integer
+ file descriptors in its public API.
+ So we need a way to map between int and RSocket.
+ Additionally, it is used to host the global RSocketServ session
+*/
+class Q_CORE_EXPORT QSymbianSocketManager
+{
+public:
+ QSymbianSocketManager();
+ ~QSymbianSocketManager();
+
+ /*!
+ \internal
+ \return handle to the socket server
+ */
+ RSocketServ& getSocketServer();
+ /*!
+ \internal
+ Adds a symbian socket to the global map
+ \param an open socket
+ \return pseudo file descriptor, -1 if out of resources
+ */
+ int addSocket(const RSocket &sock);
+ /*!
+ \internal
+ Removes a symbian socket from the global map
+ \param an open socket
+ \return true if the socket was in the map
+ */
+ bool removeSocket(const RSocket &sock);
+ /*!
+ \internal
+ Get pseudo file descriptor for a socket
+ \param an open socket
+ \return integer handle, or -1 if not in map
+ */
+ int lookupSocket(const RSocket &sock) const;
+ /*!
+ \internal
+ Get socket for a pseudo file descriptor
+ \param an open socket fd
+ \param sock (out) socket handle
+ \return true on success or false if not in map
+ */
+ bool lookupSocket(int fd, RSocket& sock) const;
+
+ /*!
+ \internal
+ Set the default connection to use for new sockets
+ \param an open connection
+ */
+ void setDefaultConnection(RConnection* con);
+ /*!
+ \internal
+ Get the default connection to use for new sockets
+ \return the connection, or null pointer if there is none set
+ */
+ RConnection *defaultConnection() const;
+
+ /*!
+ \internal
+ Gets a reference to the singleton socket manager
+ */
+ static QSymbianSocketManager& instance();
+private:
+ int allocateSocket();
+
+ const static int max_sockets = 0x20000; //covers all TCP and UDP ports, probably run out of memory first
+ const static int socket_offset = 0x40000000; //hacky way of separating sockets from file descriptors
+ int iNextSocket;
+ QHash<QHashableSocket, int> socketMap;
+ QHash<int, RSocket> reverseSocketMap;
+ mutable QMutex iMutex;
+ RSocketServ iSocketServ;
+ RConnection *iDefaultConnection;
+};
+
QT_END_NAMESPACE
QT_END_HEADER
diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp
index 4c01bde..471028e 100644
--- a/src/corelib/kernel/qeventdispatcher_symbian.cpp
+++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp
@@ -135,7 +135,7 @@ private:
* The QCompleteDeferredAOs class is a special object that runs after all others, which will
* reactivate the objects that were previously not run.
*/
-inline QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher)
+QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher)
: CActive(priority),
m_dispatcher(dispatcher),
m_hasAlreadyRun(false),
@@ -167,12 +167,25 @@ bool QActiveObject::maybeQueueForLater()
}
}
+bool QActiveObject::maybeDeferSocketEvent()
+{
+ Q_ASSERT(!m_hasRunAgain);
+ Q_ASSERT(m_dispatcher);
+ if (!m_dispatcher->areSocketEventsBlocked()) {
+ return false;
+ }
+ m_hasRunAgain = true;
+ m_dispatcher->addDeferredSocketActiveObject(this);
+ return true;
+}
+
void QActiveObject::reactivateAndComplete()
{
+ TInt error = iStatus.Int();
iStatus = KRequestPending;
SetActive();
TRequestStatus *status = &iStatus;
- QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+ QEventDispatcherSymbian::RequestComplete(status, error);
m_hasRunAgain = false;
m_hasAlreadyRun = false;
@@ -181,6 +194,7 @@ void QActiveObject::reactivateAndComplete()
QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher)
: QActiveObject(WAKE_UP_PRIORITY, dispatcher)
{
+ m_hostThreadId = RThread().Id();
CActiveScheduler::Add(this);
iStatus = KRequestPending;
SetActive();
@@ -196,6 +210,15 @@ void QWakeUpActiveObject::DoCancel()
if (iStatus.Int() == KRequestPending) {
TRequestStatus *status = &iStatus;
QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+ } else if (IsActive() && m_hostThreadId != RThread().Id()) {
+ // This is being cancelled in the adopted monitor thread, which can happen if an adopted thread with
+ // an event loop has exited. The event loop creates an event dispatcher with this active object, which may be complete but not run on exit.
+ // We force a cancellation in this thread, because a) the object cannot be deleted while active and b) without a cancellation
+ // the thread semaphore will be one count down.
+ // It is possible for this problem to affect other active objects. They symptom would be that finished signals
+ // from adopted threads are not sent, or they arrive much later than they should.
+ TRequestStatus *status = &iStatus;
+ User::RequestComplete(status, KErrNone);
}
}
@@ -635,10 +658,28 @@ void QSocketActiveObject::DoCancel()
void QSocketActiveObject::RunL()
{
+ if (maybeDeferSocketEvent())
+ return;
if (maybeQueueForLater())
return;
- QT_TRYCATCH_LEAVING(m_dispatcher->socketFired(this));
+ QT_TRYCATCH_LEAVING(run());
+}
+
+void QSocketActiveObject::run()
+{
+ QEvent e(QEvent::SockAct);
+ m_inSocketEvent = true;
+ QCoreApplication::sendEvent(m_notifier, &e);
+ m_inSocketEvent = false;
+
+ if (m_deleteLater) {
+ delete this;
+ } else {
+ iStatus = KRequestPending;
+ SetActive();
+ m_dispatcher->reactivateSocketNotifier(m_notifier);
+ }
}
void QSocketActiveObject::deleteLater()
@@ -849,6 +890,14 @@ bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags fla
CActiveScheduler::Current()->WaitForAnyRequest();
} else {
if (thread.RequestCount() == 0) {
+#ifdef QT_SYMBIAN_PRIORITY_DROP
+ if (idleDetectorThread()->hasRun()) {
+ m_lastIdleRequestTimer.start();
+ idleDetectorThread()->kick();
+ } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
+ User::AfterHighRes(m_delay);
+ }
+#endif
break;
}
// This one should return without delay.
@@ -934,27 +983,6 @@ void QEventDispatcherSymbian::timerFired(int timerId)
return;
}
-void QEventDispatcherSymbian::socketFired(QSocketActiveObject *socketAO)
-{
- if (m_noSocketEvents) {
- m_deferredSocketEvents.append(socketAO);
- return;
- }
-
- QEvent e(QEvent::SockAct);
- socketAO->m_inSocketEvent = true;
- QCoreApplication::sendEvent(socketAO->m_notifier, &e);
- socketAO->m_inSocketEvent = false;
-
- if (socketAO->m_deleteLater) {
- delete socketAO;
- } else {
- socketAO->iStatus = KRequestPending;
- socketAO->SetActive();
- reactivateSocketNotifier(socketAO->m_notifier);
- }
-}
-
void QEventDispatcherSymbian::wakeUpWasCalled()
{
// The reactivation should happen in RunL, right before the call to this function.
@@ -1015,6 +1043,12 @@ inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *obje
inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object)
{
m_deferredActiveObjects.removeAll(object);
+ m_deferredSocketEvents.removeAll(object);
+}
+
+inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject *object)
+{
+ m_deferredSocketEvents.append(object);
}
void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion()
@@ -1040,7 +1074,8 @@ bool QEventDispatcherSymbian::sendDeferredSocketEvents()
bool sentAnyEvents = false;
while (!m_deferredSocketEvents.isEmpty()) {
sentAnyEvents = true;
- socketFired(m_deferredSocketEvents.takeFirst());
+ QActiveObject *object = m_deferredSocketEvents.takeFirst();
+ object->reactivateAndComplete();
}
return sentAnyEvents;
@@ -1059,6 +1094,8 @@ bool QEventDispatcherSymbian::hasPendingEvents()
void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier )
{
+ //note - this is only for "open C" file descriptors
+ //for native sockets, an active object in the symbian socket engine handles this
QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier);
Q_CHECK_PTR(socketAO);
m_notifiers.insert(notifier, socketAO);
@@ -1067,6 +1104,8 @@ void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifie
void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier )
{
+ //note - this is only for "open C" file descriptors
+ //for native sockets, an active object in the symbian socket engine handles this
if (m_selectThread)
m_selectThread->cancelSocketEvents(notifier);
if (m_notifiers.contains(notifier)) {
@@ -1169,6 +1208,86 @@ void CQtActiveScheduler::Error(TInt aError) const
QT_CATCH (const std::bad_alloc&) {} // ignore alloc fails, nothing more can be done
}
+bool QActiveObject::wait(CActive* ao, int ms)
+{
+ if (!ao->IsActive())
+ return true; //request already complete
+ bool timedout = false;
+ if (ms > 0) {
+ TRequestStatus tstat;
+ RTimer t;
+ if (KErrNone != t.CreateLocal())
+ return false;
+ t.HighRes(tstat, ms*1000);
+ User::WaitForRequest(tstat, ao->iStatus);
+ if (tstat != KRequestPending) {
+ timedout = true;
+ } else {
+ t.Cancel();
+ //balance thread semaphore
+ User::WaitForRequest(tstat);
+ }
+ t.Close();
+ } else {
+ User::WaitForRequest(ao->iStatus);
+ }
+ if (timedout)
+ return false;
+
+ //evil cast to allow calling of protected virtual
+ ((QActiveObject*)ao)->RunL();
+
+ //clear active & pending flags
+ ao->iStatus = TRequestStatus();
+
+ return true;
+}
+
+bool QActiveObject::wait(QList<CActive*> aos, int ms)
+{
+ QVector<TRequestStatus*> stati;
+ stati.reserve(aos.count() + 1);
+ foreach (CActive* ao, aos) {
+ if (!ao->IsActive())
+ return true; //request already complete
+ stati.append(&(ao->iStatus));
+ }
+ bool timedout = false;
+ TRequestStatus tstat;
+ RTimer t;
+ if (ms > 0) {
+ if (KErrNone != t.CreateLocal())
+ return false;
+ t.HighRes(tstat, ms*1000);
+ stati.append(&tstat);
+ }
+ User::WaitForNRequest(stati.data(), stati.count());
+ if (ms > 0) {
+ if (tstat != KRequestPending) {
+ timedout = true;
+ } else {
+ t.Cancel();
+ //balance thread semaphore
+ User::WaitForRequest(tstat);
+ }
+ t.Close();
+ }
+ if (timedout)
+ return false;
+
+ foreach (CActive* ao, aos) {
+ if (ao->iStatus != KRequestPending) {
+ //evil cast to allow calling of protected virtual
+ ((QActiveObject*)ao)->RunL();
+
+ //clear active & pending flags
+ ao->iStatus = TRequestStatus();
+ break; //only call one
+ }
+ }
+ return true;
+}
+
QT_END_NAMESPACE
#include "moc_qeventdispatcher_symbian_p.cpp"
diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h
index bdb6dce..6e04bb1 100644
--- a/src/corelib/kernel/qeventdispatcher_symbian_p.h
+++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h
@@ -77,16 +77,19 @@ QT_BEGIN_NAMESPACE
class QEventDispatcherSymbian;
class QTimerActiveObject;
-class QActiveObject : public CActive
+class Q_CORE_EXPORT QActiveObject : public CActive
{
public:
QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher);
~QActiveObject();
bool maybeQueueForLater();
+ bool maybeDeferSocketEvent();
void reactivateAndComplete();
+ static bool wait(CActive* ao, int ms);
+ static bool wait(QList<CActive*> aos, int ms);
protected:
QEventDispatcherSymbian *m_dispatcher;
@@ -107,6 +110,9 @@ public:
protected:
void DoCancel();
void RunL();
+
+private:
+ TThreadId m_hostThreadId;
};
struct SymbianTimerInfo : public QSharedData
@@ -174,6 +180,7 @@ public:
protected:
void DoCancel();
void RunL();
+ void run();
private:
QSocketNotifier *m_notifier;
@@ -242,7 +249,6 @@ public:
void closingDown();
void timerFired(int timerId);
- void socketFired(QSocketActiveObject *socketAO);
void wakeUpWasCalled();
void reactivateSocketNotifier(QSocketNotifier *notifier);
@@ -254,6 +260,9 @@ public:
inline int iterationCount() const { return m_iterationCount; }
+ void addDeferredSocketActiveObject(QActiveObject *object);
+ inline bool areSocketEventsBlocked() const { return m_noSocketEvents; }
+
static void RequestComplete(TRequestStatus *&status, TInt reason);
static void RequestComplete(RThread &threadHandle, TRequestStatus *&status, TInt reason);
@@ -279,8 +288,9 @@ private:
unsigned char m_iterationCount;
bool m_insideTimerEvent;
bool m_noSocketEvents;
- QList<QSocketActiveObject *> m_deferredSocketEvents;
-
+ //deferred until socket events are enabled
+ QList<QActiveObject *> m_deferredSocketEvents;
+ //deferred until idle
QList<QActiveObject *> m_deferredActiveObjects;
int m_delay;
diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp
index 3381afa..18622c7 100644
--- a/src/corelib/kernel/qsystemerror.cpp
+++ b/src/corelib/kernel/qsystemerror.cpp
@@ -154,7 +154,7 @@ static QString symbianErrorString(int errorCode)
case KErrInUse:
return QLatin1String("in use");
case KErrNotReady:
- return QLatin1String("not ready (e.g. FS dismounted, no memory card)");
+ return QLatin1String("not ready (e.g. FS dismounted, network down)");
case KErrCorrupt:
return QLatin1String("corrupt");
case KErrAccessDenied:
@@ -190,7 +190,7 @@ static QString symbianErrorString(int errorCode)
case KErrPermissionDenied:
return QLatin1String("permission denied");
default:
- return QString(QLatin1String("symbian error %d")).arg(errorCode);
+ return QString(QLatin1String("symbian error %1")).arg(errorCode);
}
}
#endif
diff --git a/src/corelib/thread/qthread_symbian.cpp b/src/corelib/thread/qthread_symbian.cpp
index 1474b36..5d8b5cb 100644
--- a/src/corelib/thread/qthread_symbian.cpp
+++ b/src/corelib/thread/qthread_symbian.cpp
@@ -113,6 +113,8 @@ QThreadData *QThreadData::current()
}
data->deref();
}
+ data->isAdopted = true;
+ data->threadId = QThread::currentThreadId();
if (!QCoreApplicationPrivate::theMainThread)
QCoreApplicationPrivate::theMainThread = data->thread;
}
@@ -256,6 +258,13 @@ QCAddAdoptedThread* QCAddAdoptedThread::adoptedThreadAdder = 0;
void QCAdoptedThreadMonitor::RunL()
{
+ if (data->isAdopted) {
+ QThread *thread = data->thread;
+ Q_ASSERT(thread);
+ QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
+ Q_ASSERT(!thread_p->finished);
+ thread_p->finish(thread);
+ }
data->deref();
QCAddAdoptedThread::threadDied();
delete this;
@@ -312,6 +321,7 @@ void *QThreadPrivate::start(void *arg)
// attribute of the thread again once the app gains control in run()
User::SetCritical(User::EProcessCritical);
+ data->threadId = QThread::currentThreadId();
set_thread_data(data);
{
diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp
index d986b9b..5c4085a 100644
--- a/src/corelib/tools/qlocale.cpp
+++ b/src/corelib/tools/qlocale.cpp
@@ -86,6 +86,10 @@ static QLocalePrivate *system_lp = 0;
Q_GLOBAL_STATIC(QLocalePrivate, globalLocalePrivate)
#endif
+#ifdef QT_USE_ICU
+extern bool qt_initIcu(const QString &localeName);
+#endif
+
/******************************************************************************
** Helpers for accessing Qt locale database
*/
@@ -520,6 +524,12 @@ void QLocalePrivate::updateSystemPrivate()
res = sys_locale->query(QSystemLocale::PositiveSign, QVariant());
if (!res.isNull())
system_lp->m_plus = res.toString().at(0).unicode();
+
+#ifdef QT_USE_ICU
+ if (!default_lp)
+ qt_initIcu(system_lp->bcp47Name());
+#endif
+
}
#endif
@@ -879,6 +889,10 @@ void QLocale::setDefault(const QLocale &locale)
{
default_lp = locale.d();
default_number_options = locale.numberOptions();
+
+#ifdef QT_USE_ICU
+ qt_initIcu(locale.bcp47Name());
+#endif
}
/*!
diff --git a/src/corelib/tools/qlocale_icu.cpp b/src/corelib/tools/qlocale_icu.cpp
new file mode 100644
index 0000000..0e283dd
--- /dev/null
+++ b/src/corelib/tools/qlocale_icu.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#include "qlibrary.h"
+#include "qdebug.h"
+
+#include "unicode/uversion.h"
+#include "unicode/ucol.h"
+
+QT_BEGIN_NAMESPACE
+
+typedef UCollator *(*Ptr_ucol_open)(const char *loc, UErrorCode *status);
+typedef void (*Ptr_ucol_close)(UCollator *coll);
+typedef UCollationResult (*Ptr_ucol_strcoll)(const UCollator *coll, const UChar *source, int32_t sourceLength, const UChar *target, int32_t targetLength);
+typedef int32_t (*Ptr_u_strToCase)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode);
+
+static Ptr_ucol_open ptr_ucol_open = 0;
+static Ptr_ucol_strcoll ptr_ucol_strcoll = 0;
+static Ptr_ucol_close ptr_ucol_close = 0;
+static Ptr_u_strToCase ptr_u_strToUpper = 0;
+static Ptr_u_strToCase ptr_u_strToLower = 0;
+
+enum LibLoadStatus
+{
+ ErrorLoading = -1,
+ NotLoaded = 0,
+ Loaded = 1
+};
+
+static LibLoadStatus status = NotLoaded;
+
+static UCollator *icuCollator = 0;
+
+#define STRINGIFY2(x) #x
+#define STRINGIFY(x) STRINGIFY2(x)
+
+bool qt_initIcu(const QString &localeString)
+{
+ if (status == ErrorLoading)
+ return false;
+
+ if (status == NotLoaded) {
+
+ // resolve libicui18n
+ QLibrary lib(QLatin1String("icui18n"), QLatin1String(U_ICU_VERSION_SHORT));
+ if (!lib.load()) {
+ qWarning() << "Unable to load library icui18n" << lib.errorString();
+ status = ErrorLoading;
+ return false;
+ }
+
+ ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open");
+ ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close");
+ ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll");
+
+ if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) {
+ // try again with decorated symbol names
+ ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ }
+
+ if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) {
+ ptr_ucol_open = 0;
+ ptr_ucol_close = 0;
+ ptr_ucol_strcoll = 0;
+
+ qWarning("Unable to find symbols in icui18n");
+ status = ErrorLoading;
+ return false;
+ }
+
+ // resolve libicuuc
+ QLibrary ucLib(QLatin1String("icuuc"), QLatin1String(U_ICU_VERSION_SHORT));
+ if (!ucLib.load()) {
+ qWarning() << "Unable to load library icuuc" << ucLib.errorString();
+ status = ErrorLoading;
+ return false;
+ }
+
+ ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper");
+ ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower");
+
+ if (!ptr_u_strToUpper || !ptr_u_strToLower) {
+ ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower" STRINGIFY(U_ICU_VERSION_SUFFIX));
+ }
+
+ if (!ptr_u_strToUpper || !ptr_u_strToLower) {
+ ptr_u_strToUpper = 0;
+ ptr_u_strToLower = 0;
+
+ qWarning("Unable to find symbols in icuuc");
+ status = ErrorLoading;
+ return false;
+ }
+
+ // success :)
+ status = Loaded;
+ }
+
+ if (icuCollator) {
+ ptr_ucol_close(icuCollator);
+ icuCollator = 0;
+ }
+
+ UErrorCode icuStatus = U_ZERO_ERROR;
+ icuCollator = ptr_ucol_open(localeString.toLatin1().constData(), &icuStatus);
+
+ if (!icuCollator) {
+ qWarning("Unable to open locale %s in ICU, error code %d", qPrintable(localeString), icuStatus);
+ return false;
+ }
+
+ return true;
+}
+
+bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result)
+{
+ Q_ASSERT(result);
+ Q_ASSERT(source);
+ Q_ASSERT(target);
+
+ if (!icuCollator)
+ return false;
+
+ *result = ptr_ucol_strcoll(icuCollator, reinterpret_cast<const UChar *>(source), int32_t(sourceLength),
+ reinterpret_cast<const UChar *>(target), int32_t(targetLength));
+
+ return true;
+}
+
+// caseFunc can either be u_strToUpper or u_strToLower
+static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &locale, Ptr_u_strToCase caseFunc)
+{
+ Q_ASSERT(out);
+
+ if (!icuCollator)
+ return false;
+
+ QString result(str.size(), Qt::Uninitialized);
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t size = caseFunc(reinterpret_cast<UChar *>(result.data()), result.size(),
+ reinterpret_cast<const UChar *>(str.constData()), str.size(),
+ locale.bcp47Name().toLatin1().constData(), &status);
+
+ if (U_FAILURE(status))
+ return false;
+
+ if (size < result.size()) {
+ result.resize(size);
+ } else if (size > result.size()) {
+ // the resulting string is larger than our source string
+ result.resize(size);
+
+ status = U_ZERO_ERROR;
+ size = caseFunc(reinterpret_cast<UChar *>(result.data()), result.size(),
+ reinterpret_cast<const UChar *>(str.constData()), str.size(),
+ locale.bcp47Name().toLatin1().constData(), &status);
+
+ if (U_FAILURE(status))
+ return false;
+
+ // if the sizes don't match now, we give up.
+ if (size != result.size())
+ return false;
+ }
+
+ *out = result;
+ return true;
+}
+
+bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale)
+{
+ return qt_u_strToCase(str, out, locale, ptr_u_strToUpper);
+}
+
+bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale)
+{
+ return qt_u_strToCase(str, out, locale, ptr_u_strToLower);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp
index b7272ec..5493ba9 100644
--- a/src/corelib/tools/qstring.cpp
+++ b/src/corelib/tools/qstring.cpp
@@ -106,6 +106,14 @@ QTextCodec *QString::codecForCStrings;
static QHash<void *, QByteArray> *asciiCache = 0;
#endif
+#ifdef QT_USE_ICU
+// qlocale_icu.cpp
+extern bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result);
+extern bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale);
+extern bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale);
+#endif
+
+
// internal
int qFindString(const QChar *haystack, int haystackLen, int from,
const QChar *needle, int needleLen, Qt::CaseSensitivity cs);
@@ -431,7 +439,6 @@ const QString::Null QString::null = { };
\ingroup shared
\ingroup string-processing
-
QString stores a string of 16-bit \l{QChar}s, where each QChar
corresponds one Unicode 4.0 character. (Unicode characters
with code values above 65535 are stored using surrogate pairs,
@@ -4829,6 +4836,14 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1,
TPtrC p2 = TPtrC16(reinterpret_cast<const TUint16 *>(data2), length2);
return p1.CompareC(p2);
#elif defined(Q_OS_UNIX)
+# if defined(QT_USE_ICU)
+ int res;
+ if (qt_ucol_strcoll(data1, length1, data2, length2, &res)) {
+ if (res == 0)
+ res = ucstrcmp(data1, length1, data2, length2);
+ return res;
+ } // else fall through
+# endif
// declared in <string.h>
int delta = strcoll(toLocal8Bit_helper(data1, length1), toLocal8Bit_helper(data2, length2));
if (delta == 0)
@@ -4964,6 +4979,15 @@ QString QString::toLower() const
if (!d->size)
return *this;
+#ifdef QT_USE_ICU
+ {
+ QString result;
+ if (qt_u_strToLower(*this, &result, QLocale()))
+ return result;
+ // else fall through and use Qt's toUpper
+ }
+#endif
+
const ushort *e = d->data + d->size;
// this avoids one out of bounds check in the loop
@@ -5055,6 +5079,15 @@ QString QString::toUpper() const
if (!d->size)
return *this;
+#ifdef QT_USE_ICU
+ {
+ QString result;
+ if (qt_u_strToUpper(*this, &result, QLocale()))
+ return result;
+ // else fall through and use Qt's toUpper
+ }
+#endif
+
const ushort *e = d->data + d->size;
// this avoids one out of bounds check in the loop
diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri
index 849dc63..0c2cf16 100644
--- a/src/corelib/tools/tools.pri
+++ b/src/corelib/tools/tools.pri
@@ -97,6 +97,11 @@ else:SOURCES += tools/qelapsedtimer_generic.cpp
contains(QT_CONFIG, zlib):include($$PWD/../../3rdparty/zlib.pri)
else:include($$PWD/../../3rdparty/zlib_dependency.pri)
+contains(QT_CONFIG,icu) {
+ SOURCES += tools/qlocale_icu.cpp
+ DEFINES += QT_USE_ICU
+}
+
DEFINES += HB_EXPORT=Q_CORE_EXPORT
INCLUDEPATH += ../3rdparty/harfbuzz/src
HEADERS += ../3rdparty/harfbuzz/src/harfbuzz.h