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