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

Last change on this file since 752 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

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