| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| |
| #include <qcoreapplication.h> |
| #include <qdir.h> |
| #include <qurl.h> |
| #include <private/qcore_symbian_p.h> |
| |
| #include <f32file.h> // TDriveUnit etc |
| #include <pathinfo.h> // PathInfo |
| |
| #ifndef USE_SCHEMEHANDLER |
| #ifdef Q_WS_S60 |
| // This flag changes the implementation to use S60 CDcoumentHandler |
| // instead of apparc when opening the files |
| #define USE_DOCUMENTHANDLER |
| #endif |
| |
| #include <txtrich.h> // CRichText |
| #include <eikenv.h> // CEikonEnv |
| #include <apgcli.h> // RApaLsSession |
| #include <apgtask.h> // TApaTaskList, TApaTask |
| #include <rsendas.h> // RSendAs |
| #include <rsendasmessage.h> // RSendAsMessage |
| |
| #ifdef USE_DOCUMENTHANDLER |
| #include <DocumentHandler.h> // CDocumentHandler |
| #include <AknServerApp.h> |
| #endif |
| #else // USE_SCHEMEHANDLER |
| #include <schemehandler.h> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| _LIT(KCacheSubDir, "Cache\\"); |
| _LIT(KSysBin, "\\Sys\\Bin\\"); |
| _LIT(KBrowserPrefix, "4 " ); |
| _LIT(KFontsDir, "z:\\resource\\Fonts\\"); |
| |
| #ifndef USE_SCHEMEHANDLER |
| // copied from miutset.h, so we don't get a dependency into the app layer |
| const TUid KUidMsgTypeSMTP = {0x10001028}; // 268439592 |
| const TUid KUidBrowser = { 0x10008D39 }; |
| |
| template<class R> |
| class QAutoClose |
| { |
| public: |
| QAutoClose(R& aObj) : mPtr(&aObj) {} |
| ~QAutoClose() |
| { |
| if (mPtr) |
| mPtr->Close(); |
| } |
| void Forget() |
| { |
| mPtr = 0; |
| } |
| private: |
| QAutoClose(const QAutoClose&); |
| QAutoClose& operator=(const QAutoClose&); |
| private: |
| R* mPtr; |
| }; |
| |
| #ifdef USE_DOCUMENTHANDLER |
| class QS60DocumentHandler : public MAknServerAppExitObserver |
| { |
| public: |
| QS60DocumentHandler() :docHandler(0) {} |
| |
| ~QS60DocumentHandler() { |
| delete docHandler; |
| } |
| |
| CDocumentHandler& documentHandler() { |
| // In case user calls openUrl twice subsequently, before the first embedded app is closed |
| // we use the same CDocumentHandler instance. Using same instance makes sure the first |
| // launched embedded app is closed and latter one gets embedded to our app. |
| // Using different instance would help only theoretically since user cannot interact with |
| // several embedded apps at the same time. |
| if(!docHandler) { |
| QT_TRAP_THROWING(docHandler = CDocumentHandler::NewL()); |
| docHandler->SetExitObserver(this); |
| } |
| return *docHandler; |
| } |
| |
| private: // From MAknServerAppExitObserver |
| void HandleServerAppExit(TInt /*aReason*/) { |
| delete docHandler; |
| docHandler = 0; |
| } |
| |
| private: |
| CDocumentHandler* docHandler; |
| }; |
| Q_GLOBAL_STATIC(QS60DocumentHandler, qt_s60_documenthandler); |
| #endif |
| |
| static void handleMailtoSchemeLX(const QUrl &url) |
| { |
| // this function has many intermingled leaves and throws. Qt and Symbian objects do not have |
| // destructor dependencies, and cleanup object is used to prevent cleanup stack dependency on stack. |
| QString recipient = url.path(); |
| QString subject = url.queryItemValue(QLatin1String("subject")); |
| QString body = url.queryItemValue(QLatin1String("body")); |
| QString to = url.queryItemValue(QLatin1String("to")); |
| QString cc = url.queryItemValue(QLatin1String("cc")); |
| QString bcc = url.queryItemValue(QLatin1String("bcc")); |
| |
| // these fields might have comma separated addresses |
| QStringList recipients = recipient.split(QLatin1String(","), QString::SkipEmptyParts); |
| QStringList tos = to.split(QLatin1String(","), QString::SkipEmptyParts); |
| QStringList ccs = cc.split(QLatin1String(","), QString::SkipEmptyParts); |
| QStringList bccs = bcc.split(QLatin1String(","), QString::SkipEmptyParts); |
| |
| RSendAs sendAs; |
| User::LeaveIfError(sendAs.Connect()); |
| QAutoClose<RSendAs> sendAsCleanup(sendAs); |
| |
| CSendAsAccounts* accounts = CSendAsAccounts::NewL(); |
| CleanupStack::PushL(accounts); |
| sendAs.AvailableAccountsL(KUidMsgTypeSMTP, *accounts); |
| TInt count = accounts->Count(); |
| CleanupStack::PopAndDestroy(accounts); |
| |
| if(!count) { |
| // TODO: Task 259192: We should try to create account if count == 0 |
| // CSendUi would provide account creation service for us, but it requires ridicilous |
| // capabilities: LocalServices NetworkServices ReadDeviceData ReadUserData WriteDeviceData WriteUserData |
| User::Leave(KErrNotSupported); |
| } else { |
| RSendAsMessage sendAsMessage; |
| sendAsMessage.CreateL(sendAs, KUidMsgTypeSMTP); |
| QAutoClose<RSendAsMessage> sendAsMessageCleanup(sendAsMessage); |
| |
| |
| // Subject |
| sendAsMessage.SetSubjectL(qt_QString2TPtrC(subject)); |
| |
| // Body |
| sendAsMessage.SetBodyTextL(qt_QString2TPtrC(body)); |
| |
| // To |
| foreach(QString item, recipients) |
| sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo); |
| |
| foreach(QString item, tos) |
| sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo); |
| |
| // Cc |
| foreach(QString item, ccs) |
| sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientCc); |
| |
| // Bcc |
| foreach(QString item, bccs) |
| sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientBcc); |
| |
| // send the message |
| sendAsMessage.LaunchEditorAndCloseL(); |
| // sendAsMessage is already closed |
| sendAsMessageCleanup.Forget(); |
| } |
| } |
| |
| static bool handleMailtoScheme(const QUrl &url) |
| { |
| TRAPD(err, QT_TRYCATCH_LEAVING(handleMailtoSchemeLX(url))); |
| return err ? false : true; |
| } |
| |
| static void handleOtherSchemesL(const TDesC& aUrl) |
| { |
| // Other schemes are at the moment passed to WEB browser |
| HBufC* buf16 = HBufC::NewLC(aUrl.Length() + KBrowserPrefix.iTypeLength); |
| buf16->Des().Copy(KBrowserPrefix); // Prefix used to launch correct browser view |
| buf16->Des().Append(aUrl); |
| |
| TApaTaskList taskList(CEikonEnv::Static()->WsSession()); |
| TApaTask task = taskList.FindApp(KUidBrowser); |
| if (task.Exists()){ |
| // Switch to existing browser instance |
| task.BringToForeground(); |
| HBufC8* param8 = HBufC8::NewLC(buf16->Length()); |
| param8->Des().Append(buf16->Des()); |
| task.SendMessage(TUid::Uid( 0 ), *param8); // Uid is not used |
| CleanupStack::PopAndDestroy(param8); |
| } else { |
| // Start a new browser instance |
| RApaLsSession appArcSession; |
| User::LeaveIfError(appArcSession.Connect()); |
| CleanupClosePushL<RApaLsSession>(appArcSession); |
| TThreadId id; |
| appArcSession.StartDocument(*buf16, KUidBrowser, id); |
| CleanupStack::PopAndDestroy(); // appArcSession |
| } |
| |
| CleanupStack::PopAndDestroy(buf16); |
| } |
| |
| static bool handleOtherSchemes(const QUrl &url) |
| { |
| QString encUrl(QString::fromUtf8(url.toEncoded())); |
| TPtrC urlPtr(qt_QString2TPtrC(encUrl)); |
| TRAPD( err, handleOtherSchemesL(urlPtr)); |
| return err ? false : true; |
| } |
| |
| |
| static void openDocumentL(const TDesC& aUrl) |
| { |
| #ifndef USE_DOCUMENTHANDLER |
| // Start app associated to file MIME type by using RApaLsSession |
| // Apparc base method cannot be used to open app in embedded mode, |
| // but seems to be most stable way at the moment |
| RApaLsSession appArcSession; |
| User::LeaveIfError(appArcSession.Connect()); |
| CleanupClosePushL<RApaLsSession>(appArcSession); |
| TThreadId id; |
| // ESwitchFiles means do not start another instance |
| // Leaves if file does not exist, leave is trapped in openDocument and false returned to user. |
| User::LeaveIfError(appArcSession.StartDocument(aUrl, id, |
| RApaLsSession::ESwitchFiles)); // ELaunchNewApp |
| CleanupStack::PopAndDestroy(); // appArcSession |
| #else |
| // This is an alternative way to launch app associated to MIME type |
| // CDocumentHandler also supports opening apps in embedded mode. |
| TDataType temp; |
| qt_s60_documenthandler()->documentHandler().OpenFileEmbeddedL(aUrl, temp); |
| #endif |
| } |
| |
| static bool launchWebBrowser(const QUrl &url) |
| { |
| if (!url.isValid()) |
| return false; |
| |
| if (url.scheme() == QLatin1String("mailto")) { |
| return handleMailtoScheme(url); |
| } |
| return handleOtherSchemes( url ); |
| } |
| |
| static bool openDocument(const QUrl &file) |
| { |
| if (!file.isValid()) |
| return false; |
| |
| QString filePath = file.toLocalFile(); |
| filePath = QDir::toNativeSeparators(filePath); |
| TPtrC filePathPtr(qt_QString2TPtrC(filePath)); |
| TRAPD(err, openDocumentL(filePathPtr)); |
| return err ? false : true; |
| } |
| |
| #else //USE_SCHEMEHANDLER |
| // The schemehandler component only exist in private SDK. This implementation |
| // exist here just for convenience in case that we need to use it later on |
| // The schemehandle based implementation is not yet tested. |
| |
| // The biggest advantage of schemehandler is that it can handle |
| // wide range of schemes and is extensible by plugins |
| static void handleUrlL(const TDesC& aUrl) |
| { |
| CSchemeHandler* schemeHandler = CSchemeHandler::NewL(aUrl); |
| CleanupStack::PushL(schemeHandler); |
| schemeHandler->HandleUrlStandaloneL(); // Process the Url in standalone mode |
| CleanupStack::PopAndDestroy(); |
| } |
| |
| static bool handleUrl(const QUrl &url) |
| { |
| if (!url.isValid()) |
| return false; |
| |
| QString urlString(url.toEncoded()); |
| TPtrC urlPtr(qt_QString2TPtrC(urlString)); |
| TRAPD( err, handleUrlL(urlPtr)); |
| return err ? false : true; |
| } |
| |
| static bool launchWebBrowser(const QUrl &url) |
| { |
| return handleUrl(url); |
| } |
| |
| static bool openDocument(const QUrl &file) |
| { |
| return handleUrl(file); |
| } |
| |
| #endif //USE_SCHEMEHANDLER |
| |
| // Common functions to all implementations |
| |
| static TDriveUnit exeDrive() |
| { |
| RProcess me; |
| TFileName processFileName = me.FileName(); |
| TDriveUnit drive(processFileName); |
| return drive; |
| } |
| |
| static TDriveUnit writableExeDrive() |
| { |
| TDriveUnit drive = exeDrive(); |
| if (drive.operator TInt() == EDriveZ) |
| return TDriveUnit(EDriveC); |
| return drive; |
| } |
| |
| static TPtrC writableDataRoot() |
| { |
| TDriveUnit drive = exeDrive(); |
| switch (drive.operator TInt()){ |
| case EDriveC: |
| return PathInfo::PhoneMemoryRootPath(); |
| break; |
| case EDriveE: |
| return PathInfo::MemoryCardRootPath(); |
| break; |
| case EDriveZ: |
| // It is not possible to write on ROM drive -> |
| // return phone mem root path instead |
| return PathInfo::PhoneMemoryRootPath(); |
| break; |
| default: |
| return PathInfo::PhoneMemoryRootPath(); |
| break; |
| } |
| } |
| |
| QString QDesktopServices::storageLocation(StandardLocation type) |
| { |
| TFileName path; |
| |
| switch (type) { |
| case DesktopLocation: |
| qWarning("No desktop concept in Symbian OS"); |
| // But lets still use some feasible default |
| path.Append(writableDataRoot()); |
| break; |
| case DocumentsLocation: |
| path.Append(writableDataRoot()); |
| break; |
| case FontsLocation: |
| path.Append(KFontsDir); |
| break; |
| case ApplicationsLocation: |
| path.Append(exeDrive().Name()); |
| path.Append(KSysBin); |
| break; |
| case MusicLocation: |
| path.Append(writableDataRoot()); |
| path.Append(PathInfo::SoundsPath()); |
| break; |
| case MoviesLocation: |
| path.Append(writableDataRoot()); |
| path.Append(PathInfo::VideosPath()); |
| break; |
| case PicturesLocation: |
| path.Append(writableDataRoot()); |
| path.Append(PathInfo::ImagesPath()); |
| break; |
| case TempLocation: |
| return QDir::tempPath(); |
| break; |
| case HomeLocation: |
| path.Append(writableDataRoot()); |
| //return QDir::homePath(); break; |
| break; |
| case DataLocation: |
| qt_s60GetRFs().PrivatePath(path); |
| path.Insert(0, writableExeDrive().Name()); |
| break; |
| case CacheLocation: |
| qt_s60GetRFs().PrivatePath(path); |
| path.Insert(0, writableExeDrive().Name()); |
| path.Append(KCacheSubDir); |
| break; |
| default: |
| // Lets use feasible default |
| path.Append(writableDataRoot()); |
| break; |
| } |
| |
| // Convert to cross-platform format and clean the path |
| QString nativePath = QString::fromUtf16(path.Ptr(), path.Length()); |
| QString qtPath = QDir::fromNativeSeparators(nativePath); |
| qtPath = QDir::cleanPath(qtPath); |
| |
| // Note: The storage location returned can be a directory that does not exist; |
| // i.e., it may need to be created by the system or the user. |
| return qtPath; |
| } |
| |
| typedef QString (*LocalizerFunc)(QString&); |
| |
| static QString defaultLocalizedDirectoryName(QString&) |
| { |
| return QString(); |
| } |
| |
| QString QDesktopServices::displayName(StandardLocation type) |
| { |
| static LocalizerFunc ptrLocalizerFunc = NULL; |
| |
| if (!ptrLocalizerFunc) { |
| ptrLocalizerFunc = reinterpret_cast<LocalizerFunc> |
| (qt_resolveS60PluginFunc(S60Plugin_LocalizedDirectoryName)); |
| if (!ptrLocalizerFunc) |
| ptrLocalizerFunc = &defaultLocalizedDirectoryName; |
| } |
| |
| QString rawPath = storageLocation(type); |
| return ptrLocalizerFunc(rawPath); |
| } |
| |
| |
| QT_END_NAMESPACE |