OSDN Git Service

Changed creation of QFileSystemModel. Object will now be created when the user first...
[lamexp/LameXP.git] / src / Model_FileSystem.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "Model_FileSystem.h"
23 #include "Global.h"
24
25 #include <QApplication>
26 #include <QFileIconProvider>
27 #include <QDesktopServices>
28 #include <QLibrary>
29
30 #define IS_DIR(ATTR) (((ATTR) & FILE_ATTRIBUTE_DIRECTORY) && (!((ATTR) & FILE_ATTRIBUTE_HIDDEN)))
31 #define NO_DOT_OR_DOTDOT(STR) (wcscmp((STR), L".") && wcscmp((STR), L".."))
32
33 typedef HANDLE (WINAPI *FindFirstFileExFun)(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
34
35 ///////////////////////////////////////////////////////////////////////////////
36 // Dummy QFileIconProvider class
37 ///////////////////////////////////////////////////////////////////////////////
38
39 class QFileIconProviderEx : public QFileIconProvider
40 {
41 public:
42         QFileIconProviderEx();
43         virtual QIcon icon(IconType type) const { return (type == Drive) ? m_driveIcon : m_folderIcon; }
44         virtual QIcon icon(const QFileInfo &info) const;
45         virtual QString type (const QFileInfo &info) const { return info.isDir() ? m_folderType : m_emptyType; }
46
47 private:
48         const QIcon m_driveIcon;
49         const QIcon m_cdromIcon;
50         const QIcon m_networkIcon;
51         const QIcon m_floppyIcon;
52         const QIcon m_folderIcon;
53         const QIcon m_homeIcon;
54         const QIcon m_desktopIcon;
55         const QIcon m_musicIcon;
56         const QIcon m_moviesIcon;
57         const QIcon m_picturesIcon;
58         const QIcon m_emptyIcon;
59         const QString m_folderType;
60         const QString m_emptyType;
61         const QString m_homeDir;
62         const QString m_desktopDir;
63         const QString m_musicDir;
64         const QString m_moviesDir;
65         const QString m_picturesDir;
66 };
67
68 QFileIconProviderEx::QFileIconProviderEx()
69 :
70         m_folderIcon(":/icons/folder.png"),
71         m_driveIcon(":/icons/drive.png"),
72         m_cdromIcon(":/icons/drive_cd.png"),
73         m_networkIcon(":/icons/drive_link.png"),
74         m_floppyIcon(":/icons/drive_disk.png"),
75         m_homeIcon(":/icons/house.png"),
76         m_desktopIcon(":/icons/monitor.png"),
77         m_musicIcon(":/icons/music.png"),
78         m_moviesIcon(":/icons/film.png"),
79         m_picturesIcon(":/icons/picture.png"),
80         m_homeDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::HomeLocation))),
81         m_desktopDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation))),
82         m_musicDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::MusicLocation))),
83         m_moviesDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::MoviesLocation))),
84         m_picturesDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation))),
85         m_folderType("Folder")
86 {
87         /* Nothing to do! */
88 }
89
90 QIcon QFileIconProviderEx::icon(const QFileInfo &info) const
91 {
92         if(info.isFile())
93         {
94                 return m_emptyIcon;
95         }
96         else if(info.isRoot())
97         {
98                 switch(GetDriveType(QWCHAR(QDir::toNativeSeparators(info.absoluteFilePath()))))
99                 {
100                 case DRIVE_CDROM:
101                         return m_cdromIcon;
102                         break;
103                 case DRIVE_REMOVABLE:
104                         return m_floppyIcon;
105                         break;
106                 case DRIVE_REMOTE:
107                         return m_networkIcon;
108                         break;
109                 default:
110                         return m_driveIcon;
111                         break;
112                 }
113         }
114         else if(!info.filePath().compare(m_homeDir, Qt::CaseInsensitive))
115         {
116                 return m_homeIcon;
117         }
118         else if(!info.filePath().compare(m_desktopDir, Qt::CaseInsensitive))
119         {
120                 return m_desktopIcon;
121         }
122         else if(!info.filePath().compare(m_musicDir, Qt::CaseInsensitive))
123         {
124                 return m_musicIcon;
125         }
126         else if(!info.filePath().compare(m_moviesDir, Qt::CaseInsensitive))
127         {
128                 return m_moviesIcon;
129         }
130         else if(!info.filePath().compare(m_picturesDir, Qt::CaseInsensitive))
131         {
132                 return m_picturesIcon;
133         }
134         else
135         {
136                 return  m_folderIcon;
137         }
138 }
139
140 ///////////////////////////////////////////////////////////////////////////////
141 // Modified QFileSystemModel class
142 ///////////////////////////////////////////////////////////////////////////////
143
144 QFileSystemModelEx::QFileSystemModelEx()
145 :
146         QFileSystemModel()
147 {
148         this->m_myIconProvider = new QFileIconProviderEx();
149         this->setIconProvider(m_myIconProvider);
150         this->setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
151         this->setNameFilterDisables(false);
152 }
153
154 QFileSystemModelEx::~QFileSystemModelEx()
155 {
156         removeAllFromCache();
157         LAMEXP_DELETE(m_myIconProvider);
158 }
159
160 bool QFileSystemModelEx::hasChildren(const QModelIndex &parent) const
161 {
162         if(parent.isValid())
163         {
164                 return hasSubfoldersCached(filePath(parent).toLower()); //return (QDir(QFileSystemModel::filePath(parent)).entryList(QDir::Dirs | QDir::NoDotAndDotDot).count() > 0);
165         }
166         return true;
167 }
168
169 void QFileSystemModelEx::fetchMore(const QModelIndex &parent)
170 {
171         if(parent.isValid())
172         {
173                 removeFromCache(filePath(parent).toLower());
174         }
175
176         QFileSystemModel::fetchMore(parent);
177 }
178
179 QModelIndex QFileSystemModelEx::index(const QString &path, int column) const
180 {
181         QFileInfo info(path);
182         if(info.exists() && info.isDir())
183         {
184                 QString fullPath = QDir::fromNativeSeparators(info.canonicalFilePath());
185                 QStringList parts = fullPath.split('/', QString::SkipEmptyParts);
186                 for(int i = 2; i <= parts.count(); i++)
187                 {
188                         QFileInfo currentPath(((QStringList) parts.mid(0, i)).join("/"));
189                         if((!currentPath.exists()) || (!currentPath.isDir()) || currentPath.isHidden())
190                         {
191                                 return QModelIndex();
192                         }
193                 }
194                 QModelIndex index = QFileSystemModel::index(fullPath, column);
195                 if(index.isValid())
196                 {
197                         QModelIndex temp = index;
198                         while(temp.isValid())
199                         {
200                                 removeFromCache(filePath(temp).toLower());
201                                 temp = temp.parent();
202                         }
203                         return index;
204                 }
205         }
206         return QModelIndex();
207 }
208
209 void QFileSystemModelEx::flushCache(void)
210 {
211         removeAllFromCache();
212 }
213
214 /* ------------------------ */
215 /*  STATIC FUNCTIONS BELOW  */
216 /* ------------------------ */
217
218 QHash<const QString, bool> QFileSystemModelEx::s_hasSubfolderCache;
219 QMutex QFileSystemModelEx::s_hasSubfolderMutex;
220
221 void *QFileSystemModelEx::FindFirstFileExPtr = NULL;
222 bool QFileSystemModelEx::FindFirstFileExInitialized = false;
223 bool QFileSystemModelEx::FindFirstFileExInfoBasicOK = false;
224
225 bool QFileSystemModelEx::hasSubfoldersCached(const QString &path)
226 {
227         QMutexLocker lock(&s_hasSubfolderMutex);
228
229         if(s_hasSubfolderCache.contains(path))
230         {
231                 return s_hasSubfolderCache.value(path);
232         }
233         
234         bool bChildren = hasSubfolders(path);
235         s_hasSubfolderCache.insert(path, bChildren);
236         return bChildren;
237 }
238
239 void QFileSystemModelEx::removeFromCache(const QString &path)
240 {
241         QMutexLocker lock(&s_hasSubfolderMutex);
242         s_hasSubfolderCache.remove(path);
243 }
244
245 void QFileSystemModelEx::removeAllFromCache(void)
246 {
247         QMutexLocker lock(&s_hasSubfolderMutex);
248         s_hasSubfolderCache.clear();
249 }
250
251 bool QFileSystemModelEx::hasSubfolders(const QString &path)
252 {
253         if(!FindFirstFileExInitialized)
254         {
255                 QLibrary kernel32Lib("kernel32.dll");
256                 if(kernel32Lib.load())
257                 {
258                         FindFirstFileExPtr = kernel32Lib.resolve("FindFirstFileExW");
259                         DWORD osVersionNo = lamexp_get_os_version();
260                         FindFirstFileExInfoBasicOK = LAMEXP_MIN_OS_VER(osVersionNo, 6, 1);
261                 }
262                 FindFirstFileExInitialized = true;
263         }
264
265         WIN32_FIND_DATAW findData;
266         bool bChildren = false;
267
268         HANDLE h = (FindFirstFileExPtr)
269                 ? reinterpret_cast<FindFirstFileExFun>(FindFirstFileExPtr)(QWCHAR(QDir::toNativeSeparators(path + "/*")), (FindFirstFileExInfoBasicOK ? FindExInfoBasic : FindExInfoStandard), &findData, FindExSearchLimitToDirectories, NULL, 0)
270                 : FindFirstFileW(QWCHAR(QDir::toNativeSeparators(path + "/*")), &findData);
271
272         if(h != INVALID_HANDLE_VALUE)
273         {
274                 if(NO_DOT_OR_DOTDOT(findData.cFileName))
275                 {
276                         bChildren = IS_DIR(findData.dwFileAttributes);
277                 }
278                 while((!bChildren) && FindNextFile(h, &findData))
279                 {
280                         if(NO_DOT_OR_DOTDOT(findData.cFileName))
281                         {
282                                 bChildren = IS_DIR(findData.dwFileAttributes);
283                         }
284                 }
285                 FindClose(h);
286         }
287         else
288         {
289                 DWORD err = GetLastError();
290                 if((err == ERROR_NOT_SUPPORTED) || (err == ERROR_INVALID_PARAMETER))
291                 {
292                         qWarning("%s failed with error code #%u", FindFirstFileExPtr ? "FindFirstFileEx" : "FindFirstFile", err);
293                 }
294         }
295
296         return bChildren;
297 }