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

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

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

File size: 20.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 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 qt-info@nokia.com.
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# define SHGFI_OVERLAYINDEX 0x000000040
72#endif
73
74QT_BEGIN_NAMESPACE
75
76/*!
77 \class QFileIconProvider
78
79 \brief The QFileIconProvider class provides file icons for the QDirModel and the QFileSystemModel classes.
80*/
81
82/*!
83 \enum QFileIconProvider::IconType
84 \value Computer
85 \value Desktop
86 \value Trashcan
87 \value Network
88 \value Drive
89 \value Folder
90 \value File
91*/
92
93class QFileIconProviderPrivate
94{
95 Q_DECLARE_PUBLIC(QFileIconProvider)
96
97public:
98 QFileIconProviderPrivate();
99 QIcon getIcon(QStyle::StandardPixmap name) const;
100#ifdef Q_WS_WIN
101 QIcon getWinIcon(const QFileInfo &fi) const;
102#elif defined(Q_WS_PM)
103 QIcon getPmIcon(const QFileInfo &fi) const;
104#elif defined(Q_WS_MAC)
105 QIcon getMacIcon(const QFileInfo &fi) const;
106#endif
107 QFileIconProvider *q_ptr;
108 const QString homePath;
109
110private:
111 mutable QIcon file;
112 mutable QIcon fileLink;
113 mutable QIcon directory;
114 mutable QIcon directoryLink;
115 mutable QIcon harddisk;
116 mutable QIcon floppy;
117 mutable QIcon cdrom;
118 mutable QIcon ram;
119 mutable QIcon network;
120 mutable QIcon computer;
121 mutable QIcon desktop;
122 mutable QIcon trashcan;
123 mutable QIcon generic;
124 mutable QIcon home;
125};
126
127QFileIconProviderPrivate::QFileIconProviderPrivate() :
128 homePath(QDir::home().absolutePath())
129{
130}
131
132QIcon QFileIconProviderPrivate::getIcon(QStyle::StandardPixmap name) const
133{
134 switch (name) {
135 case QStyle::SP_FileIcon:
136 if (file.isNull())
137 file = QApplication::style()->standardIcon(name);
138 return file;
139 case QStyle::SP_FileLinkIcon:
140 if (fileLink.isNull())
141 fileLink = QApplication::style()->standardIcon(name);
142 return fileLink;
143 case QStyle::SP_DirIcon:
144 if (directory.isNull())
145 directory = QApplication::style()->standardIcon(name);
146 return directory;
147 case QStyle::SP_DirLinkIcon:
148 if (directoryLink.isNull())
149 directoryLink = QApplication::style()->standardIcon(name);
150 return directoryLink;
151 case QStyle::SP_DriveHDIcon:
152 if (harddisk.isNull())
153 harddisk = QApplication::style()->standardIcon(name);
154 return harddisk;
155 case QStyle::SP_DriveFDIcon:
156 if (floppy.isNull())
157 floppy = QApplication::style()->standardIcon(name);
158 return floppy;
159 case QStyle::SP_DriveCDIcon:
160 if (cdrom.isNull())
161 cdrom = QApplication::style()->standardIcon(name);
162 return cdrom;
163 case QStyle::SP_DriveNetIcon:
164 if (network.isNull())
165 network = QApplication::style()->standardIcon(name);
166 return network;
167 case QStyle::SP_ComputerIcon:
168 if (computer.isNull())
169 computer = QApplication::style()->standardIcon(name);
170 return computer;
171 case QStyle::SP_DesktopIcon:
172 if (desktop.isNull())
173 desktop = QApplication::style()->standardIcon(name);
174 return desktop;
175 case QStyle::SP_TrashIcon:
176 if (trashcan.isNull())
177 trashcan = QApplication::style()->standardIcon(name);
178 return trashcan;
179 case QStyle::SP_DirHomeIcon:
180 if (home.isNull())
181 home = QApplication::style()->standardIcon(name);
182 return home;
183 default:
184 return QIcon();
185 }
186 return QIcon();
187}
188
189/*!
190 Constructs a file icon provider.
191*/
192
193QFileIconProvider::QFileIconProvider()
194 : d_ptr(new QFileIconProviderPrivate)
195{
196}
197
198/*!
199 Destroys the file icon provider.
200
201*/
202
203QFileIconProvider::~QFileIconProvider()
204{
205}
206
207/*!
208 Returns an icon set for the given \a type.
209*/
210
211QIcon QFileIconProvider::icon(IconType type) const
212{
213 Q_D(const QFileIconProvider);
214 switch (type) {
215 case Computer:
216 return d->getIcon(QStyle::SP_ComputerIcon);
217 case Desktop:
218 return d->getIcon(QStyle::SP_DesktopIcon);
219 case Trashcan:
220 return d->getIcon(QStyle::SP_TrashIcon);
221 case Network:
222 return d->getIcon(QStyle::SP_DriveNetIcon);
223 case Drive:
224 return d->getIcon(QStyle::SP_DriveHDIcon);
225 case Folder:
226 return d->getIcon(QStyle::SP_DirIcon);
227 case File:
228 return d->getIcon(QStyle::SP_FileIcon);
229 default:
230 break;
231 };
232 return QIcon();
233}
234
235#ifdef Q_WS_WIN
236QIcon QFileIconProviderPrivate::getWinIcon(const QFileInfo &fileInfo) const
237{
238 QIcon retIcon;
239 const QString fileExtension = QLatin1Char('.') + fileInfo.suffix().toUpper();
240
241 QString key;
242 if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink())
243 key = QLatin1String("qt_") + fileExtension;
244
245 QPixmap pixmap;
246 if (!key.isEmpty()) {
247 QPixmapCache::find(key, pixmap);
248 }
249
250 if (!pixmap.isNull()) {
251 retIcon.addPixmap(pixmap);
252 if (QPixmapCache::find(key + QLatin1Char('l'), pixmap))
253 retIcon.addPixmap(pixmap);
254 return retIcon;
255 }
256
257 /* We don't use the variable, but by storing it statically, we
258 * ensure CoInitialize is only called once. */
259 static HRESULT comInit = CoInitialize(NULL);
260 Q_UNUSED(comInit);
261
262 SHFILEINFO info;
263 unsigned long val = 0;
264
265 //Get the small icon
266#ifndef Q_OS_WINCE
267 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
268 sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX);
269#else
270 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
271 sizeof(SHFILEINFO), SHGFI_SMALLICON|SHGFI_SYSICONINDEX);
272#endif
273
274 // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
275 if (val && info.hIcon) {
276 if (fileInfo.isDir() && !fileInfo.isRoot()) {
277 //using the unique icon index provided by windows save us from duplicate keys
278 key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
279 QPixmapCache::find(key, pixmap);
280 if (!pixmap.isNull()) {
281 retIcon.addPixmap(pixmap);
282 if (QPixmapCache::find(key + QLatin1Char('l'), pixmap))
283 retIcon.addPixmap(pixmap);
284 DestroyIcon(info.hIcon);
285 return retIcon;
286 }
287 }
288 if (pixmap.isNull()) {
289#ifndef Q_OS_WINCE
290 pixmap = QPixmap::fromWinHICON(info.hIcon);
291#else
292 pixmap = QPixmap::fromWinHICON(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
293#endif
294 if (!pixmap.isNull()) {
295 retIcon.addPixmap(pixmap);
296 if (!key.isEmpty())
297 QPixmapCache::insert(key, pixmap);
298 }
299 else {
300 qWarning("QFileIconProviderPrivate::getWinIcon() no small icon found");
301 }
302 }
303 DestroyIcon(info.hIcon);
304 }
305
306 //Get the big icon
307#ifndef Q_OS_WINCE
308 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
309 sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_LARGEICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX);
310#else
311 val = SHGetFileInfo((const wchar_t *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
312 sizeof(SHFILEINFO), SHGFI_LARGEICON|SHGFI_SYSICONINDEX);
313#endif
314 if (val && info.hIcon) {
315 if (fileInfo.isDir() && !fileInfo.isRoot()) {
316 //using the unique icon index provided by windows save us from duplicate keys
317 key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
318 }
319#ifndef Q_OS_WINCE
320 pixmap = QPixmap::fromWinHICON(info.hIcon);
321#else
322 pixmap = QPixmap::fromWinHICON(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
323#endif
324 if (!pixmap.isNull()) {
325 retIcon.addPixmap(pixmap);
326 if (!key.isEmpty())
327 QPixmapCache::insert(key + QLatin1Char('l'), pixmap);
328 }
329 else {
330 qWarning("QFileIconProviderPrivate::getWinIcon() no large icon found");
331 }
332 DestroyIcon(info.hIcon);
333 }
334 return retIcon;
335}
336
337#elif defined(Q_WS_PM)
338QIcon QFileIconProviderPrivate::getPmIcon(const QFileInfo &fileInfo) const
339{
340 QIcon retIcon;
341
342 if (fileInfo.isRoot()) {
343 // Unfortunately, WinLoadFileIcon() returns a regular folder icon for
344 // paths like "C:\" (and nothing for "C:") instead of a drive icon.
345 // Getting the latter involves calling WPS object methods so we leave it
346 // out for now and let the stock Qt drive-specific icons be used instead.
347 return retIcon;
348 }
349
350 QByteArray path = QDir::toNativeSeparators(
351 QDir::cleanPath(fileInfo.absoluteFilePath())).toLocal8Bit();
352 HPOINTER hicon = WinLoadFileIcon(path, FALSE);
353 if (hicon == NULLHANDLE) {
354 // WinLoadFileIcon() fails on non-existing paths but we still want a
355 // system icon for it to be "in tune" with other icons. One known case
356 // is a directory on an NDFS SMB multi-resource share that links to a
357 // remote CD-ROM drive with no disk in it. This actually represents a
358 // more common problem when an icon for a non-existent path is
359 // requested. We will solve it by providing an icon of a known-to-exist
360 // path element instead. In order to differentiate between a
361 // non-existent folder and file we use a return code from
362 // DosQueryPathInfo() which appears to be ERROR_PATH_NOT_FOUND for the
363 // SMB multi-resource case and ERROR_FILE_NOT_FOUND otherwise. Note that
364 // a calling app may actually already know if it's a file or a directory
365 // from the listing of a parent directory (or we could do such a listing
366 // here) but it looks like too much hassle to use this approach here.
367 FILESTATUS3 info;
368 APIRET arc = DosQueryPathInfo(path, FIL_STANDARD, &info, sizeof(info));
369 if (arc == ERROR_PATH_NOT_FOUND) {
370 // a known-to-exist directory
371 hicon = WinLoadFileIcon("\\", FALSE);
372 } else {
373 // a known-to-exist file
374 static char kernelFilePath[] = "?:\\OS2KRNL";
375 if (kernelFilePath[0] == '?') {
376 ULONG bootDrive = 0;
377 DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
378 (PVOID)&bootDrive, sizeof(bootDrive));
379 kernelFilePath[0] = bootDrive + 'A' - 1;
380 }
381 hicon = WinLoadFileIcon(kernelFilePath, FALSE);
382 }
383 }
384 if (hicon != NULLHANDLE) {
385 // we're requesting the system (shared) icon handle which should be
386 // always the same for the given file until the icon is changed, so use
387 // the bitmap handles as a key in the pixmap cache
388 QString key = QString(QLatin1String("qt_hicon_%1")).arg(hicon);
389 QString keyMini = key + QLatin1String("_m");
390 QPixmap pixmap;
391 QPixmapCache::find(key, pixmap);
392 if (!pixmap.isNull()) {
393 retIcon.addPixmap(pixmap);
394 QPixmapCache::find(keyMini, pixmap);
395 if (!pixmap.isNull())
396 retIcon.addPixmap(pixmap);
397 } else {
398 QPixmap mini;
399 retIcon = QPixmap::fromPmHPOINTER(hicon, &pixmap, &mini);
400 if (!retIcon.isNull()) {
401 // store pixmaps in the cache
402 Q_ASSERT(!pixmap.isNull() || !mini.isNull());
403 if (!pixmap.isNull())
404 QPixmapCache::insert(key, pixmap);
405 if (!mini.isNull())
406 QPixmapCache::insert(keyMini, mini);
407 }
408 }
409 }
410
411 return retIcon;
412}
413
414#elif defined(Q_WS_MAC)
415QIcon QFileIconProviderPrivate::getMacIcon(const QFileInfo &fi) const
416{
417 QIcon retIcon;
418 QString fileExtension = fi.suffix().toUpper();
419 fileExtension.prepend(QLatin1String("."));
420
421 const QString keyBase = QLatin1String("qt_") + fileExtension;
422
423 QPixmap pixmap;
424 if (fi.isFile() && !fi.isExecutable() && !fi.isSymLink()) {
425 QPixmapCache::find(keyBase + QLatin1String("16"), pixmap);
426 }
427
428 if (!pixmap.isNull()) {
429 retIcon.addPixmap(pixmap);
430 if (QPixmapCache::find(keyBase + QLatin1String("32"), pixmap)) {
431 retIcon.addPixmap(pixmap);
432 if (QPixmapCache::find(keyBase + QLatin1String("64"), pixmap)) {
433 retIcon.addPixmap(pixmap);
434 if (QPixmapCache::find(keyBase + QLatin1String("128"), pixmap)) {
435 retIcon.addPixmap(pixmap);
436 return retIcon;
437 }
438 }
439 }
440 }
441
442
443 FSRef macRef;
444 OSStatus status = FSPathMakeRef(reinterpret_cast<const UInt8*>(fi.canonicalFilePath().toUtf8().constData()),
445 &macRef, 0);
446 if (status != noErr)
447 return retIcon;
448 FSCatalogInfo info;
449 HFSUniStr255 macName;
450 status = FSGetCatalogInfo(&macRef, kIconServicesCatalogInfoMask, &info, &macName, 0, 0);
451 if (status != noErr)
452 return retIcon;
453 IconRef iconRef;
454 SInt16 iconLabel;
455 status = GetIconRefFromFileInfo(&macRef, macName.length, macName.unicode,
456 kIconServicesCatalogInfoMask, &info, kIconServicesNormalUsageFlag,
457 &iconRef, &iconLabel);
458 if (status != noErr)
459 return retIcon;
460 qt_mac_constructQIconFromIconRef(iconRef, 0, &retIcon);
461 ReleaseIconRef(iconRef);
462
463 pixmap = retIcon.pixmap(16);
464 QPixmapCache::insert(keyBase + QLatin1String("16"), pixmap);
465 pixmap = retIcon.pixmap(32);
466 QPixmapCache::insert(keyBase + QLatin1String("32"), pixmap);
467 pixmap = retIcon.pixmap(64);
468 QPixmapCache::insert(keyBase + QLatin1String("64"), pixmap);
469 pixmap = retIcon.pixmap(128);
470 QPixmapCache::insert(keyBase + QLatin1String("128"), pixmap);
471
472 return retIcon;
473}
474#endif
475
476
477/*!
478 Returns an icon for the file described by \a info.
479*/
480
481QIcon QFileIconProvider::icon(const QFileInfo &info) const
482{
483 Q_D(const QFileIconProvider);
484
485 QIcon platformIcon = qt_guiPlatformPlugin()->fileSystemIcon(info);
486 if (!platformIcon.isNull())
487 return platformIcon;
488
489#if defined(Q_WS_X11) && !defined(QT_NO_STYLE_GTK)
490 if (X11->desktopEnvironment == DE_GNOME) {
491 QIcon gtkIcon = QGtkStylePrivate::getFilesystemIcon(info);
492 if (!gtkIcon.isNull())
493 return gtkIcon;
494 }
495#endif
496
497#ifdef Q_WS_MAC
498 QIcon retIcon = d->getMacIcon(info);
499 if (!retIcon.isNull())
500 return retIcon;
501#elif defined Q_WS_WIN
502 QIcon icon = d->getWinIcon(info);
503 if (!icon.isNull())
504 return icon;
505#elif defined Q_WS_PM
506 if (QApplication::desktopSettingsAware()) {
507 QIcon icon= d->getPmIcon(info);
508 if (!icon.isNull())
509 return icon;
510 }
511#endif
512 if (info.isRoot())
513#if defined (Q_WS_WIN) && !defined(Q_WS_WINCE)
514 {
515 UINT type = GetDriveType((wchar_t *)info.absoluteFilePath().utf16());
516
517 switch (type) {
518 case DRIVE_REMOVABLE:
519 return d->getIcon(QStyle::SP_DriveFDIcon);
520 case DRIVE_FIXED:
521 return d->getIcon(QStyle::SP_DriveHDIcon);
522 case DRIVE_REMOTE:
523 return d->getIcon(QStyle::SP_DriveNetIcon);
524 case DRIVE_CDROM:
525 return d->getIcon(QStyle::SP_DriveCDIcon);
526 case DRIVE_RAMDISK:
527 case DRIVE_UNKNOWN:
528 case DRIVE_NO_ROOT_DIR:
529 default:
530 return d->getIcon(QStyle::SP_DriveHDIcon);
531 }
532 }
533#elif defined(Q_WS_PM)
534 {
535 UCHAR ioc_parm[2];
536 BIOSPARAMETERBLOCK bpb;
537 ioc_parm[0] = 0;
538 ioc_parm[1] = info.absoluteFilePath().at(0).toUpper().cell() - 'A';
539 APIRET arc = DosDevIOCtl((HFILE) - 1, IOCTL_DISK, DSK_GETDEVICEPARAMS,
540 ioc_parm, sizeof(ioc_parm), NULL,
541 &bpb, sizeof(bpb), NULL);
542
543 if (arc == ERROR_NOT_SUPPORTED)
544 return d->getIcon(QStyle::SP_DriveNetIcon);
545
546 if (arc == NO_ERROR && bpb.bDeviceType != DEVTYPE_FIXED) {
547 if (bpb.fsDeviceAttr & 0x10) // floppy format
548 return d->getIcon(QStyle::SP_DriveFDIcon);
549 if (!(bpb.fsDeviceAttr & 0x08)) // partitionable removable
550 return d->getIcon(QStyle::SP_DriveCDIcon);
551 }
552
553 return d->getIcon(QStyle::SP_DriveHDIcon);
554 }
555#else
556 return d->getIcon(QStyle::SP_DriveHDIcon);
557#endif
558 if (info.isFile()) {
559 if (info.isSymLink())
560 return d->getIcon(QStyle::SP_FileLinkIcon);
561 else
562 return d->getIcon(QStyle::SP_FileIcon);
563 }
564 if (info.isDir()) {
565 if (info.isSymLink()) {
566 return d->getIcon(QStyle::SP_DirLinkIcon);
567 } else {
568 if (info.absoluteFilePath() == d->homePath) {
569 return d->getIcon(QStyle::SP_DirHomeIcon);
570 } else {
571 return d->getIcon(QStyle::SP_DirIcon);
572 }
573 }
574 }
575 return QIcon();
576}
577
578/*!
579 Returns the type of the file described by \a info.
580*/
581
582QString QFileIconProvider::type(const QFileInfo &info) const
583{
584 if (info.isRoot())
585 return QApplication::translate("QFileDialog", "Drive");
586 if (info.isFile()) {
587 if (!info.suffix().isEmpty())
588 return info.suffix() + QLatin1Char(' ') + QApplication::translate("QFileDialog", "File");
589 return QApplication::translate("QFileDialog", "File");
590 }
591
592 if (info.isDir())
593#ifdef Q_WS_WIN
594 return QApplication::translate("QFileDialog", "File Folder", "Match Windows Explorer");
595#else
596 return QApplication::translate("QFileDialog", "Folder", "All other platforms");
597#endif
598 // Windows - "File Folder"
599 // OS X - "Folder"
600 // Konqueror - "Folder"
601 // Nautilus - "folder"
602
603 if (info.isSymLink())
604#ifdef Q_OS_MAC
605 return QApplication::translate("QFileDialog", "Alias", "Mac OS X Finder");
606#else
607 return QApplication::translate("QFileDialog", "Shortcut", "All other platforms");
608#endif
609 // OS X - "Alias"
610 // Windows - "Shortcut"
611 // Konqueror - "Folder" or "TXT File" i.e. what it is pointing to
612 // Nautilus - "link to folder" or "link to object file", same as Konqueror
613
614 return QApplication::translate("QFileDialog", "Unknown");
615}
616
617QT_END_NAMESPACE
618
619#endif
Note: See TracBrowser for help on using the repository browser.