OSDN Git Service

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