source: trunk/src/gui/util/qdesktopservices_s60.cpp@ 829

Last change on this file since 829 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 14.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42// This flag changes the implementation to use S60 CDcoumentHandler
43// instead of apparch when opening the files
44#define USE_DOCUMENTHANDLER
45
46#include <qcoreapplication.h>
47#include <qdir.h>
48#include <qurl.h>
49#include <private/qcore_symbian_p.h>
50
51#include <txtrich.h> // CRichText
52#include <f32file.h> // TDriveUnit etc
53#include <eikenv.h> // CEikonEnv
54#include <apgcli.h> // RApaLsSession
55#include <apgtask.h> // TApaTaskList, TApaTask
56#include <rsendas.h> // RSendAs
57#include <rsendasmessage.h> // RSendAsMessage
58
59// copied from miutset.h, so we don't get a dependency into the app layer
60const TUid KUidMsgTypeSMTP = {0x10001028}; // 268439592
61
62#ifdef Q_WS_S60
63# include <pathinfo.h> // PathInfo
64# ifdef USE_DOCUMENTHANDLER
65# include <DocumentHandler.h> // CDocumentHandler
66# include <AknServerApp.h>
67# endif
68#else
69# warning CDocumentHandler requires support for S60
70# undef USE_DOCUMENTHANDLER // Fallback to RApaLsSession based implementation
71#endif
72
73QT_BEGIN_NAMESPACE
74
75_LIT(KCacheSubDir, "Cache\\");
76_LIT(KSysBin, "\\Sys\\Bin\\");
77_LIT(KBrowserPrefix, "4 " );
78_LIT(KFontsDir, "z:\\resource\\Fonts\\");
79const TUid KUidBrowser = { 0x10008D39 };
80
81template<class R>
82class QAutoClose
83{
84public:
85 QAutoClose(R& aObj) : mPtr(&aObj) {}
86 ~QAutoClose()
87 {
88 if (mPtr)
89 mPtr->Close();
90 }
91 void Forget()
92 {
93 mPtr = 0;
94 }
95private:
96 QAutoClose(const QAutoClose&);
97 QAutoClose& operator=(const QAutoClose&);
98private:
99 R* mPtr;
100};
101
102#ifdef USE_DOCUMENTHANDLER
103class QS60DocumentHandler : public MAknServerAppExitObserver
104{
105public:
106 QS60DocumentHandler() :docHandler(0) {}
107
108 ~QS60DocumentHandler() {
109 delete docHandler;
110 }
111
112 CDocumentHandler& documentHandler() {
113 // In case user calls openUrl twice subsequently, before the first embedded app is closed
114 // we use the same CDocumentHandler instance. Using same instance makes sure the first
115 // launched embedded app is closed and latter one gets embedded to our app.
116 // Using different instance would help only theoretically since user cannot interact with
117 // several embedded apps at the same time.
118 if(!docHandler) {
119 QT_TRAP_THROWING(docHandler = CDocumentHandler::NewL());
120 docHandler->SetExitObserver(this);
121 }
122 return *docHandler;
123 }
124
125private: // From MAknServerAppExitObserver
126 void HandleServerAppExit(TInt /*aReason*/) {
127 delete docHandler;
128 docHandler = 0;
129 }
130
131private:
132 CDocumentHandler* docHandler;
133};
134Q_GLOBAL_STATIC(QS60DocumentHandler, qt_s60_documenthandler);
135#endif
136
137
138static void handleMailtoSchemeLX(const QUrl &url)
139{
140 // this function has many intermingled leaves and throws. Qt and Symbian objects do not have
141 // destructor dependencies, and cleanup object is used to prevent cleanup stack dependency on stack.
142 QString recipient = url.path();
143 QString subject = url.queryItemValue(QLatin1String("subject"));
144 QString body = url.queryItemValue(QLatin1String("body"));
145 QString to = url.queryItemValue(QLatin1String("to"));
146 QString cc = url.queryItemValue(QLatin1String("cc"));
147 QString bcc = url.queryItemValue(QLatin1String("bcc"));
148
149 // these fields might have comma separated addresses
150 QStringList recipients = recipient.split(QLatin1String(","), QString::SkipEmptyParts);
151 QStringList tos = to.split(QLatin1String(","), QString::SkipEmptyParts);
152 QStringList ccs = cc.split(QLatin1String(","), QString::SkipEmptyParts);
153 QStringList bccs = bcc.split(QLatin1String(","), QString::SkipEmptyParts);
154
155
156 RSendAs sendAs;
157 User::LeaveIfError(sendAs.Connect());
158 QAutoClose<RSendAs> sendAsCleanup(sendAs);
159
160
161 CSendAsAccounts* accounts = CSendAsAccounts::NewL();
162 CleanupStack::PushL(accounts);
163 sendAs.AvailableAccountsL(KUidMsgTypeSMTP, *accounts);
164 TInt count = accounts->Count();
165 CleanupStack::PopAndDestroy(accounts);
166
167 if(!count) {
168 // TODO: Task 259192: We should try to create account if count == 0
169 // CSendUi would provide account creation service for us, but it requires ridicilous
170 // capabilities: LocalServices NetworkServices ReadDeviceData ReadUserData WriteDeviceData WriteUserData
171 User::Leave(KErrNotSupported);
172 } else {
173 RSendAsMessage sendAsMessage;
174 sendAsMessage.CreateL(sendAs, KUidMsgTypeSMTP);
175 QAutoClose<RSendAsMessage> sendAsMessageCleanup(sendAsMessage);
176
177
178 // Subject
179 sendAsMessage.SetSubjectL(qt_QString2TPtrC(subject));
180
181 // Body
182 sendAsMessage.SetBodyTextL(qt_QString2TPtrC(body));
183
184 // To
185 foreach(QString item, recipients)
186 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo);
187
188 foreach(QString item, tos)
189 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo);
190
191 // Cc
192 foreach(QString item, ccs)
193 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientCc);
194
195 // Bcc
196 foreach(QString item, bccs)
197 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientBcc);
198
199 // send the message
200 sendAsMessage.LaunchEditorAndCloseL();
201 // sendAsMessage is already closed
202 sendAsMessageCleanup.Forget();
203 }
204}
205
206static bool handleMailtoScheme(const QUrl &url)
207{
208 TRAPD(err, QT_TRYCATCH_LEAVING(handleMailtoSchemeLX(url)));
209 return err ? false : true;
210}
211
212static void handleOtherSchemesL(const TDesC& aUrl)
213{
214 // Other schemes are at the moment passed to WEB browser
215 HBufC* buf16 = HBufC::NewLC(aUrl.Length() + KBrowserPrefix.iTypeLength);
216 buf16->Des().Copy(KBrowserPrefix); // Prefix used to launch correct browser view
217 buf16->Des().Append(aUrl);
218
219 TApaTaskList taskList(CEikonEnv::Static()->WsSession());
220 TApaTask task = taskList.FindApp(KUidBrowser);
221 if (task.Exists()){
222 // Switch to existing browser instance
223 HBufC8* param8 = HBufC8::NewLC(buf16->Length());
224 param8->Des().Append(buf16->Des());
225 task.SendMessage(TUid::Uid( 0 ), *param8); // Uid is not used
226 CleanupStack::PopAndDestroy(param8);
227 } else {
228 // Start a new browser instance
229 RApaLsSession appArcSession;
230 User::LeaveIfError(appArcSession.Connect());
231 CleanupClosePushL<RApaLsSession>(appArcSession);
232 TThreadId id;
233 appArcSession.StartDocument(*buf16, KUidBrowser, id);
234 CleanupStack::PopAndDestroy(); // appArcSession
235 }
236
237 CleanupStack::PopAndDestroy(buf16);
238}
239
240static bool handleOtherSchemes(const QUrl &url)
241{
242 QString encUrl(QString::fromUtf8(url.toEncoded()));
243 TPtrC urlPtr(qt_QString2TPtrC(encUrl));
244 TRAPD( err, handleOtherSchemesL(urlPtr));
245 return err ? false : true;
246}
247
248static TDriveUnit exeDrive()
249{
250 RProcess me;
251 TFileName processFileName = me.FileName();
252 TDriveUnit drive(processFileName);
253 return drive;
254}
255
256static TDriveUnit writableExeDrive()
257{
258 TDriveUnit drive = exeDrive();
259 if(drive.operator TInt() == EDriveZ)
260 return TDriveUnit(EDriveC);
261 return drive;
262}
263
264static TPtrC writableDataRoot()
265{
266 TDriveUnit drive = exeDrive();
267#ifdef Q_WS_S60
268 switch(drive.operator TInt()){
269 case EDriveC:
270 return PathInfo::PhoneMemoryRootPath();
271 break;
272 case EDriveE:
273 return PathInfo::MemoryCardRootPath();
274 break;
275 case EDriveZ:
276 // It is not possible to write on ROM drive ->
277 // return phone mem root path instead
278 return PathInfo::PhoneMemoryRootPath();
279 break;
280 default:
281 return PathInfo::PhoneMemoryRootPath();
282 break;
283 }
284#else
285#warning No fallback implementation of writableDataRoot()
286 return 0;
287#endif
288}
289
290static void openDocumentL(const TDesC& aUrl)
291{
292#ifndef USE_DOCUMENTHANDLER
293 // Start app associated to file MIME type by using RApaLsSession
294 // Apparc base method cannot be used to open app in embedded mode,
295 // but seems to be most stable way at the moment
296 RApaLsSession appArcSession;
297 User::LeaveIfError(appArcSession.Connect());
298 CleanupClosePushL<RApaLsSession>(appArcSession);
299 TThreadId id;
300 // ESwitchFiles means do not start another instance
301 // Leaves if file does not exist, leave is trapped in openDocument and false returned to user.
302 User::LeaveIfError(appArcSession.StartDocument(aUrl, id,
303 RApaLsSession::ESwitchFiles)); // ELaunchNewApp
304 CleanupStack::PopAndDestroy(); // appArcSession
305#else
306 // This is an alternative way to launch app associated to MIME type
307 // CDocumentHandler also supports opening apps in embedded mode.
308 TDataType temp;
309 qt_s60_documenthandler()->documentHandler().OpenFileEmbeddedL(aUrl, temp);
310#endif
311}
312
313#ifdef USE_SCHEMEHANDLER
314// The schemehandler component only exist in private SDK. This implementation
315// exist here just for convenience in case that we need to use it later on
316// The schemehandle based implementation is not yet tested.
317
318// The biggest advantage of schemehandler is that it can handle
319// wide range of schemes and is extensible by plugins
320static bool handleUrl(const QUrl &url)
321{
322 if (!url.isValid())
323 return false;
324
325 QString urlString(url.toString());
326 TPtrC urlPtr(qt_QString2TPtrC(urlString));
327 TRAPD( err, handleUrlL(urlPtr));
328 return err ? false : true;
329}
330
331static void handleUrlL(const TDesC& aUrl)
332{
333 CSchemeHandler* schemeHandler = CSchemeHandler::NewL(aUrl);
334 CleanupStack::PushL(schemeHandler);
335 schemeHandler->HandleUrlStandaloneL(); // Process the Url in standalone mode
336 CleanupStack::PopAndDestroy();
337}
338static bool launchWebBrowser(const QUrl &url)
339{
340 return handleUrl(url);
341}
342
343static bool openDocument(const QUrl &file)
344{
345 return handleUrl(url);
346}
347#endif
348
349static bool launchWebBrowser(const QUrl &url)
350{
351 if (!url.isValid())
352 return false;
353
354 if (url.scheme() == QLatin1String("mailto")) {
355 return handleMailtoScheme(url);
356 }
357 return handleOtherSchemes( url );
358}
359
360static bool openDocument(const QUrl &file)
361{
362 if (!file.isValid())
363 return false;
364
365 QString filePath = file.toLocalFile();
366 filePath = QDir::toNativeSeparators(filePath);
367 TPtrC filePathPtr(qt_QString2TPtrC(filePath));
368 TRAPD(err, openDocumentL(filePathPtr));
369 return err ? false : true;
370}
371
372QString QDesktopServices::storageLocation(StandardLocation type)
373{
374 TFileName path;
375
376 switch (type) {
377 case DesktopLocation:
378 qWarning("No desktop concept in Symbian OS");
379 // But lets still use some feasible default
380 path.Append(writableDataRoot());
381 break;
382 case DocumentsLocation:
383 path.Append(writableDataRoot());
384 break;
385 case FontsLocation:
386 path.Append(KFontsDir);
387 break;
388 case ApplicationsLocation:
389 path.Append(exeDrive().Name());
390 path.Append(KSysBin);
391 break;
392 case MusicLocation:
393 path.Append(writableDataRoot());
394#ifdef Q_WS_S60
395 path.Append(PathInfo::SoundsPath());
396#endif
397 break;
398 case MoviesLocation:
399 path.Append(writableDataRoot());
400#ifdef Q_WS_S60
401 path.Append(PathInfo::VideosPath());
402#endif
403 break;
404 case PicturesLocation:
405 path.Append(writableDataRoot());
406#ifdef Q_WS_S60
407 path.Append(PathInfo::ImagesPath());
408#endif
409 break;
410 case TempLocation:
411 return QDir::tempPath();
412 break;
413 case HomeLocation:
414 path.Append(writableDataRoot());
415 //return QDir::homePath(); break;
416 break;
417 case DataLocation:
418 CEikonEnv::Static()->FsSession().PrivatePath(path);
419 path.Insert(0, writableExeDrive().Name());
420 break;
421 case CacheLocation:
422 CEikonEnv::Static()->FsSession().PrivatePath(path);
423 path.Insert(0, writableExeDrive().Name());
424 path.Append(KCacheSubDir);
425 break;
426 default:
427 // Lets use feasible default
428 path.Append(writableDataRoot());
429 break;
430 }
431
432 // Convert to cross-platform format and clean the path
433 QString nativePath = QString::fromUtf16(path.Ptr(), path.Length());
434 QString qtPath = QDir::fromNativeSeparators(nativePath);
435 qtPath = QDir::cleanPath(qtPath);
436
437 // Note: The storage location returned can be a directory that does not exist;
438 // i.e., it may need to be created by the system or the user.
439 return qtPath;
440}
441
442typedef QString (*LocalizerFunc)(QString&);
443
444static QString defaultLocalizedDirectoryName(QString&)
445{
446 return QString();
447}
448
449QString QDesktopServices::displayName(StandardLocation type)
450{
451 static LocalizerFunc ptrLocalizerFunc = NULL;
452
453 if (!ptrLocalizerFunc) {
454 ptrLocalizerFunc = reinterpret_cast<LocalizerFunc>
455 (qt_resolveS60PluginFunc(S60Plugin_LocalizedDirectoryName));
456 if (!ptrLocalizerFunc)
457 ptrLocalizerFunc = &defaultLocalizedDirectoryName;
458 }
459
460 QString rawPath = storageLocation(type);
461 return ptrLocalizerFunc(rawPath);
462}
463
464
465QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.