Integrated file selection dialogue with the main code. Improved the
[openvpnui.git] / src / filebrowse / filemodel.cpp
1 #include "filemodel.h"
2 #include <QDateTime>
3 #include <QSettings>
4 #include <QGuiApplication>
5 #include "engine.h"
6 #include "globals.h"
7
8 enum {
9 FilenameRole = Qt::UserRole + 1,
10 FileKindRole = Qt::UserRole + 2,
11 FileIconRole = Qt::UserRole + 3,
12 PermissionsRole = Qt::UserRole + 4,
13 SizeRole = Qt::UserRole + 5,
14 LastModifiedRole = Qt::UserRole + 6,
15 CreatedRole = Qt::UserRole + 7,
16 IsDirRole = Qt::UserRole + 8,
17 IsLinkRole = Qt::UserRole + 9,
18 SymLinkTargetRole = Qt::UserRole + 10
19 };
20
21 FileModel::FileModel(QObject *parent) :
22 QAbstractListModel(parent),
23 m_active(false),
24 m_dirty(false)
25 {
26 m_dir = "";
27 m_watcher = new QFileSystemWatcher(this);
28 connect(m_watcher, SIGNAL(directoryChanged(const QString&)), this, SLOT(refresh()));
29 connect(m_watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(refresh()));
30
31 QSettings settings;
32 m_showAll = settings.value("showAll", false).toBool();
33
34 // refresh model every time settings are changed
35 Engine *engine = qApp->property("engine").value<Engine *>();
36 connect(engine, SIGNAL(settingsChanged()), this, SLOT(refreshFull()));
37 }
38
39 FileModel::~FileModel()
40 {
41 }
42
43 int FileModel::rowCount(const QModelIndex &parent) const
44 {
45 Q_UNUSED(parent);
46 return m_files.count();
47 }
48
49 QVariant FileModel::data(const QModelIndex &index, int role) const
50 {
51 if (!index.isValid() || index.row() > m_files.size()-1)
52 return QVariant();
53
54 QFileInfo info = m_files.at(index.row()).info;
55 switch (role) {
56
57 case Qt::DisplayRole:
58 case FilenameRole:
59 return info.fileName();
60
61 case FileKindRole:
62 if (info.isSymLink()) return "l";
63 if (info.isDir()) return "d";
64 if (info.isFile()) return "-";
65 return "?";
66
67 case FileIconRole:
68 if (info.isSymLink() && info.isDir()) return "folder-link";
69 if (info.isDir()) return "folder";
70 if (info.isSymLink()) return "link";
71 if (info.isFile()) {
72 QString suffix = info.suffix().toLower();
73 return suffixToIconName(suffix);
74 }
75 return "file";
76
77 case PermissionsRole:
78 return permissionsToString(info.permissions());
79
80 case SizeRole:
81 if (info.isSymLink() && info.isDir()) return "dir-link";
82 if (info.isDir()) return "dir";
83 return filesizeToString(info.size());
84
85 case LastModifiedRole:
86 return datetimeToString(info.lastModified());
87
88 case CreatedRole:
89 return datetimeToString(info.created());
90
91 case IsDirRole:
92 return info.isDir();
93
94 case IsLinkRole:
95 return info.isSymLink();
96
97 case SymLinkTargetRole:
98 return info.symLinkTarget();
99
100 default:
101 return QVariant();
102 }
103 }
104
105 QHash<int, QByteArray> FileModel::roleNames() const
106 {
107 QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
108 roles.insert(FilenameRole, QByteArray("filename"));
109 roles.insert(FileKindRole, QByteArray("filekind"));
110 roles.insert(FileIconRole, QByteArray("fileIcon"));
111 roles.insert(PermissionsRole, QByteArray("permissions"));
112 roles.insert(SizeRole, QByteArray("size"));
113 roles.insert(LastModifiedRole, QByteArray("modified"));
114 roles.insert(CreatedRole, QByteArray("created"));
115 roles.insert(IsDirRole, QByteArray("isDir"));
116 roles.insert(IsLinkRole, QByteArray("isLink"));
117 roles.insert(SymLinkTargetRole, QByteArray("symLinkTarget"));
118 return roles;
119 }
120
121 int FileModel::fileCount() const
122 {
123 return m_files.count();
124 }
125
126 QString FileModel::errorMessage() const
127 {
128 return m_errorMessage;
129 }
130
131 void FileModel::setDir(QString dir)
132 {
133 if (m_dir == dir)
134 return;
135
136 // update watcher to watch the new directory
137 if (!m_dir.isEmpty())
138 m_watcher->removePath(m_dir);
139 if (!dir.isEmpty())
140 m_watcher->addPath(dir);
141
142 m_dir = dir;
143
144 readDirectory();
145 m_dirty = false;
146
147 emit dirChanged();
148 }
149
150 QString FileModel::appendPath(QString dirName)
151 {
152 return QDir::cleanPath(QDir(m_dir).absoluteFilePath(dirName));
153 }
154
155 void FileModel::setActive(bool active)
156 {
157 if (m_active == active)
158 return;
159
160 m_active = active;
161 emit activeChanged();
162
163 if (m_dirty)
164 readDirectory();
165
166 m_dirty = false;
167 }
168
169 QString FileModel::parentPath()
170 {
171 return QDir::cleanPath(QDir(m_dir).absoluteFilePath(".."));
172 }
173
174 QString FileModel::fileNameAt(int fileIndex)
175 {
176 if (fileIndex < 0 || fileIndex >= m_files.count())
177 return QString();
178
179 return m_files.at(fileIndex).info.absoluteFilePath();
180 }
181
182 void FileModel::refresh()
183 {
184 if (!m_active) {
185 m_dirty = true;
186 return;
187 }
188
189 refreshEntries();
190 m_dirty = false;
191 }
192
193 void FileModel::refreshFull()
194 {
195 if (!m_active) {
196 m_dirty = true;
197 return;
198 }
199
200 readDirectory();
201 m_dirty = false;
202 }
203
204 void FileModel::readDirectory()
205 {
206 // wrapped in reset model methods to get views notified
207 beginResetModel();
208
209 m_files.clear();
210 m_errorMessage = "";
211
212 if (!m_dir.isEmpty())
213 readEntries();
214
215 endResetModel();
216 emit fileCountChanged();
217 emit errorMessageChanged();
218 }
219
220 void FileModel::readEntries()
221 {
222 QDir dir(m_dir);
223 if (!dir.exists()) {
224 m_errorMessage = tr("Folder does not exist");
225 return;
226 }
227 if (access(m_dir, R_OK) == -1) {
228 m_errorMessage = tr("No permission to read the folder");
229 return;
230 }
231
232 QSettings settings;
233 bool hiddenSetting = settings.value("show-hidden-files", false).toBool();
234 QDir::Filter hidden = hiddenSetting ? QDir::Hidden : (QDir::Filter)0;
235 dir.setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot | hidden);
236
237 if (settings.value("show-dirs-first", false).toBool())
238 dir.setSorting(QDir::Name | QDir::DirsFirst);
239
240 Engine *engine = qApp->property("engine").value<Engine *>();
241
242 QFileInfoList infoList = dir.entryInfoList();
243 foreach (QFileInfo info, infoList) {
244 if (m_showAll || (info.isDir()) || (info.suffix() == engine->extensionFilter())) {
245 FileData data;
246 data.info = info;
247 m_files.append(data);
248 }
249 }
250 }
251
252 void FileModel::refreshEntries()
253 {
254 m_errorMessage = "";
255
256 // empty dir name
257 if (m_dir.isEmpty()) {
258 beginResetModel();
259 m_files.clear();
260 endResetModel();
261 emit fileCountChanged();
262 emit errorMessageChanged();
263 return;
264 }
265
266 QDir dir(m_dir);
267 if (!dir.exists()) {
268 m_errorMessage = tr("Folder does not exist");
269 emit errorMessageChanged();
270 return;
271 }
272 if (access(m_dir, R_OK) == -1) {
273 m_errorMessage = tr("No permission to read the folder");
274 emit errorMessageChanged();
275 return;
276 }
277
278 QSettings settings;
279 bool hiddenSetting = settings.value("show-hidden-files", false).toBool();
280 QDir::Filter hidden = hiddenSetting ? QDir::Hidden : (QDir::Filter)0;
281 dir.setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot | hidden);
282
283 if (settings.value("show-dirs-first", false).toBool())
284 dir.setSorting(QDir::Name | QDir::DirsFirst);
285
286 Engine *engine = qApp->property("engine").value<Engine *>();
287
288 // read all files
289 QList<FileData> newFiles;
290 QFileInfoList infoList = dir.entryInfoList();
291 foreach (QFileInfo info, infoList) {
292 if ((m_showAll) || (info.isDir()) || (info.suffix() == engine->extensionFilter())) {
293 FileData data;
294 data.info = info;
295 newFiles.append(data);
296 }
297 }
298
299 int oldFileCount = m_files.count();
300
301 // compare old and new files and do removes if needed
302 for (int i = m_files.count()-1; i >= 0; --i) {
303 FileData data = m_files.at(i);
304 if (!newFiles.contains(data)) {
305 beginRemoveRows(QModelIndex(), i, i);
306 m_files.removeAt(i);
307 endRemoveRows();
308 }
309 }
310 // compare old and new files and do inserts if needed
311 for (int i = 0; i < newFiles.count(); ++i) {
312 FileData data = newFiles.at(i);
313 if (!m_files.contains(data)) {
314 beginInsertRows(QModelIndex(), i, i);
315 m_files.insert(i, data);
316 endInsertRows();
317 }
318 }
319
320 if (m_files.count() != oldFileCount)
321 emit fileCountChanged();
322
323 emit errorMessageChanged();
324 }
325
326 void FileModel::setShowAll(bool showAll)
327 {
328 if (m_showAll != showAll) {
329 m_showAll = showAll;
330
331 QSettings settings;
332 settings.setValue("showAll", m_showAll);
333
334 emit showAllChanged();
335 refresh();
336 }
337 }