Integrated file selection dialogue with the main code. Improved the
[openvpnui.git] / src / filebrowse / engine.cpp
1 #include "engine.h"
2 #include <QDateTime>
3 #include <QTextStream>
4 #include <QSettings>
5 #include <QStandardPaths>
6 #include "globals.h"
7 #include "fileworker.h"
8
9 Engine::Engine(QObject *parent) :
10 QObject(parent),
11 m_clipboardContainsCopy(false),
12 m_progress(0),
13 m_selectedFilename(""),
14 m_extensionFilter("")
15 {
16 m_fileWorker = new FileWorker;
17
18 // update progress property when worker progresses
19 connect(m_fileWorker, SIGNAL(progressChanged(int, QString)),
20 this, SLOT(setProgress(int, QString)));
21
22 // pass worker end signals to QML
23 connect(m_fileWorker, SIGNAL(done()), this, SIGNAL(workerDone()));
24 connect(m_fileWorker, SIGNAL(errorOccurred(QString, QString)),
25 this, SIGNAL(workerErrorOccurred(QString, QString)));
26 connect(m_fileWorker, SIGNAL(fileDeleted(QString)), this, SIGNAL(fileDeleted(QString)));
27 }
28
29 Engine::~Engine()
30 {
31 // is this the way to force stop the worker thread?
32 m_fileWorker->cancel(); // stop possibly running background thread
33 m_fileWorker->wait(); // wait until thread stops
34 delete m_fileWorker; // delete it
35 }
36
37 void Engine::deleteFiles(QStringList filenames)
38 {
39 setProgress(0, "");
40 m_fileWorker->startDeleteFiles(filenames);
41 }
42
43 void Engine::cutFiles(QStringList filenames)
44 {
45 m_clipboardFiles = filenames;
46 m_clipboardContainsCopy = false;
47 emit clipboardCountChanged();
48 emit clipboardContainsCopyChanged();
49 }
50
51 void Engine::copyFiles(QStringList filenames)
52 {
53 m_clipboardFiles = filenames;
54 m_clipboardContainsCopy = true;
55 emit clipboardCountChanged();
56 emit clipboardContainsCopyChanged();
57 }
58
59 void Engine::pasteFiles(QString destDirectory)
60 {
61 if (m_clipboardFiles.isEmpty()) {
62 emit workerErrorOccurred("No files to paste", "");
63 return;
64 }
65
66 QStringList files = m_clipboardFiles;
67 setProgress(0, "");
68
69 QDir dest(destDirectory);
70 if (!dest.exists()) {
71 emit workerErrorOccurred(tr("Destination does not exist"), destDirectory);
72 return;
73 }
74
75 foreach (QString filename, files) {
76 QFileInfo fileInfo(filename);
77 QString newname = dest.absoluteFilePath(fileInfo.fileName());
78
79 // source and dest filenames are the same?
80 if (filename == newname) {
81 emit workerErrorOccurred(tr("Can't overwrite itself"), newname);
82 return;
83 }
84
85 // dest is under source? (directory)
86 if (newname.startsWith(filename)) {
87 emit workerErrorOccurred(tr("Can't move/copy to itself"), filename);
88 return;
89 }
90 }
91
92 m_clipboardFiles.clear();
93 emit clipboardCountChanged();
94
95 if (m_clipboardContainsCopy) {
96 m_fileWorker->startCopyFiles(files, destDirectory);
97 return;
98 }
99
100 m_fileWorker->startMoveFiles(files, destDirectory);
101 }
102
103 void Engine::cancel()
104 {
105 m_fileWorker->cancel();
106 }
107
108 QString Engine::homeFolder() const
109 {
110 return QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
111 }
112
113 int Engine::startDepth() const
114 {
115 return 2;
116 }
117
118 bool Engine::exists(QString filename)
119 {
120 return QFile::exists(filename);
121 }
122
123 QStringList Engine::diskSpace(QString path)
124 {
125 // run df to get disk space
126 QString blockSize = "--block-size=1024";
127 QString result = execute("/bin/df", QStringList() << blockSize << path, false);
128 if (result.isEmpty())
129 return QStringList();
130
131 // parse result
132 QStringList lines = result.split(QRegExp("[\n\r]"));
133 if (lines.count() < 2)
134 return QStringList();
135
136 QString line = lines.at(1);
137 QStringList columns = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
138 if (columns.count() < 5)
139 return QStringList();
140
141 QString totalString = columns.at(1);
142 QString usedString = columns.at(2);
143 QString percentageString = columns.at(4);
144 qint64 total = totalString.toLongLong() * 1024LL;
145 qint64 used = usedString.toLongLong() * 1024LL;
146
147 return QStringList() << percentageString << filesizeToString(used)+"/"+filesizeToString(total);
148 }
149
150 QStringList Engine::readFile(QString filename)
151 {
152 int maxLines = 1000;
153 int maxSize = 10000;
154 int maxBinSize = 2048;
155
156 // check permissions
157 if (access(filename, R_OK) == -1)
158 return makeStringList(tr("No permission to read the file\n%1").arg(filename));
159
160 QFile file(filename);
161 if (!file.open(QIODevice::ReadOnly))
162 return makeStringList(tr("Error reading file\n%1").arg(filename));
163
164 // read start of file
165 char buffer[maxSize+1];
166 qint64 readSize = file.read(buffer, maxSize);
167 if (readSize < 0)
168 return makeStringList(tr("Error reading file\n%1").arg(filename));
169
170 if (readSize == 0)
171 return makeStringList(tr("Empty file"));
172
173 bool atEnd = file.atEnd();
174 file.close();
175
176 // detect binary or text file, it is binary if it contains zeros
177 bool isText = true;
178 for (int i = 0; i < readSize; ++i) {
179 if (buffer[i] == 0) {
180 isText = false;
181 break;
182 }
183 }
184
185 // binary output
186 if (!isText) {
187 // two different line widths
188 if (readSize > maxBinSize) {
189 readSize = maxBinSize;
190 atEnd = false;
191 }
192 QString out8 = createHexDump(buffer, readSize, 8);
193 QString out16 = createHexDump(buffer, readSize, 16);
194 QString msg = "";
195
196 if (!atEnd) {
197 msg = tr("--- Binary file preview clipped at %1 kB ---").arg(maxBinSize/1000);
198 msg = tr("--- Binary file preview clipped at %1 kB ---").arg(maxBinSize/1000);
199 }
200
201 return QStringList() << msg << out8 << out16;
202 }
203
204 // read lines to a string list and join
205 QByteArray ba(buffer, readSize);
206 QTextStream in(&ba);
207 QStringList lines;
208 int lineCount = 0;
209 while (!in.atEnd() && lineCount < maxLines) {
210 QString line = in.readLine();
211 lines.append(line);
212 lineCount++;
213 }
214
215 QString msg = "";
216 if (lineCount == maxLines)
217 msg = tr("--- Text file preview clipped at %1 lines ---").arg(maxLines);
218 else if (!atEnd)
219 msg = tr("--- Text file preview clipped at %1 kB ---").arg(maxSize/1000);
220
221 return makeStringList(msg, lines.join("\n"));
222 }
223
224 QString Engine::mkdir(QString path, QString name)
225 {
226 QDir dir(path);
227
228 if (!dir.mkdir(name)) {
229 if (access(dir.absolutePath(), W_OK) == -1)
230 return tr("Cannot create folder %1\nPermission denied").arg(name);
231
232 return tr("Cannot create folder %1").arg(name);
233 }
234
235 return QString();
236 }
237
238 QStringList Engine::rename(QString fullOldFilename, QString newName)
239 {
240 QFile file(fullOldFilename);
241 QFileInfo fileInfo(fullOldFilename);
242 QDir dir = fileInfo.absoluteDir();
243 QString fullNewFilename = dir.absoluteFilePath(newName);
244
245 QString errorMessage;
246 if (!file.rename(fullNewFilename)) {
247 QString oldName = fileInfo.fileName();
248 errorMessage = tr("Cannot rename %1\n%2").arg(oldName).arg(file.errorString());
249 }
250
251 return QStringList() << fullNewFilename << errorMessage;
252 }
253
254 QString Engine::chmod(QString path,
255 bool ownerRead, bool ownerWrite, bool ownerExecute,
256 bool groupRead, bool groupWrite, bool groupExecute,
257 bool othersRead, bool othersWrite, bool othersExecute)
258 {
259 QFile file(path);
260 QFileDevice::Permissions p;
261 if (ownerRead) p |= QFileDevice::ReadOwner;
262 if (ownerWrite) p |= QFileDevice::WriteOwner;
263 if (ownerExecute) p |= QFileDevice::ExeOwner;
264 if (groupRead) p |= QFileDevice::ReadGroup;
265 if (groupWrite) p |= QFileDevice::WriteGroup;
266 if (groupExecute) p |= QFileDevice::ExeGroup;
267 if (othersRead) p |= QFileDevice::ReadOther;
268 if (othersWrite) p |= QFileDevice::WriteOther;
269 if (othersExecute) p |= QFileDevice::ExeOther;
270 if (!file.setPermissions(p))
271 return tr("Cannot change permissions\n%1").arg(file.errorString());
272
273 return QString();
274 }
275
276 QString Engine::readSetting(QString key, QString defaultValue)
277 {
278 QSettings settings;
279 return settings.value(key, defaultValue).toString();
280 }
281
282 void Engine::writeSetting(QString key, QString value)
283 {
284 QSettings settings;
285
286 // do nothing if value didn't change
287 if (settings.value(key) == value)
288 return;
289
290 settings.setValue(key, value);
291
292 emit settingsChanged();
293 }
294
295 void Engine::setProgress(int progress, QString filename)
296 {
297 m_progress = progress;
298 m_progressFilename = filename;
299 emit progressChanged();
300 emit progressFilenameChanged();
301 }
302
303 QString Engine::createHexDump(char *buffer, int size, int bytesPerLine)
304 {
305 QString out;
306 QString ascDump;
307 int i;
308 for (i = 0; i < size; ++i) {
309 if ((i % bytesPerLine) == 0) { // line change
310 out += " "+ascDump+"\n"+
311 QString("%1").arg(QString::number(i, 16), 4, QLatin1Char('0'))+": ";
312 ascDump.clear();
313 }
314
315 out += QString("%1").arg(QString::number((unsigned char)buffer[i], 16),
316 2, QLatin1Char('0'))+" ";
317 if (buffer[i] >= 32 && buffer[i] <= 126)
318 ascDump += buffer[i];
319 else
320 ascDump += ".";
321 }
322 // write out remaining asc dump
323 if ((i % bytesPerLine) > 0) {
324 int emptyBytes = bytesPerLine - (i % bytesPerLine);
325 for (int j = 0; j < emptyBytes; ++j) {
326 out += " ";
327 }
328 }
329 out += " "+ascDump;
330
331 return out;
332 }
333
334 QStringList Engine::makeStringList(QString msg, QString str)
335 {
336 QStringList list;
337 list << msg << str << str;
338 return list;
339 }
340
341 void Engine::setSelectedFilename(QString selectedFilename)
342 {
343 m_selectedFilename = selectedFilename;
344 emit selectedFilenameChanged();
345 }
346
347 void Engine::setExtensionFilter(QString extensionFilter)
348 {
349 if (m_extensionFilter != extensionFilter) {
350 m_extensionFilter = extensionFilter;
351 emit extensionFilterChanged();
352 }
353 }