source: trunk/src/gui/itemviews/qfileiconprovider.cpp@ 642

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

gui: Use native file icons in standard Qt file dialogs.

File size: 17.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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#include "qfileiconprovider.h"
43
44#ifndef QT_NO_FILEICONPROVIDER
45#include <qstyle.h>
46#include <qapplication.h>
47#include <qdir.h>
48#include <qpixmapcache.h>
49#if defined(Q_WS_WIN)
50# define _WIN32_IE 0x0500
51# include <qt_windows.h>
52# include <commctrl.h>
53# include <objbase.h>
54#elif defined(Q_WS_PM)
55# define INCL_DOSDEVIOCTL
56# include <qt_os2.h>
57#elif defined(Q_WS_MAC)
58# include <private/qt_cocoa_helpers_mac_p.h>
59#endif
60
61#include <private/qfunctions_p.h>
62#include <private/qguiplatformplugin_p.h>
63
64#if defined(Q_WS_X11) && !defined(Q_NO_STYLE_GTK)
65# include <private/qgtkstyle_p.h>
66# include <private/qt_x11_p.h>
67#endif
68
69#ifndef SHGFI_ADDOVERLAYS
70# define SHGFI_ADDOVERLAYS 0x000000020
71#endif
72
73QT_BEGIN_NAMESPACE
74
75/*!
76 \class QFileIconProvider
77
78 \brief The QFileIconProvider class provides file icons for the QDirModel class.
79*/
80
81/*!
82 \enum QFileIconProvider::IconType
83 \value Computer
84 \value Desktop
85 \value Trashcan
86 \value Network
87 \value Drive
88 \value Folder
89 \value File
90*/
91
92class QFileIconProviderPrivate
93{
94 Q_DECLARE_PUBLIC(QFileIconProvider)
95
96public:
97 QFileIconProviderPrivate();
98 QIcon getIcon(QStyle::StandardPixmap name) const;
99#ifdef Q_WS_WIN
100 QIcon getWinIcon(const QFileInfo &fi) const;
101#elif defined(Q_WS_PM)
102 QIcon getPmIcon(const QFileInfo &fi) const;
103#elif defined(Q_WS_MAC)
104 QIcon getMacIcon(const QFileInfo &fi) const;
105#endif
106 QFileIconProvider *q_ptr;
107 QString homePath;
108
109private:
110 QIcon file;
111 QIcon fileLink;
112 QIcon directory;
113 QIcon directoryLink;
114 QIcon harddisk;
115 QIcon floppy;
116 QIcon cdrom;
117 QIcon ram;
118 QIcon network;
119 QIcon computer;
120 QIcon desktop;
121 QIcon trashcan;
122 QIcon generic;
123 QIcon home;
124};
125
126QFileIconProviderPrivate::QFileIconProviderPrivate()
127{
128 QStyle *style = QApplication::style();
129 file = style->standardIcon(QStyle::SP_FileIcon);
130 directory = style->standardIcon(QStyle::SP_DirIcon);
131 fileLink = style->standardIcon(QStyle::SP_FileLinkIcon);
132 directoryLink = style->standardIcon(QStyle::SP_DirLinkIcon);
133 harddisk = style->standardIcon(QStyle::SP_DriveHDIcon);
134 floppy = style->standardIcon(QStyle::SP_DriveFDIcon);
135 cdrom = style->standardIcon(QStyle::SP_DriveCDIcon);
136 network = style->standardIcon(QStyle::SP_DriveNetIcon);
137 computer = style->standardIcon(QStyle::SP_ComputerIcon);
138 desktop = style->standardIcon(QStyle::SP_DesktopIcon);
139 trashcan = style->standardIcon(QStyle::SP_TrashIcon);
140 home = style->standardIcon(QStyle::SP_DirHomeIcon);
141 homePath = QDir::home().absolutePath();
142}
143
144QIcon QFileIconProviderPrivate::getIcon(QStyle::StandardPixmap name) const
145{
146 switch (name) {
147 case QStyle::SP_FileIcon:
148 return file;
149 case QStyle::SP_FileLinkIcon:
150 return fileLink;
151 case QStyle::SP_DirIcon:
152 return directory;
153 case QStyle::SP_DirLinkIcon:
154 return directoryLink;
155 case QStyle::SP_DriveHDIcon:
156 return harddisk;
157 case QStyle::SP_DriveFDIcon:
158 return floppy;
159 case QStyle::SP_DriveCDIcon:
160 return cdrom;
161 case QStyle::SP_DriveNetIcon:
162 return network;
163 case QStyle::SP_ComputerIcon:
164 return computer;
165 case QStyle::SP_DesktopIcon:
166 return desktop;
167 case QStyle::SP_TrashIcon:
168 return trashcan;
169 case QStyle::SP_DirHomeIcon:
170 return home;
171 default:
172 return QIcon();
173 }
174 return QIcon();
175}
176
177/*!
178 Constructs a file icon provider.
179*/
180
181QFileIconProvider::QFileIconProvider()
182 : d_ptr(new QFileIconProviderPrivate)
183{
184}
185
186/*!
187 Destroys the file icon provider.
188
189*/
190
191QFileIconProvider::~QFileIconProvider()
192{
193}
194
195/*!
196 Returns an icon set for the given \a type.
197*/
198
199QIcon QFileIconProvider::icon(IconType type) const
200{
201 Q_D(const QFileIconProvider);
202 switch (type) {
203 case Computer:
204 return d->getIcon(QStyle::SP_ComputerIcon);
205 case Desktop:
206 return d->getIcon(QStyle::SP_DesktopIcon);
207 case Trashcan:
208 return d->getIcon(QStyle::SP_TrashIcon);
209 case Network:
210 return d->getIcon(QStyle::SP_DriveNetIcon);
211 case Drive:
212 return d->getIcon(QStyle::SP_DriveHDIcon);
213 case Folder:
214 return d->getIcon(QStyle::SP_DirIcon);
215 case File:
216 return d->getIcon(QStyle::SP_FileIcon);
217 default:
218 break;
219 };
220 return QIcon();
221}
222
223#ifdef Q_WS_WIN
224QIcon QFileIconProviderPrivate::getWinIcon(const QFileInfo &fileInfo) const
225{
226 QIcon retIcon;
227 const QString fileExtension = QLatin1Char('.') + fileInfo.suffix().toUpper();
228
229 QString key;
230 if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink())
231 key = QLatin1String("qt_") + fileExtension;
232
233 QPixmap pixmap;
234 if (!key.isEmpty()) {
235 QPixmapCache::find(key, pixmap);
236 }
237
238 if (!pixmap.isNull()) {
239 retIcon.addPixmap(pixmap);
240 if (QPixmapCache::find(key + QLatin1Char('l'), pixmap))
241 retIcon.addPixmap(pixmap);
242 return retIcon;
243 }
244
245 /* We don't use the variable, but by storing it statically, we
246 * ensure CoInitialize is only called once. */
247 static HRESULT comInit = CoInitialize(NULL);
248 Q_UNUSED(comInit);
249
250 SHFILEINFO info;
251 unsigned long val = 0;
252
253 //Get the small icon
254#ifndef Q_OS_WINCE
255 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
256 sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS);
257#else
258 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
259 sizeof(SHFILEINFO), SHGFI_SMALLICON|SHGFI_SYSICONINDEX);
260#endif
261 if (val) {
262 if (fileInfo.isDir() && !fileInfo.isRoot()) {
263 //using the unique icon index provided by windows save us from duplicate keys
264 key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
265 QPixmapCache::find(key, pixmap);
266 if (!pixmap.isNull()) {
267 retIcon.addPixmap(pixmap);
268 if (QPixmapCache::find(key + QLatin1Char('l'), pixmap))
269 retIcon.addPixmap(pixmap);
270 DestroyIcon(info.hIcon);
271 return retIcon;
272 }
273 }
274 if (pixmap.isNull()) {
275#ifndef Q_OS_WINCE
276 pixmap = QPixmap::fromWinHICON(info.hIcon);
277#else
278 pixmap = QPixmap::fromWinHICON(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
279#endif
280 if (!pixmap.isNull()) {
281 retIcon.addPixmap(pixmap);
282 if (!key.isEmpty())
283 QPixmapCache::insert(key, pixmap);
284 }
285 else {
286 qWarning("QFileIconProviderPrivate::getWinIcon() no small icon found");
287 }
288 }
289 DestroyIcon(info.hIcon);
290 }
291
292 //Get the big icon
293#ifndef Q_OS_WINCE
294 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
295 sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_LARGEICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS);
296#else
297 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
298 sizeof(SHFILEINFO), SHGFI_LARGEICON|SHGFI_SYSICONINDEX);
299#endif
300 if (val) {
301 if (fileInfo.isDir() && !fileInfo.isRoot()) {
302 //using the unique icon index provided by windows save us from duplicate keys
303 key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
304 }
305#ifndef Q_OS_WINCE
306 pixmap = QPixmap::fromWinHICON(info.hIcon);
307#else
308 pixmap = QPixmap::fromWinHICON(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
309#endif
310 if (!pixmap.isNull()) {
311 retIcon.addPixmap(pixmap);
312 if (!key.isEmpty())
313 QPixmapCache::insert(key + QLatin1Char('l'), pixmap);
314 }
315 else {
316 qWarning("QFileIconProviderPrivate::getWinIcon() no large icon found");
317 }
318 DestroyIcon(info.hIcon);
319 }
320 return retIcon;
321}
322
323#elif defined(Q_WS_PM)
324QIcon QFileIconProviderPrivate::getPmIcon(const QFileInfo &fileInfo) const
325{
326 QIcon retIcon;
327
328 if (fileInfo.isRoot()) {
329 // Unfortunately, WinLoadFileIcon() returns a regular folder icon for
330 // paths like "C:\" (and nothing for "C:") instead of a drive icon.
331 // Getting the latter involves calling WPS object methods so we leave it
332 // out for now and let the stock Qt drive-specific icons be used instead.
333 return retIcon;
334 }
335
336 QByteArray path = QDir::toNativeSeparators(
337 QDir::cleanPath(fileInfo.absoluteFilePath())).toLocal8Bit();
338 HPOINTER hicon = WinLoadFileIcon(path, FALSE);
339 if (hicon != NULLHANDLE) {
340 // we're requesting the system (shared) icon handle which should be
341 // always the same for the given file until the icon is changed, so use
342 // the bitmap handles as a key in the pixmap cache
343 QString key = QString(QLatin1String("qt_hicon_%1")).arg(hicon);
344 QString keyMini = key + QLatin1String("_m");
345 QPixmap pixmap;
346 QPixmapCache::find(key, pixmap);
347 if (!pixmap.isNull()) {
348 retIcon.addPixmap(pixmap);
349 QPixmapCache::find(keyMini, pixmap);
350 if (!pixmap.isNull())
351 retIcon.addPixmap(pixmap);
352 } else {
353 QPixmap mini;
354 retIcon = QPixmap::fromPmHPOINTER(hicon, &pixmap, &mini);
355 if (!retIcon.isNull()) {
356 // store pixmaps in the cache
357 Q_ASSERT(!pixmap.isNull() || !mini.isNull());
358 if (!pixmap.isNull())
359 QPixmapCache::insert(key, pixmap);
360 if (!mini.isNull())
361 QPixmapCache::insert(keyMini, mini);
362 }
363 }
364 }
365
366 return retIcon;
367}
368
369#elif defined(Q_WS_MAC)
370QIcon QFileIconProviderPrivate::getMacIcon(const QFileInfo &fi) const
371{
372 QIcon retIcon;
373 QString fileExtension = fi.suffix().toUpper();
374 fileExtension.prepend(QLatin1String("."));
375
376 const QString keyBase = QLatin1String("qt_") + fileExtension;
377
378 QPixmap pixmap;
379 if (fi.isFile() && !fi.isExecutable() && !fi.isSymLink()) {
380 QPixmapCache::find(keyBase + QLatin1String("16"), pixmap);
381 }
382
383 if (!pixmap.isNull()) {
384 retIcon.addPixmap(pixmap);
385 if (QPixmapCache::find(keyBase + QLatin1String("32"), pixmap)) {
386 retIcon.addPixmap(pixmap);
387 if (QPixmapCache::find(keyBase + QLatin1String("64"), pixmap)) {
388 retIcon.addPixmap(pixmap);
389 if (QPixmapCache::find(keyBase + QLatin1String("128"), pixmap)) {
390 retIcon.addPixmap(pixmap);
391 return retIcon;
392 }
393 }
394 }
395 }
396
397
398 FSRef macRef;
399 OSStatus status = FSPathMakeRef(reinterpret_cast<const UInt8*>(fi.canonicalFilePath().toUtf8().constData()),
400 &macRef, 0);
401 if (status != noErr)
402 return retIcon;
403 FSCatalogInfo info;
404 HFSUniStr255 macName;
405 status = FSGetCatalogInfo(&macRef, kIconServicesCatalogInfoMask, &info, &macName, 0, 0);
406 if (status != noErr)
407 return retIcon;
408 IconRef iconRef;
409 SInt16 iconLabel;
410 status = GetIconRefFromFileInfo(&macRef, macName.length, macName.unicode,
411 kIconServicesCatalogInfoMask, &info, kIconServicesNormalUsageFlag,
412 &iconRef, &iconLabel);
413 if (status != noErr)
414 return retIcon;
415 qt_mac_constructQIconFromIconRef(iconRef, 0, &retIcon);
416 ReleaseIconRef(iconRef);
417
418 pixmap = retIcon.pixmap(16);
419 QPixmapCache::insert(keyBase + QLatin1String("16"), pixmap);
420 pixmap = retIcon.pixmap(32);
421 QPixmapCache::insert(keyBase + QLatin1String("32"), pixmap);
422 pixmap = retIcon.pixmap(64);
423 QPixmapCache::insert(keyBase + QLatin1String("64"), pixmap);
424 pixmap = retIcon.pixmap(128);
425 QPixmapCache::insert(keyBase + QLatin1String("128"), pixmap);
426
427 return retIcon;
428}
429#endif
430
431
432/*!
433 Returns an icon for the file described by \a info.
434*/
435
436QIcon QFileIconProvider::icon(const QFileInfo &info) const
437{
438 Q_D(const QFileIconProvider);
439
440 QIcon platformIcon = qt_guiPlatformPlugin()->fileSystemIcon(info);
441 if (!platformIcon.isNull())
442 return platformIcon;
443
444#if defined(Q_WS_X11) && !defined(QT_NO_STYLE_GTK)
445 if (X11->desktopEnvironment == DE_GNOME) {
446 QIcon gtkIcon = QGtkStylePrivate::getFilesystemIcon(info);
447 if (!gtkIcon.isNull())
448 return gtkIcon;
449 }
450#endif
451
452#ifdef Q_WS_MAC
453 QIcon retIcon = d->getMacIcon(info);
454 if (!retIcon.isNull())
455 return retIcon;
456#elif defined Q_WS_WIN
457 QIcon icon = d->getWinIcon(info);
458 if (!icon.isNull())
459 return icon;
460#elif defined Q_WS_PM
461 if (QApplication::desktopSettingsAware()) {
462 QIcon icon= d->getPmIcon(info);
463 if (!icon.isNull())
464 return icon;
465 }
466#endif
467 if (info.isRoot())
468#if defined (Q_WS_WIN) && !defined(Q_WS_WINCE)
469 {
470 UINT type = GetDriveType((wchar_t *)info.absoluteFilePath().utf16());
471
472 switch (type) {
473 case DRIVE_REMOVABLE:
474 return d->getIcon(QStyle::SP_DriveFDIcon);
475 case DRIVE_FIXED:
476 return d->getIcon(QStyle::SP_DriveHDIcon);
477 case DRIVE_REMOTE:
478 return d->getIcon(QStyle::SP_DriveNetIcon);
479 case DRIVE_CDROM:
480 return d->getIcon(QStyle::SP_DriveCDIcon);
481 case DRIVE_RAMDISK:
482 case DRIVE_UNKNOWN:
483 case DRIVE_NO_ROOT_DIR:
484 default:
485 return d->getIcon(QStyle::SP_DriveHDIcon);
486 }
487 }
488#elif defined(Q_WS_PM)
489 {
490 UCHAR ioc_parm[2];
491 BIOSPARAMETERBLOCK bpb;
492 ioc_parm[0] = 0;
493 ioc_parm[1] = info.absoluteFilePath().at(0).toUpper().cell() - 'A';
494 APIRET arc = DosDevIOCtl((HFILE) - 1, IOCTL_DISK, DSK_GETDEVICEPARAMS,
495 ioc_parm, sizeof(ioc_parm), NULL,
496 &bpb, sizeof(bpb), NULL);
497
498 if (arc == ERROR_NOT_SUPPORTED)
499 return d->getIcon(QStyle::SP_DriveNetIcon);
500
501 if (arc == NO_ERROR && bpb.bDeviceType != DEVTYPE_FIXED) {
502 if (bpb.fsDeviceAttr & 0x10) // floppy format
503 return d->getIcon(QStyle::SP_DriveFDIcon);
504 if (!(bpb.fsDeviceAttr & 0x08)) // partitionable removable
505 return d->getIcon(QStyle::SP_DriveCDIcon);
506 }
507
508 return d->getIcon(QStyle::SP_DriveHDIcon);
509 }
510#else
511 return d->getIcon(QStyle::SP_DriveHDIcon);
512#endif
513 if (info.isFile()) {
514 if (info.isSymLink())
515 return d->getIcon(QStyle::SP_FileLinkIcon);
516 else
517 return d->getIcon(QStyle::SP_FileIcon);
518 }
519 if (info.isDir()) {
520 if (info.isSymLink()) {
521 return d->getIcon(QStyle::SP_DirLinkIcon);
522 } else {
523 if (info.absoluteFilePath() == d->homePath) {
524 return d->getIcon(QStyle::SP_DirHomeIcon);
525 } else {
526 return d->getIcon(QStyle::SP_DirIcon);
527 }
528 }
529 }
530 return QIcon();
531}
532
533/*!
534 Returns the type of the file described by \a info.
535*/