source: trunk/src/qt3support/dialogs/q3filedialog_mac.cpp@ 116

Last change on this file since 116 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 21.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the Qt3Support module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "q3filedialog.h"
43
44#ifndef QT_NO_FILEDIALOG
45
46/*****************************************************************************
47 Q3FileDialog debug facilities
48 *****************************************************************************/
49//#define DEBUG_FILEDIALOG_FILTERS
50
51#include "qapplication.h"
52#include <private/qapplication_p.h>
53#include <private/qt_mac_p.h>
54#include "qregexp.h"
55#include "qbuffer.h"
56#include "qstringlist.h"
57#include "qtextcodec.h"
58#include "qdesktopwidget.h"
59#include <stdlib.h>
60
61QT_BEGIN_NAMESPACE
62
63/*****************************************************************************
64 Externals
65 *****************************************************************************/
66extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp
67extern const char qt3_file_dialog_filter_reg_exp[]; //qfiledialog.cpp
68
69/*****************************************************************************
70 Q3FileDialog utility functions
71 *****************************************************************************/
72static UInt8 *str_buffer = NULL;
73static void cleanup_str_buffer()
74{
75 if(str_buffer) {
76 free(str_buffer);
77 str_buffer = NULL;
78 }
79}
80
81// Returns the wildcard part of a filter.
82struct qt_mac_filter_name {
83 QString description, regxp, filter;
84};
85static qt_mac_filter_name *extractFilter(const QString& rawFilter)
86{
87 qt_mac_filter_name *ret = new qt_mac_filter_name;
88 ret->filter = rawFilter;
89 QString result = rawFilter;
90 QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
91 int index = r.indexIn(result);
92 if(index >= 0) {
93 ret->description = r.cap(1).trimmed();
94 result = r.cap(2);
95 }
96 if(ret->description.isEmpty())
97 ret->description = result;
98 ret->regxp = result.replace(QLatin1Char(' '), QLatin1Char(';'));
99 return ret;
100}
101
102// Makes a list of filters from ;;-separated text.
103static QList<qt_mac_filter_name*> makeFiltersList(const QString &filter)
104{
105#ifdef DEBUG_FILEDIALOG_FILTERS
106 qDebug("Q3FileDialog:%d - Got filter (%s)", __LINE__, filter.latin1());
107#endif
108 QString f(filter);
109 if(f.isEmpty())
110 f = Q3FileDialog::tr("All Files (*)");
111 if(f.isEmpty())
112 return QList<qt_mac_filter_name*>();
113 QString sep(QLatin1String(";;"));
114 int i = f.indexOf(sep, 0);
115 if(i == -1) {
116 sep = QLatin1String("\n");
117 if(f.indexOf(sep, 0) != -1)
118 i = f.indexOf(sep, 0);
119 }
120
121 QList<qt_mac_filter_name*> ret;
122 QStringList filts = f.split(sep);
123 for (QStringList::Iterator it = filts.begin(); it != filts.end(); ++it) {
124 qt_mac_filter_name *filter = extractFilter((*it));
125#ifdef DEBUG_FILEDIALOG_FILTERS
126 qDebug("Q3FileDialog:%d Split out filter (%d) '%s' '%s'", __LINE__, ret.count(),
127 filter->regxp.latin1(), filter->description.latin1());
128#endif
129 ret.append(filter);
130 }
131 return ret;
132}
133
134struct qt_mac_nav_filter_type {
135 int index;
136 QList<qt_mac_filter_name*> *filts;
137};
138
139static Boolean qt_mac_nav_filter(AEDesc *theItem, void *info,
140 void *myd, NavFilterModes)
141{
142 qt_mac_nav_filter_type *t = (qt_mac_nav_filter_type *)myd;
143 if(!t || !t->filts || t->index >= t->filts->count())
144 return true;
145
146 NavFileOrFolderInfo *theInfo = (NavFileOrFolderInfo *)info;
147 QString file;
148 qt_mac_filter_name *fn = t->filts->at(t->index);
149 if(!fn)
150 return true;
151 if(theItem->descriptorType == typeFSRef) {
152 FSRef ref;
153 AEGetDescData(theItem, &ref, sizeof(ref));
154 if(!str_buffer) {
155 qAddPostRoutine(cleanup_str_buffer);
156 str_buffer = (UInt8 *)malloc(1024);
157 }
158 FSRefMakePath(&ref, str_buffer, 1024);
159 file = QString::fromUtf8((const char *)str_buffer);
160 int slsh = file.lastIndexOf(QLatin1Char('/'));
161 if(slsh != -1)
162 file = file.right(file.length() - slsh - 1);
163 }
164 QStringList reg = fn->regxp.split(QLatin1String(";"));
165 for(QStringList::Iterator it = reg.begin(); it != reg.end(); ++it) {
166 QRegExp rg((*it), false, true);
167#ifdef DEBUG_FILEDIALOG_FILTERS
168 qDebug("Q3FileDialog:%d, asked to filter.. %s (%s)", __LINE__,
169 file.latin1(), (*it).latin1());
170#endif
171 if(rg.exactMatch(file))
172 return true;
173 }
174 return (theInfo->isFolder && !file.endsWith(QLatin1String(".app")));
175}
176
177//filter UPP stuff
178static NavObjectFilterUPP mac_navFilterUPP = NULL;
179static void cleanup_navFilterUPP()
180{
181 DisposeNavObjectFilterUPP(mac_navFilterUPP);
182 mac_navFilterUPP = NULL;
183}
184static const NavObjectFilterUPP make_navFilterUPP()
185{
186 if(mac_navFilterUPP)
187 return mac_navFilterUPP;
188 qAddPostRoutine(cleanup_navFilterUPP);
189 return mac_navFilterUPP = NewNavObjectFilterUPP(qt_mac_nav_filter);
190}
191//event UPP stuff
192static NavEventUPP mac_navProcUPP = NULL;
193static void cleanup_navProcUPP()
194{
195 DisposeNavEventUPP(mac_navProcUPP);
196 mac_navProcUPP = NULL;
197}
198static bool g_nav_blocking=true;
199static void qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg,
200 NavCBRecPtr p, NavCallBackUserData myd)
201{
202 switch(msg) {
203 case kNavCBPopupMenuSelect: {
204 qt_mac_nav_filter_type *t = (qt_mac_nav_filter_type *)myd;
205 NavMenuItemSpec *s = (NavMenuItemSpec*)p->eventData.eventDataParms.param;
206 t->index = s->menuType;
207#ifdef DEBUG_FILEDIALOG_FILTERS
208 qDebug("Q3FileDialog:%d - Selected a filter: %ld", __LINE__, s->menuType);
209#endif
210 break; }
211 case kNavCBStart:
212 g_nav_blocking=true;
213 break;
214 case kNavCBUserAction:
215 g_nav_blocking=false;
216 break;
217 }
218}
219static const NavEventUPP make_navProcUPP()
220{
221 if(mac_navProcUPP)
222 return mac_navProcUPP;
223 qAddPostRoutine(cleanup_navProcUPP);
224 return mac_navProcUPP = NewNavEventUPP(qt_mac_filedialog_event_proc);
225}
226
227
228extern OSErr qt_mac_create_fsref(const QString &, FSRef *); //qglobal.cpp
229
230QStringList Q3FileDialog::macGetOpenFileNames(const QString &filter, QString *pwd,
231 QWidget *parent, const char* /*name*/,
232 const QString& caption, QString *selectedFilter,
233 bool multi, bool directory)
234{
235 OSErr err;
236 QStringList retstrl;
237
238 NavDialogCreationOptions options;
239 NavGetDefaultDialogCreationOptions(&options);
240 options.modality = kWindowModalityAppModal;
241 options.optionFlags |= kNavDontConfirmReplacement | kNavSupportPackages;
242 if (!multi)
243 options.optionFlags &= ~kNavAllowMultipleFiles;
244 if(!caption.isEmpty())
245 options.windowTitle = CFStringCreateWithCharacters(NULL, (UniChar *)caption.unicode(),
246 caption.length());
247
248 static const int w = 450, h = 350;
249 options.location.h = options.location.v = -1;
250 if(parent && parent->isVisible()) {
251 Qt::WindowType wt = parent->window()->windowType();
252 if (wt != Qt::Desktop && wt != Qt::Sheet && wt != Qt::Drawer) {
253 options.modality = kWindowModalityWindowModal;
254 options.parentWindow = qt_mac_window_for(parent);
255 } else {
256 parent = parent->window();
257 QString s = parent->windowTitle();
258 options.clientName = CFStringCreateWithCharacters(NULL, (UniChar *)s.unicode(), s.length());
259 options.location.h = (parent->x() + (parent->width() / 2)) - (w / 2);
260 options.location.v = (parent->y() + (parent->height() / 2)) - (h / 2);
261
262 QRect r = QApplication::desktop()->screenGeometry(
263 QApplication::desktop()->screenNumber(parent));
264 if(options.location.h + w > r.right())
265 options.location.h -= (options.location.h + w) - r.right() + 10;
266 if(options.location.v + h > r.bottom())
267 options.location.v -= (options.location.v + h) - r.bottom() + 10;
268 }
269 } else if(QWidget *p = qApp->mainWidget()) {
270 static int last_screen = -1;
271 int scr = QApplication::desktop()->screenNumber(p);
272 if(last_screen != scr) {
273 QRect r = QApplication::desktop()->screenGeometry(scr);
274 options.location.h = (r.x() + (r.width() / 2)) - (w / 2);
275 options.location.v = (r.y() + (r.height() / 2)) - (h / 2);
276 }
277 }
278
279 QList<qt_mac_filter_name*> filts = makeFiltersList(filter);
280 qt_mac_nav_filter_type t;
281 t.index = 0;
282 t.filts = &filts;
283 if(filts.count() > 1) {
284 int i = 0;
285 CFStringRef *arr = (CFStringRef *)malloc(sizeof(CFStringRef) * filts.count());
286 for (QList<qt_mac_filter_name*>::Iterator it = filts.begin(); it != filts.end(); ++it) {
287 QString rg = (*it)->description;
288 arr[i++] = CFStringCreateWithCharacters(NULL, (UniChar *)rg.unicode(), rg.length());
289 }
290 options.popupExtension = CFArrayCreate(NULL, (const void **)arr, filts.count(), NULL);
291 }
292
293 NavDialogRef dlg;
294 if(directory) {
295 if(NavCreateChooseFolderDialog(&options, make_navProcUPP(), NULL, NULL, &dlg)) {
296 qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
297 return retstrl;
298 }
299 } else {
300 if(NavCreateGetFileDialog(&options, NULL, make_navProcUPP(), NULL,
301 make_navFilterUPP(), (void *) (filts.isEmpty() ? NULL : &t),
302 &dlg)) {
303 qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
304 return retstrl;
305 }
306 }
307 if(pwd && !pwd->isEmpty()) {
308 FSRef fsref;
309 if(qt_mac_create_fsref(*pwd, &fsref) == noErr) {
310 AEDesc desc;
311 if(AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc) == noErr)
312 NavCustomControl(dlg, kNavCtlSetLocation, (void*)&desc);
313 }
314 }
315
316 NavDialogRun(dlg);
317 if (selectedFilter) {
318 NavMenuItemSpec navSpec;
319 bzero(&navSpec, sizeof(NavMenuItemSpec));
320 qt_mac_filter_name *sel_filt_name = makeFiltersList(*selectedFilter).at(0);
321 for (int i = 0; i < filts.count(); ++i) {
322 const qt_mac_filter_name *filter = filts.at(i);
323 if (sel_filt_name->description == filter->description
324 && sel_filt_name->regxp == filter->regxp
325 && sel_filt_name->filter == filter->filter) {
326 navSpec.menuType = i;
327 break;
328 }
329 }
330 NavCustomControl(dlg, kNavCtlSelectCustomType, &navSpec);
331 }
332 if(options.modality == kWindowModalityWindowModal) { //simulate modality
333 QWidget modal_widg(parent, __FILE__ "__modal_dlg",
334 Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_DialogBorder);
335 modal_widg.createWinId();
336 QApplicationPrivate::enterModal(&modal_widg);
337 while(g_nav_blocking)
338 qApp->processEvents(QEventLoop::WaitForMoreEvents);
339 QApplicationPrivate::leaveModal(&modal_widg);
340 }
341
342 if(!(NavDialogGetUserAction(dlg) &
343 (kNavUserActionOpen | kNavUserActionChoose | kNavUserActionNewFolder))) {
344 NavDialogDispose(dlg);
345 return retstrl;
346 }
347 NavReplyRecord ret;
348 NavDialogGetReply(dlg, &ret);
349 NavDialogDispose(dlg);
350
351 long count;
352 err = AECountItems(&(ret.selection), &count);
353 if(!ret.validRecord || err != noErr || !count) {
354 NavDisposeReply(&ret);
355 return retstrl;
356 }
357
358 for(long index = 1; index <= count; index++) {
359 FSRef ref;
360 err = AEGetNthPtr(&(ret.selection), index, typeFSRef, 0, 0, &ref, sizeof(ref), 0);
361 if(err != noErr)
362 break;
363
364 if(!str_buffer) {
365 qAddPostRoutine(cleanup_str_buffer);
366 str_buffer = (UInt8 *)malloc(1024);
367 }
368 FSRefMakePath(&ref, str_buffer, 1024);
369 retstrl.append(QString::fromUtf8((const char *)str_buffer));
370 }
371 NavDisposeReply(&ret);
372 if(selectedFilter)
373 *selectedFilter = filts.at(t.index)->filter;
374 while (!filts.isEmpty())
375 delete filts.takeFirst();
376 return retstrl;
377}
378
379// Copious copy and paste from qfiledialog.cpp. Fix in 4.0.
380static QString encodeFileName(const QString &fName)
381{
382 QString newStr;
383 QByteArray cName = fName.utf8();
384 const QByteArray sChars("<>#@\"&%$:,;?={}|^~[]\'`\\*");
385
386 int len = cName.length();
387 if (!len)
388 return QString();
389 for (int i = 0; i < len ;++i) {
390 uchar inCh = (uchar)cName[i];
391 if (inCh >= 128 || sChars.contains(inCh))
392 {
393 newStr += QLatin1Char('%');
394 ushort c = inCh / 16;
395 c += c > 9 ? 'A' - 10 : '0';
396 newStr += QLatin1Char((char)c);
397 c = inCh % 16;
398 c += c > 9 ? 'A' - 10 : '0';
399 newStr += QLatin1Char((char)c);
400 } else {
401 newStr += QLatin1Char((char)inCh);
402 }
403 }
404 return newStr;
405}
406
407QString Q3FileDialog::macGetSaveFileName(const QString &start, const QString &filter,
408 QString *, QWidget *parent, const char* /*name*/,
409 const QString& caption, QString *selectedFilter)
410{
411 OSErr err;
412 QString retstr;
413 NavDialogCreationOptions options;
414 NavGetDefaultDialogCreationOptions(&options);
415 static const int w = 450, h = 350;
416 options.optionFlags |= kNavDontConfirmReplacement;
417 options.modality = kWindowModalityAppModal;
418 options.location.h = options.location.v = -1;
419 QString workingDir;
420 QString initialSelection;
421 if (!start.isEmpty()) {
422 Q3UrlOperator u(encodeFileName(start));
423 if (u.isLocalFile() && QFileInfo(u.path()).isDir()) {
424 workingDir = start;
425 } else {
426 if (u.isLocalFile()) {
427 QFileInfo fi(u.dirPath());
428 if (fi.exists()) {
429 workingDir = u.dirPath();
430 initialSelection = u.fileName();
431 }
432 } else {
433 workingDir = u.toString();
434 }
435 }
436 if (!initialSelection.isEmpty())
437 options.saveFileName = CFStringCreateWithCharacters(0,
438 (UniChar *)initialSelection.unicode(),
439 initialSelection.length());
440 }
441 if(!caption.isEmpty())
442 options.windowTitle = CFStringCreateWithCharacters(NULL, (UniChar *)caption.unicode(),
443 caption.length());
444 if(parent && parent->isVisible()) {
445 Qt::WindowType wt = parent->window()->windowType();
446 if (wt != Qt::Desktop && wt != Qt::Sheet && wt != Qt::Drawer) {
447 options.modality = kWindowModalityWindowModal;
448 options.parentWindow = qt_mac_window_for(parent);
449 } else {
450 parent = parent->window();
451 QString s = parent->windowTitle();
452 options.clientName = CFStringCreateWithCharacters(NULL, (UniChar *)s.unicode(), s.length());
453 options.location.h = (parent->x() + (parent->width() / 2)) - (w / 2);
454 options.location.v = (parent->y() + (parent->height() / 2)) - (h / 2);
455
456 QRect r = QApplication::desktop()->screenGeometry(
457 QApplication::desktop()->screenNumber(parent));
458 if(options.location.h + w > r.right())
459 options.location.h -= (options.location.h + w) - r.right() + 10;
460 if(options.location.v + h > r.bottom())
461 options.location.v -= (options.location.v + h) - r.bottom() + 10;
462 }
463 } else if(QWidget *p = qApp->mainWidget()) {
464 static int last_screen = -1;
465 int scr = QApplication::desktop()->screenNumber(p);
466 if(last_screen != scr) {
467 QRect r = QApplication::desktop()->screenGeometry(scr);
468 options.location.h = (r.x() + (r.width() / 2)) - (w / 2);
469 options.location.v = (r.y() + (r.height() / 2)) - (h / 2);
470 }
471 }
472
473 QList<qt_mac_filter_name*> filts = makeFiltersList(filter);
474 qt_mac_nav_filter_type t;
475 t.index = 0;
476 t.filts = &filts;
477 if(filts.count() > 1) {
478 int i = 0;
479 CFStringRef *arr = (CFStringRef *)malloc(sizeof(CFStringRef) * filts.count());
480 for (QList<qt_mac_filter_name*>::Iterator it = filts.begin(); it != filts.end(); ++it) {
481 QString rg = (*it)->description;
482 arr[i++] = CFStringCreateWithCharacters(NULL, (UniChar *)rg.unicode(), rg.length());
483 }
484 options.popupExtension = CFArrayCreate(NULL, (const void **)arr, filts.count(), NULL);
485 }
486
487 NavDialogRef dlg;
488 if(NavCreatePutFileDialog(&options, 'cute', kNavGenericSignature, make_navProcUPP(),
489 (void *) (filts.isEmpty() ? NULL : &t), &dlg)) {
490 qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
491 return retstr;
492 }
493 if (!workingDir.isEmpty()) {
494 FSRef fsref;
495 if (qt_mac_create_fsref(workingDir, &fsref) == noErr) {
496 AEDesc desc;
497 if (AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc) == noErr)
498 NavCustomControl(dlg, kNavCtlSetLocation, (void*)&desc);
499 }
500 }
501 NavDialogRun(dlg);
502 if (selectedFilter) {
503 NavMenuItemSpec navSpec;
504 bzero(&navSpec, sizeof(NavMenuItemSpec));
505 qt_mac_filter_name *sel_filt_name = makeFiltersList(*selectedFilter).at(0);
506 for (int i = 0; i < filts.count(); ++i) {
507 const qt_mac_filter_name *filter = filts.at(i);
508 if (sel_filt_name->description == filter->description
509 && sel_filt_name->regxp == filter->regxp
510 && sel_filt_name->filter == filter->filter) {
511 navSpec.menuType = i;
512 break;
513 }
514 }
515 NavCustomControl(dlg, kNavCtlSelectCustomType, &navSpec);
516 }
517 if(options.modality == kWindowModalityWindowModal) { //simulate modality
518 QWidget modal_widg(parent, __FILE__ "__modal_dlg",
519 Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_DialogBorder);
520 modal_widg.createWinId();
521 QApplicationPrivate::enterModal(&modal_widg);
522 while(g_nav_blocking)
523 qApp->processEvents(QEventLoop::WaitForMoreEvents);
524 QApplicationPrivate::leaveModal(&modal_widg);
525 }
526
527 if(NavDialogGetUserAction(dlg) != kNavUserActionSaveAs) {
528 NavDialogDispose(dlg);
529 return retstr;
530 }
531 NavReplyRecord ret;
532 NavDialogGetReply(dlg, &ret);
533 NavDialogDispose(dlg);
534
535 long count;
536 err = AECountItems(&(ret.selection), &count);
537 if(!ret.validRecord || err != noErr || !count) {
538 NavDisposeReply(&ret);
539 return retstr;
540 }
541
542 AEKeyword keyword;
543 DescType type;
544 Size size;
545 FSRef ref;
546 err = AEGetNthPtr(&(ret.selection), 1, typeFSRef, &keyword,
547 &type, &ref, sizeof(ref), &size);
548 if(err == noErr) {
549 if(!str_buffer) {
550 qAddPostRoutine(cleanup_str_buffer);
551 str_buffer = (UInt8 *)malloc(1024);
552 }
553 FSRefMakePath(&ref, str_buffer, 1024);
554 retstr = QString::fromUtf8((const char *)str_buffer);
555 //now filename
556 CFStringGetCString(ret.saveFileName, (char *)str_buffer, 1024, kCFStringEncodingUTF8);
557 retstr += QLatin1String("/") + QString::fromUtf8((const char *)str_buffer);
558 }
559 NavDisposeReply(&ret);
560 if(selectedFilter)
561 *selectedFilter = filts.at(t.index)->filter;
562 while (!filts.isEmpty())
563 delete filts.takeFirst();
564 return retstr;
565}
566
567QT_END_NAMESPACE
568
569#endif
Note: See TracBrowser for help on using the repository browser.