]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/filepicker.cpp
86fbe55b410d25cb62cc1776099a5aed63c06a78
[virtualjaguar] / src / gui / filepicker.cpp
1 //
2 // filepicker.cpp - A ROM chooser
3 //
4 // by James L. Hammons
5 // (C) 2010 Underground Software
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  01/22/2010  Created this file
12 // JLH  02/06/2010  Modified to use Qt model/view framework
13 // JLH  03/08/2010  Added large cart view and info text
14 //
15
16 #include "filepicker.h"
17
18 #include "file.h"
19 #include "filedb.h"
20 #include "filelistmodel.h"
21 #include "filethread.h"
22 #include "imagedelegate.h"
23 //#include "settings.h"
24 //#include "types.h"
25
26 /*
27 Our strategy here is like so:
28 Look at the files in the directory pointed to by ROMPath.
29 For each file in the directory, take the CRC32 of it and compare it to the CRC
30 in the romList[]. If there's a match, put it in a list and note it's index value
31 in romList for future reference.
32
33 When constructing the list, use the index to pull up an image of the cart and
34 put that in the list. User picks from a graphical image of the cart.
35
36 Ideally, the label will go into the archive along with the ROM image, but that's
37 for the future...
38 Maybe box art, screenshots will go as well...
39
40 I'm thinking compatibility info should be displayed as well... Just to stop the
41 inevitable stupid questions from people too lazy to do basic research for themselves.
42
43
44 Data strategy:
45
46 - Should keep a QImage of the blank cart with blank label
47 - Should keep a QImage of the blank cart? (For overpainting the ROMs label)
48 - Should we have a special Alpine image? Floppy image (for COF/ABS)?
49
50 - Need some way of keeping track of cart size and compatibility info
51   [compat info needs to be BAD DUMP or % of what works]
52 - Need to properly scale the thumbnails images in the list
53 */
54
55 //could use Window as well...
56 //FilePickerWindow::FilePickerWindow(QWidget * parent/*= 0*/): QWidget(parent, Qt::Dialog)
57 FilePickerWindow::FilePickerWindow(QWidget * parent/*= 0*/): QWidget(parent, Qt::Window),
58         currentFile("")
59 {
60         setWindowTitle(tr("Insert Cartridge..."));
61
62 //is there any reason why this must be cast as a QAbstractListModel? No
63 //Also, need to think about data structure for the model...
64         model = new FileListModel;
65         fileList = new QListView;
66         fileList->setModel(model);
67 //      fileList->setItemDelegate(new ImageDelegate(this));
68         fileList->setItemDelegate(new ImageDelegate());
69 #if 0
70         //nope.
71 //      fileList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
72 //      fileList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
73 //small problem with this is that it doesn't take scrollbar into account...
74 QSize sbSize = fileList->verticalScrollBar()->minimumSizeHint();
75 printf("VSB minimumSizeHint: %u, %u\n", sbSize.width(), sbSize.height());
76 QSize sbSize2 = fileList->verticalScrollBar()->sizeHint();
77 printf("VSB sizeHint: %u, %u\n", sbSize2.width(), sbSize2.height());
78 QRect sbRect = fileList->verticalScrollBar()->normalGeometry();
79 printf("VSB normalGeometry: %u, %u\n", sbRect.width(), sbRect.height());
80 QSize sbSize3 = fileList->verticalScrollBar()->size();
81 printf("VSB size: %u, %u\n", sbSize3.width(), sbSize3.height());
82 //      int sbWidth = fileList->verticalScrollBar()->width();
83         int sbWidth = fileList->verticalScrollBar()->size().width();
84         fileList->setFixedWidth((488/4) + 4 + sbWidth);//ick
85 #else
86         // This sets it to the "too large size" as the minimum!
87         QScrollBar * vsb = new QScrollBar(Qt::Vertical, this);
88         int sbWidth = vsb->size().width();
89         printf("VSB size width: %u\n", sbWidth);
90         int sbWidth2 = vsb->sizeHint().width();
91         printf("VSB sizeHint width: %u\n", sbWidth2);
92         int sbWidth3 = vsb->minimumSize().width();
93         printf("VSB minimum width: %u\n", sbWidth3);
94         int sbWidth4 = vsb->frameSize().width();
95         printf("VSB frame width: %u\n", sbWidth4);
96         delete vsb;
97
98 //      fileList->setFixedWidth((488/4) + 4);
99         int sbWidth5 = fileList->frameWidth();
100         printf("List frame width: %u, (diff=%d)\n", sbWidth5, sbWidth5 - ((488/4) + 4));
101         int sbWidth6 = fileList->sizeHint().width();
102         printf("List sizeHint width: %u\n", sbWidth6);
103         int sbWidth7 = fileList->minimumSize().width();
104         printf("List minimum width: %u\n", sbWidth7);
105         int sbWidth8 = fileList->minimumSizeHint().width();
106         printf("List minimum hint width: %u\n", sbWidth8);
107 //      fileList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
108 //      fileList->verticalScrollBar()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
109         // (488/4) + 4 is the width of the object in the filelistmodel. Dunno why the QListView
110         // isn't picking that up. :-(
111         fileList->setFixedWidth((488/4) + 4 + sbWidth);//ick
112 //      fileList->setFixedWidth((488/4) + 4 + 17 + 4);//sbWidth);//ick
113
114 //      fileList->setSpacing(4);
115         fileList->setUniformItemSizes(true);
116 #endif
117
118 //      QVBoxLayout * layout = new QVBoxLayout;
119         QHBoxLayout * layout = new QHBoxLayout;
120         setLayout(layout);
121         layout->addWidget(fileList);
122
123         // Weird note: This layout has to be added *before* putting anything into it...
124         QVBoxLayout * vLayout = new QVBoxLayout;
125         layout->addLayout(vLayout);
126
127         cartImage = new QLabel;
128         QImage cartImg(":/res/cart-blank.png");
129         QPainter painter(&cartImg);
130         painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
131         painter.end();
132         cartImage->setPixmap(QPixmap::fromImage(cartImg));
133         cartImage->setMargin(4);
134         vLayout->addWidget(cartImage);
135
136         title = new QLabel(QString(tr("<h2>...</h2>")));
137         title->setMargin(6);
138         title->setAlignment(Qt::AlignCenter);
139         vLayout->addWidget(title);
140
141 #if 1
142         QHBoxLayout * dataLayout = new QHBoxLayout;
143         vLayout->addLayout(dataLayout);
144
145         QLabel * labels = new QLabel(QString(tr(
146                 "<b>Type: </b><br>"
147                 "<b>CRC32: </b><br>"
148                 "<b>Compatibility: </b><br>"
149                 "<b>Notes:</b>"
150         )));
151         labels->setAlignment(Qt::AlignRight);
152         labels->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
153         dataLayout->addWidget(labels);
154         data = new QLabel(QString(tr(
155                 "?MB Cartridge<br>"
156                 "????????<br>"
157                 "???<br>"
158                 "???"
159         )));
160         data->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
161         dataLayout->addWidget(data);
162
163 //#warning "!!! Icon size for pushbutton is tiny !!!"
164         insertCart = new QPushButton(this);
165         insertCart->setIconSize(QSize(40, 40));
166         insertCart->setIcon(QIcon(":/res/insert.png"));
167         insertCart->setDefault(true);                           // We want this button to be the default
168         insertCart->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
169         dataLayout->addWidget(insertCart);
170 #else
171         QLabel * text2 = new QLabel(QString(tr(
172                 "<table>"
173                 "<tr><td align='right'><b>Type: </b></td><td>4MB Cartridge</td></tr>"
174                 "<tr><td align='right'><b>CRC32: </b></td><td>FEDCBA98</td></tr>"
175                 "<tr><td align='right'><b>Compatibility: </b></td><td>DOES NOT WORK</td></tr>"
176                 "<tr><td align='right'><b>Notes: </b></td><td>Universal Header detected; Requires DSP</td></tr>"
177                 "</table>"
178         )));
179         vLayout->addWidget(text2);
180 #endif
181
182         fileThread = new FileThread(this);
183 //      connect(fileThread, SIGNAL(FoundAFile(unsigned long)), this, SLOT(AddFileToList(unsigned long)));
184 //      connect(fileThread, SIGNAL(FoundAFile2(unsigned long, QString, QImage *, unsigned long)), this, SLOT(AddFileToList2(unsigned long, QString, QImage *, unsigned long)));
185         connect(fileThread, SIGNAL(FoundAFile3(unsigned long, QString, QImage *,
186                 unsigned long, bool, unsigned long, unsigned long)), this,
187                 SLOT(AddFileToList3(unsigned long, QString, QImage *, unsigned long,
188                 bool, unsigned long, unsigned long)));
189
190 // Let's defer this to the main window, so we can have some control over when this is done.
191 //      fileThread->Go();
192 /*
193 New sizes: 373x172 (label), 420x340 (cart)
194 */
195
196 //      QItemSelectionModel * ism = fileList->selectionModel();
197 //      connect(ism, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(UpdateSelection(const QModelIndex &, const QModelIndex &)));
198         connect(fileList->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(UpdateSelection(const QModelIndex &, const QModelIndex &)));
199
200         connect(insertCart, SIGNAL(clicked()), this, SLOT(LoadButtonPressed()));
201
202         connect(fileList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(CatchDoubleClick(const QModelIndex &)));
203
204 //      connect(fileList, SIGNAL(doubleClicked()), this, SLOT(LoadButtonPressed()));
205 // This returns:
206 // Object::connect: No such signal QListView::QAbstractItemView::doubleClicked() in src/gui/filepicker.cpp:203
207 }
208
209 void FilePickerWindow::keyPressEvent(QKeyEvent * e)
210 {
211         if (e->key() == Qt::Key_Escape)
212                 hide();
213         else if (e->key() == Qt::Key_Return)
214                 LoadButtonPressed();
215 }
216
217 void FilePickerWindow::CatchDoubleClick(const QModelIndex &)
218 {
219         LoadButtonPressed();
220 }
221
222 QString FilePickerWindow::GetSelectedPrettyName(void)
223 {
224         return prettyFilename;
225 }
226
227 void FilePickerWindow::ScanSoftwareFolder(bool allow/*= false*/)
228 {
229         // "allow" is whether or not to allow scanning for unknown software.
230         model->ClearData();
231         fileThread->Go(allow);
232 }
233
234 //
235 // This slot gets called by the FileThread's run() function when it finds a
236 // match in the filesystem to a ROM on our CRC list.
237 //
238 void FilePickerWindow::AddFileToList(unsigned long index)
239 {
240 printf("FilePickerWindow: Found match [%s]...\n", romList[index].name);
241         // NOTE: The model *ignores* what you send it, so this is crap. !!! FIX !!! [DONE, somewhat]
242 //      model->AddData(QIcon(":/res/generic.png"));
243 //      model->AddData(index);
244 }
245
246 void FilePickerWindow::AddFileToList2(unsigned long index, QString str, QImage * img, unsigned long size)
247 {
248 if (index != 0xFFFFFFFF)
249         printf("FilePickerWindow(2): Found match [%s]...\n", romList[index].name);
250
251         if (img)
252         {
253                 model->AddData(index, str, *img, size);
254 //It would be better to pass the pointer into the model though...
255                 delete img;
256         }
257         else
258                 model->AddData(index, str, QImage(), size);
259 }
260
261 void FilePickerWindow::AddFileToList3(unsigned long index, QString str, QImage * img, unsigned long size, bool haveUniversalHeader, unsigned long fileType, unsigned long crc)
262 {
263 //if (index != 0xFFFFFFFF)
264 //      printf("FilePickerWindow(3): Found match [%s]...\n", romList[index].name);
265
266         if (img)
267         {
268                 model->AddData(index, str, *img, size, haveUniversalHeader, fileType, crc);
269 //It would be better to pass the pointer into the model though...
270                 delete img;
271         }
272         else
273                 model->AddData(index, str, QImage(), size, haveUniversalHeader, fileType, crc);
274 }
275
276 void FilePickerWindow::LoadButtonPressed(void)
277 {
278         // TODO: Get the text of the current selection, call the MainWin slot for loading
279         emit(RequestLoad(currentFile));
280         hide();
281 }
282
283 //
284 // This slot gets called when the QListView gets clicked on. Updates
285 // the cart graphic and accompanying text.
286 //
287 void FilePickerWindow::UpdateSelection(const QModelIndex & current, const QModelIndex &/*previous*/)
288 {
289 #if 0
290         QString s = current.model()->data(current, Qt::EditRole).toString();
291         unsigned long i = current.model()->data(current, Qt::DisplayRole).toUInt();
292         QImage label = current.model()->data(current, Qt::DecorationRole).value<QImage>();
293 //      printf("FPW: %s\n", s.toAscii().data());
294         unsigned long fileSize = current.model()->data(current, Qt::WhatsThisRole).toUInt();
295 #else
296 //      QString s = current.model()->data(current, FLM_FILENAME).toString();
297         currentFile = current.model()->data(current, FLM_FILENAME).toString();
298         unsigned long i = current.model()->data(current, FLM_INDEX).toUInt();
299         QImage label = current.model()->data(current, FLM_LABEL).value<QImage>();
300         unsigned long fileSize = current.model()->data(current, FLM_FILESIZE).toUInt();
301         bool haveUniversalHeader = current.model()->data(current, FLM_UNIVERSALHDR).toBool();
302         unsigned long fileType = current.model()->data(current, FLM_FILETYPE).toUInt();
303         uint32 crc = (uint32)current.model()->data(current, FLM_CRC).toUInt();
304 //      printf("FPW: %s\n", s.toAscii().data());
305         bool haveUnknown = (i == 0xFFFFFFFF ? true : false);
306 #endif
307
308         // Disallow loading completely unknown files, but allow all others.
309         insertCart->setEnabled(haveUnknown && (fileType == JST_NONE) ? false : true);
310 //hm.
311 //currentFile = s;
312
313 //373x172 is label size...
314 //365x168 now...
315         if (!label.isNull())
316         {
317 /*
318         QImage cartImg(":/res/cart-blank.png");
319         QPainter painter(&cartImg);
320         painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
321         painter.end();
322         cartSmall = cartImg.scaled(488/4, 395/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
323 */
324                 QImage cart(":/res/cart-blank.png");
325                 QPainter painter(&cart);
326 //Though this should probably be done when this is loaded, instead of every time here...
327 //QImage scaledImg = label.scaled(373, 172, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
328 //painter.drawPixmap(23, 87, QPixmap::fromImage(scaledImg));
329                 // Now, looks like it is...
330 //              painter.drawPixmap(23, 87, QPixmap::fromImage(label));
331                 painter.drawPixmap(27, 89, QPixmap::fromImage(label));
332 //              painter.drawPixmap(23, 87, 373, 172, QPixmap::fromImage(label));
333
334 // Well, heck. This should be done to the label *before* we get here.
335                 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/upper-left.png")));
336                 painter.drawPixmap(27+355, 89, QPixmap::fromImage(QImage(":/res/upper-right.png")));
337
338                 painter.end();
339                 cartImage->setPixmap(QPixmap::fromImage(cart));
340         }
341         else
342         {
343                 // We should try to be intelligent with our updates here, and only redraw when
344                 // we're going from a selection with a label to a selection without. Now, we
345                 // redraw regardless.
346                 QImage cart;
347
348 // We now have to sources of data for the passed in files:
349 // - The file DB
350 // - The file type detection
351 // This means we have to be mindful of what's passed back by that stuff.
352 // We can assume that if it wasn't found in the DB, then the fileType
353 // should be valid.
354 // The DB takes precedence over the fileType.
355                 if ((!haveUnknown && (romList[i].flags & FF_ROM))
356                         || (haveUnknown && (fileType == JST_ROM)))
357                 {
358                         cart = QImage(":/res/cart-blank.png");
359                         QPainter painter(&cart);
360                         painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/label-blank.png")));
361                         painter.end();
362                 }
363                 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE))
364                         || (haveUnknown && (fileType == JST_ALPINE)))
365                 {
366                         cart = QImage(":/res/alpine-file.png");
367                 }
368                 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2 || fileType == JST_JAGSERVER))
369                 {
370                         cart = QImage(":/res/homebrew-file.png");
371                 }
372                 else
373                         cart = QImage(":/res/unknown-file.png");
374
375                 cartImage->setPixmap(QPixmap::fromImage(cart));
376         }
377
378 //1048576
379 //2097152
380 //4194304
381         if (!haveUnknown)
382                 prettyFilename = romList[i].name;
383         else
384         {
385                 int lastSlashPos = currentFile.lastIndexOf('/');
386                 prettyFilename = "\"" + currentFile.mid(lastSlashPos + 1) + "\"";
387         }
388
389         title->setText(QString("<h2>%1</h2>").arg(prettyFilename));
390
391 //Kludge for now, we'll have to fix this later...
392 // So let's fix it now!
393         QString fileTypeString, crcString, notes, compatibility;
394
395 #if 0
396         if (!haveUnknown)
397         {
398                 if (romList[i].flags & FF_ROM)
399                         fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
400                 else if (romList[i].flags & FF_ALPINE)
401                         fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
402                 else
403                         fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
404         }
405 #else
406         if ((!haveUnknown && (romList[i].flags & FF_ROM)) || (haveUnknown && (fileType == JST_ROM)))
407                 fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
408         else if ((!haveUnknown && (romList[i].flags & FF_ALPINE)) || (haveUnknown && (fileType == JST_ALPINE)))
409                 fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
410         else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2))
411                 fileTypeString = QString(tr("ABS/COF Executable (%1 bytes)")).arg(fileSize);
412         else if (haveUnknown && (fileType == JST_JAGSERVER))
413                 fileTypeString = QString(tr("Jaguar Server Executable (%1 bytes)")).arg(fileSize);
414         else
415                 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
416 #endif
417
418 //      crcString = QString("%1").arg(romList[i].crc32, 8, 16, QChar('0')).toUpper();
419         crcString = QString("%1").arg(crc, 8, 16, QChar('0')).toUpper();
420
421         if (!haveUnknown && (romList[i].flags & FF_NON_WORKING))
422                 compatibility = "DOES NOT WORK";
423         else
424                 compatibility = "Unknown";
425
426         // This is going to need some formatting love before long...
427         if (!haveUnknown && (romList[i].flags & FF_BAD_DUMP))
428                 notes = "<b>BAD DUMP</b>";
429
430         if (haveUniversalHeader)
431                 notes += " Universal Header detected";
432
433         if (!haveUnknown && (romList[i].flags & FF_REQ_DSP))
434                 notes += " Requires DSP";
435
436         if (!haveUnknown && (romList[i].flags & FF_VERIFIED))
437                 notes += " <i>(Verified)</i>";
438
439         data->setText(QString("%1<br>%2<br>%3<br>%4")
440                 .arg(fileTypeString).arg(crcString).arg(compatibility).arg(notes));
441 }
442
443 /*
444     Super Duper Awesome Guy (World)
445
446          Type: 4MB Cartridge
447         CRC32: FEDCBA98
448 Compatibility: DOES NOT WORK
449         Notes: Universal Header detected; Requires DSP
450
451
452     Stupid Homebrew Game That Sux
453
454          Type: ABS/COF Executable (43853 bytes)
455         CRC32: 76543210
456 Compatibility: Unknown
457         Notes: $4000 Load, $4000 Run
458
459
460     Action Hopscotch Plus (Prototype)
461
462          Type: 2MB Alpine ROM
463         CRC32: 44889921
464 Compatibility: 80% (or ****)
465         Notes: EEPROM available
466 */