OSDN Git Service

Updated Russian translation. Thanks to Иван Митин <bardak@inbox.ru>.
[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_heartIcon;
59         const QIcon m_emptyIcon;
60         const QString m_folderType;
61         const QString m_emptyType;
62         const QString m_homeDir;
63         const QString m_desktopDir;
64         const QString m_musicDir;
65         const QString m_moviesDir;
66         const QString m_picturesDir;
67         const QString m_installDir;
68 };
69
70 QFileIconProviderEx::QFileIconProviderEx()
71 :
72         m_folderIcon(":/icons/folder.png"),
73         m_driveIcon(":/icons/drive.png"),
74         m_cdromIcon(":/icons/drive_cd.png"),
75         m_networkIcon(":/icons/drive_link.png"),
76         m_floppyIcon(":/icons/drive_disk.png"),
77         m_homeIcon(":/icons/house.png"),
78         m_desktopIcon(":/icons/monitor.png"),
79         m_musicIcon(":/icons/music.png"),
80         m_moviesIcon(":/icons/film.png"),
81         m_picturesIcon(":/icons/picture.png"),
82         m_heartIcon(":/icons/heart.png"),
83         m_homeDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::HomeLocation))),
84         m_desktopDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation))),
85         m_musicDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::MusicLocation))),
86         m_moviesDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::MoviesLocation))),
87         m_picturesDir(QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation))),
88         m_installDir(QDir::fromNativeSeparators(qApp->applicationDirPath())),
89         m_folderType("Folder")
90 {
91         /* Nothing to do! */
92 }
93
94 QIcon QFileIconProviderEx::icon(const QFileInfo &info) const
95 {
96         if(info.isFile())
97         {
98                 return m_emptyIcon;
99         }
100         else if(info.isRoot())
101         {
102                 switch(GetDriveType(QWCHAR(QDir::toNativeSeparators(info.absoluteFilePath()))))
103                 {
104                 case DRIVE_CDROM:
105                         return m_cdromIcon;
106                         break;
107                 case DRIVE_REMOVABLE:
108                         return m_floppyIcon;
109                         break;
110                 case DRIVE_REMOTE:
111                         return m_networkIcon;
112                         break;
113                 default:
114                         return m_driveIcon;
115                         break;
116                 }
117         }
118         else
119         {
120                 const QString filePath = info.filePath();
121                 if(m_homeDir.compare(filePath, Qt::CaseInsensitive) == 0)
122                 {
123                         return m_homeIcon;
124                 }
125                 else if(m_desktopDir.compare(filePath, Qt::CaseInsensitive) == 0)
126                 {
127                         return m_desktopIcon;
128                 }
129                 else if(m_musicDir.compare(filePath, Qt::CaseInsensitive) == 0)
130                 {
131                         return m_musicIcon;
132                 }
133                 else if(m_moviesDir.compare(filePath, Qt::CaseInsensitive) == 0)
134                 {
135                         return m_moviesIcon;
136                 }
137                 else if(m_picturesDir.compare(filePath, Qt::CaseInsensitive) == 0)
138                 {
139                         return m_picturesIcon;
140                 }
141                 else if(m_installDir.compare(filePath, Qt::CaseInsensitive) == 0)
142                 {
143                         return m_heartIcon;
144                 }
145                 else
146                 {
147                         return  m_folderIcon;
148                 }
149         }
150 }
151
152 ///////////////////////////////////////////////////////////////////////////////
153 // Modified QFileSystemModel class
154 ///////////////////////////////////////////////////////////////////////////////
155
156 QFileSystemModelEx::QFileSystemModelEx()
157 :
158         QFileSystemModel()
159 {
160         this->m_myIconProvider = new QFileIconProviderEx();
161         this->setIconProvider(m_myIconProvider);
162         this->setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
163         this->setNameFilterDisables(false);
164 }
165
166 QFileSystemModelEx::~QFileSystemModelEx()
167 {
168         removeAllFromCache();
169         LAMEXP_DELETE(m_myIconProvider);
170 }
171
172 bool QFileSystemModelEx::hasChildren(const QModelIndex &parent) const
173 {
174         if(parent.isValid())
175         {
176                 return hasSubfoldersCached(filePath(parent).toLower()); //return (QDir(QFileSystemModel::filePath(parent)).entryList(QDir::Dirs | QDir::NoDotAndDotDot).count() > 0);
177         }
178         return true;
179 }
180
181 void QFileSystemModelEx::fetchMore(const QModelIndex &parent)
182 {
183         if(parent.isValid())
184         {
185                 removeFromCache(filePath(parent).toLower());
186         }
187
188         QFileSystemModel::fetchMore(parent);
189 }
190
191 QModelIndex QFileSystemModelEx::index(const QString &path, int column) const
192 {
193         QFileInfo info(path);
194         if(info.exists() && info.isDir())
195         {
196                 QString fullPath = QDir::fromNativeSeparators(info.canonicalFilePath());
197                 QStringList parts = fullPath.split('/', QString::SkipEmptyParts);
198                 for(int i = 2; i <= parts.count(); i++)
199                 {
200                         QFileInfo currentPath(((QStringList) parts.mid(0, i)).join("/"));
201                         if((!currentPath.exists()) || (!currentPath.isDir()) || currentPath.isHidden())
202                         {
203                                 return QModelIndex();
204                         }
205                 }
206                 QModelIndex index = QFileSystemModel::index(fullPath, column);
207                 if(index.isValid())
208                 {
209                         QModelIndex temp = index;
210                         while(temp.isValid())
211                         {
212                                 removeFromCache(filePath(temp).toLower());
213                                 temp = temp.parent();
214                         }
215                         return index;
216                 }
217         }
218         return QModelIndex();
219 }
220
221 void QFileSystemModelEx::flushCache(void)
222 {
223         removeAllFromCache();
224 }
225
226 /* ------------------------ */
227 /*  STATIC FUNCTIONS BELOW  */
228 /* ------------------------ */
229
230 QHash<const QString, bool> QFileSystemModelEx::s_hasSubfolderCache;
231 QMutex QFileSystemModelEx::s_hasSubfolderMutex;
232
233 void *QFileSystemModelEx::FindFirstFileExPtr = NULL;
234 bool QFileSystemModelEx::FindFirstFileExInitialized = false;
235 bool QFileSystemModelEx::FindFirstFileExInfoBasicOK = false;
236
237 bool QFileSystemModelEx::hasSubfoldersCached(const QString &path)
238 {
239         QMutexLocker lock(&s_hasSubfolderMutex);
240
241         if(s_hasSubfolderCache.contains(path))
242         {
243                 return s_hasSubfolderCache.value(path);
244         }
245         
246         bool bChildren = hasSubfolders(path);
247         s_hasSubfolderCache.insert(path, bChildren);
248         return bChildren;
249 }
250
251 void QFileSystemModelEx::removeFromCache(const QString &path)
252 {
253         QMutexLocker lock(&s_hasSubfolderMutex);
254         s_hasSubfolderCache.remove(path);
255 }
256
257 void QFileSystemModelEx::removeAllFromCache(void)
258 {
259         QMutexLocker lock(&s_hasSubfolderMutex);
260         s_hasSubfolderCache.clear();
261 }
262
263 bool QFileSystemModelEx::hasSubfolders(const QString &path)
264 {
265         if(!FindFirstFileExInitialized)
266         {
267                 QLibrary kernel32Lib("kernel32.dll");
268                 if(kernel32Lib.load())
269                 {
270                         FindFirstFileExPtr = kernel32Lib.resolve("FindFirstFileExW");
271                         DWORD osVersionNo = lamexp_get_os_version();
272                         FindFirstFileExInfoBasicOK = LAMEXP_MIN_OS_VER(osVersionNo, 6, 1);
273                 }
274                 FindFirstFileExInitialized = true;
275         }
276
277         WIN32_FIND_DATAW findData;
278         bool bChildren = false;
279
280         HANDLE h = (FindFirstFileExPtr)
281                 ? reinterpret_cast<FindFirstFileExFun>(FindFirstFileExPtr)(QWCHAR(QDir::toNativeSeparators(path + "/*")), (FindFirstFileExInfoBasicOK ? FindExInfoBasic : FindExInfoStandard), &findData, FindExSearchLimitToDirectories, NULL, 0)
282                 : FindFirstFileW(QWCHAR(QDir::toNativeSeparators(path + "/*")), &findData);
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("%s failed with error code #%u", FindFirstFileExPtr ? "FindFirstFileEx" : "FindFirstFile", err);
305                 }
306         }
307
308         return bChildren;
309 }