OSDN Git Service

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