1 #include "cbmdosfsmodel.h"
2 #include "cbmdosfilemimedata.h"
3 #include "editoperationcheck.h"
4 #include "v1541commander.h"
5 #include "petsciistr.h"
6 #include "settings.h"
7 #include "utils.h"
8
9 #include <QFont>
10 #include <QList>
11 #include <QRegularExpression>
12 #include <QTemporaryDir>
13 #include <QUrl>
14
15 #include <1541img/cbmdosfs.h>
16 #include <1541img/cbmdosvfs.h>
17 #include <1541img/cbmdosvfseventargs.h>
18 #include <1541img/cbmdosfile.h>
19 #include <1541img/event.h>
20 #include <1541img/petscii.h>
21
evhdl(void * receiver,int id,const void * sender,const void * args)22 static void evhdl(void *receiver, int id, const void *sender, const void *args)
23 {
24 (void) id;
25 (void) sender;
26
27 CbmdosFsModel *model = (CbmdosFsModel *)receiver;
28 const CbmdosVfsEventArgs *eventArgs = (const CbmdosVfsEventArgs *)args;
29 model->fsChanged(eventArgs);
30 }
31
32 class CbmdosFsModel::priv
33 {
34 public:
35 priv();
36 ~priv();
37 QTemporaryDir *tmpDir;
38 CbmdosFs *fs;
39 QSizeF itemSize;
40 };
41
priv()42 CbmdosFsModel::priv::priv() :
43 tmpDir(0), fs(0), itemSize()
44 {}
45
~priv()46 CbmdosFsModel::priv::~priv()
47 {
48 delete tmpDir;
49 }
50
CbmdosFsModel(QObject * parent)51 CbmdosFsModel::CbmdosFsModel(QObject *parent)
52 : QAbstractListModel(parent)
53 {
54 d = new priv();
55
56 connect(&cmdr, &V1541Commander::lowerCaseChanged,
57 this, [this](bool lowerCase){
58 (void) lowerCase;
59 emit dataChanged(createIndex(0, 0),
60 createIndex(rowCount()-1, 0),
61 QVector<int>(Qt::DisplayRole));
62 });
63 }
64
~CbmdosFsModel()65 CbmdosFsModel::~CbmdosFsModel()
66 {
67 if (d->fs)
68 {
69 CbmdosVfs *vfs = CbmdosFs_vfs(d->fs);
70 Event_unregister(CbmdosVfs_changedEvent(vfs), this, evhdl);
71 }
72 delete d;
73 }
74
fsChanged(const CbmdosVfsEventArgs * args)75 void CbmdosFsModel::fsChanged(const CbmdosVfsEventArgs *args)
76 {
77 emit modified();
78 switch (args->what)
79 {
80 case CbmdosVfsEventArgs::CVE_FILECHANGED:
81 {
82 QModelIndex pos = createIndex(args->filepos + 1, 0);
83 emit dataChanged(pos, pos, QVector<int>(Qt::DisplayRole));
84 }
85 break;
86
87 case CbmdosVfsEventArgs::CVE_FILEMOVED:
88 if (args->targetpos > args->filepos)
89 {
90 QModelIndex from = createIndex(args->filepos + 1, 0);
91 QModelIndex to = createIndex(args->targetpos + 1, 0);
92 emit dataChanged(from, to, QVector<int>(Qt::DisplayRole));
93 emit selectedIndexChanged(to,
94 QItemSelectionModel::ClearAndSelect);
95 }
96 else
97 {
98 QModelIndex from = createIndex(args->targetpos + 1, 0);
99 QModelIndex to = createIndex(args->filepos + 1, 0);
100 emit dataChanged(from, to, QVector<int>(Qt::DisplayRole));
101 emit selectedIndexChanged(from,
102 QItemSelectionModel::ClearAndSelect);
103 }
104 break;
105
106 case CbmdosVfsEventArgs::CVE_NAMECHANGED:
107 case CbmdosVfsEventArgs::CVE_IDCHANGED:
108 case CbmdosVfsEventArgs::CVE_DOSVERCHANGED:
109 {
110 QModelIndex pos = createIndex(0, 0);
111 emit dataChanged(pos, pos, QVector<int>(Qt::DisplayRole));
112 }
113 break;
114
115 case CbmdosVfsEventArgs::CVE_FILEDELETED:
116 {
117 endRemoveRows();
118 QModelIndex last = createIndex(rowCount() - 1, 0);
119 emit dataChanged(last, last, QVector<int>(Qt::DisplayRole));
120 }
121 break;
122
123 case CbmdosVfsEventArgs::CVE_FILEADDED:
124 {
125 endInsertRows();
126 QModelIndex last = createIndex(rowCount() - 1, 0);
127 emit dataChanged(last, last, QVector<int>(Qt::DisplayRole));
128 QModelIndex at = createIndex(args->filepos + 1, 0);
129 emit selectedIndexChanged(at,
130 QItemSelectionModel::ClearAndSelect);
131 }
132 break;
133
134 default:
135 break;
136 }
137 }
138
fs() const139 CbmdosFs *CbmdosFsModel::fs() const
140 {
141 return d->fs;
142 }
143
setFs(CbmdosFs * fs)144 void CbmdosFsModel::setFs(CbmdosFs *fs)
145 {
146 if (d->fs)
147 {
148 CbmdosVfs *vfs = CbmdosFs_vfs(d->fs);
149 Event_unregister(CbmdosVfs_changedEvent(vfs), this, evhdl);
150 beginRemoveRows(QModelIndex(), 0, CbmdosVfs_fileCount(vfs)+1);
151 d->fs = 0;
152 endRemoveRows();
153 }
154 if (fs)
155 {
156 CbmdosVfs *vfs = CbmdosFs_vfs(fs);
157 beginInsertRows(QModelIndex(), 0, CbmdosVfs_fileCount(vfs)+1);
158 d->fs = fs;
159 Event_register(CbmdosVfs_changedEvent(vfs), this, evhdl);
160 endInsertRows();
161 }
162 }
163
deleteAt(const QModelIndex & at)164 void CbmdosFsModel::deleteAt(const QModelIndex &at)
165 {
166 if (at.row() > 0 && at.row() < rowCount() - 1)
167 {
168 beginRemoveRows(QModelIndex(), at.row(), at.row());
169 CbmdosVfs_deleteAt(CbmdosFs_vfs(d->fs), at.row() - 1);
170 }
171 }
172
addFile(const QModelIndex & at,CbmdosFile * newFile)173 void CbmdosFsModel::addFile(const QModelIndex &at, CbmdosFile *newFile)
174 {
175 EditOperationCheck check(newFile);
176 emit checkEditOperation(check);
177 if (!check.allowed())
178 {
179 CbmdosFile_destroy(newFile);
180 return;
181 }
182 if (at.isValid() && at.row() > 0)
183 {
184 beginInsertRows(QModelIndex(), at.row(), at.row());
185 CbmdosVfs_insert(CbmdosFs_vfs(d->fs), newFile, at.row() - 1);
186 }
187 else
188 {
189 beginInsertRows(QModelIndex(), rowCount() - 1, rowCount() - 1);
190 CbmdosVfs_append(CbmdosFs_vfs(d->fs), newFile);
191 }
192 }
193
setItemSize(QSizeF size)194 void CbmdosFsModel::setItemSize(QSizeF size)
195 {
196 d->itemSize = size;
197 }
198
tmpDir() const199 const QTemporaryDir *CbmdosFsModel::tmpDir() const
200 {
201 if (!d->tmpDir) d->tmpDir = new QTemporaryDir();
202 return d->tmpDir;
203 }
204
rowCount(const QModelIndex & parent) const205 int CbmdosFsModel::rowCount(const QModelIndex &parent) const
206 {
207 (void) parent;
208 if (!d->fs) return 0;
209 const CbmdosVfs *vfs = CbmdosFs_rvfs(d->fs);
210 return CbmdosVfs_fileCount(vfs) + 2;
211 }
212
data(const QModelIndex & index,int role) const213 QVariant CbmdosFsModel::data(const QModelIndex &index, int role) const
214 {
215 uint8_t buffer[28];
216
217 if (role == Qt::FontRole)
218 {
219 return cmdr.c64font();
220 }
221
222 if (role == Qt::SizeHintRole)
223 {
224 return d->itemSize;
225 }
226
227 if (!d->fs) return QVariant();
228
229 if (role != Qt::DisplayRole)
230 {
231 return QVariant();
232 }
233
234 int row = index.row();
235 int rowcount = rowCount();
236 const CbmdosVfs *vfs = CbmdosFs_rvfs(d->fs);
237
238 if (row > 0 && row < rowcount - 1)
239 {
240 const CbmdosFile *file = CbmdosVfs_rfile(vfs, row-1);
241 CbmdosFile_getDirLine(file, buffer);
242 PetsciiStr dirLine((char *)buffer, 28);
243 return dirLine.toString(cmdr.settings().lowercase());
244 }
245
246 if (row == 0)
247 {
248 QString heading("0 ");
249 CbmdosVfs_getDirHeader(vfs, buffer);
250 PetsciiStr dirHeader((char *)buffer, 24);
251 heading.append(dirHeader.toString(cmdr.settings().lowercase(), true));
252 return heading;
253 }
254
255 CbmdosFs_getFreeBlocksLine(d->fs, buffer);
256 PetsciiStr freeLine((char *)buffer, 16);
257 return freeLine.toString(cmdr.settings().lowercase());
258 }
259
flags(const QModelIndex & index) const260 Qt::ItemFlags CbmdosFsModel::flags(const QModelIndex &index) const
261 {
262 if (!d->fs) return Qt::ItemNeverHasChildren;
263 if (!index.isValid()) return Qt::ItemIsDropEnabled;
264
265 int row = index.row();
266 int rowcount = rowCount();
267 if (row > 0 && row < rowcount - 1)
268 {
269 return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled
270 | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
271 }
272
273 return Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
274 }
275
mimeTypes() const276 QStringList CbmdosFsModel::mimeTypes() const
277 {
278 return QStringList({
279 CbmdosFileMimeData::internalFormat(),
280 "text/uri-list"});
281 }
282
mimeData(const QModelIndexList & indexes) const283 QMimeData *CbmdosFsModel::mimeData(const QModelIndexList &indexes) const
284 {
285 if (indexes.empty()) return 0;
286 CbmdosFileMimeData *mimeData = new CbmdosFileMimeData(this);
287 CbmdosVfs *vfs = CbmdosFs_vfs(d->fs);
288 for (QModelIndexList::const_iterator i = indexes.cbegin();
289 i != indexes.cend(); ++i)
290 {
291 if (i->isValid())
292 {
293 int row = i->row();
294 if (row > 0 && row < rowCount() - 1)
295 {
296 mimeData->addFile(CbmdosVfs_rfile(vfs, row-1), row-1);
297 }
298 }
299 }
300 if (mimeData->files().empty())
301 {
302 delete mimeData;
303 return 0;
304 }
305 return mimeData;
306 }
307
supportedDropActions() const308 Qt::DropActions CbmdosFsModel::supportedDropActions() const
309 {
310 return Qt::MoveAction|Qt::CopyAction;
311 }
312
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)313 bool CbmdosFsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
314 int row, int column, const QModelIndex &parent)
315 {
316 (void) parent;
317 if (!d->fs) return false;
318 if (CbmdosFs_status(d->fs) & CFS_BROKEN) return false;
319 const CbmdosFileMimeData *fileData =
320 qobject_cast<const CbmdosFileMimeData *>(data);
321 if (fileData)
322 {
323 if (action == Qt::MoveAction)
324 {
325 if (row < 0 || column < 0) return false;
326 if (row < 1) ++row;
327 int to = row - 1;
328 QList<int> fromRows;
329 for (int r = 0; r < rowCount() - 2; ++r) fromRows.append(r);
330 CbmdosVfs *vfs = CbmdosFs_vfs(d->fs);
331 for (int i = 0; i < fileData->filePositions().size(); ++i)
332 {
333 int from = fromRows.at(fileData->filePositions().at(i));
334 if (to > from) --to;
335 CbmdosVfs_move(vfs, to, from);
336 int oldfrom = fromRows.at(to);
337 fromRows.removeAt(to);
338 fromRows.insert(from, oldfrom);
339 ++to;
340 }
341 return true;
342 }
343 if (action == Qt::CopyAction)
344 {
345 if (fileData->files().empty()) return false;
346 for (int i = 0; i < fileData->files().size(); ++i)
347 {
348 CbmdosFile *copy = CbmdosFile_clone(fileData->files().at(i));
349 addFile(createIndex(row+i, column), copy);
350 }
351 return true;
352 }
353 }
354 else if (data->hasUrls())
355 {
356 QList<QUrl> urls = data->urls();
357 int fileno = 0;
358 for (QList<QUrl>::const_iterator i = urls.cbegin();
359 i != urls.cend(); ++i)
360 {
361 if (i->isLocalFile())
362 {
363 QFileInfo file(i->toLocalFile());
364 FILE *fp = qfopen(file.canonicalFilePath(), "rb");
365 if (fp)
366 {
367 CbmdosFile *newFile = CbmdosFile_create();
368 char name[17];
369 QByteArray basename = file.completeBaseName().toUtf8();
370 size_t nameLen = petscii_fromUtf8(name, 17,
371 basename.data(), basename.size(), PC_UPPER, 1, 0);
372 if(--nameLen > 16) nameLen = 16;
373 CbmdosFile_setName(newFile, name, nameLen);
374 QString ext = file.suffix().toLower();
375 if (ext == "seq" || QRegularExpression("^s\\d{2}$")
376 .match(ext).hasMatch())
377 {
378 CbmdosFile_setType(newFile, CbmdosFileType::CFT_SEQ);
379 }
380 else if (ext == "usr" || QRegularExpression("^u\\d{2}$").
381 match(ext).hasMatch())
382 {
383 CbmdosFile_setType(newFile, CbmdosFileType::CFT_USR);
384 }
385 else if (ext == "prg" || QRegularExpression("^p\\d{2}$").
386 match(ext).hasMatch())
387 {
388 CbmdosFile_setType(newFile, CbmdosFileType::CFT_PRG);
389 }
390 else if (ext == "rel" || QRegularExpression("^r\\d{2}$").
391 match(ext).hasMatch())
392 {
393 CbmdosFile_setType(newFile, CbmdosFileType::CFT_REL);
394 }
395 else
396 {
397 CbmdosFile_setType(newFile,
398 cmdr.settings().defaultImportType());
399 }
400 int rc = CbmdosFile_import(newFile, fp);
401 fclose(fp);
402 if (rc < 0)
403 {
404 CbmdosFile_destroy(newFile);
405 break;
406 }
407 addFile(createIndex(row + (fileno++), column), newFile);
408 }
409 }
410 }
411 return fileno > 0;
412 }
413 return false;
414 }
415