]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/filepicker.cpp
Fixed 24BPP mode (used R twice instead of B), fixed problem with Object
[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
203 void FilePickerWindow::keyPressEvent(QKeyEvent * e)
204 {
205         if (e->key() == Qt::Key_Escape)
206                 hide();
207         else if (e->key() == Qt::Key_Return)
208                 LoadButtonPressed();
209 }
210
211 QString FilePickerWindow::GetSelectedPrettyName(void)
212 {
213         return prettyFilename;
214 }
215
216 void FilePickerWindow::ScanSoftwareFolder(bool allow/*= false*/)
217 {
218         // "allow" is whether or not to allow scanning for unknown software.
219         model->ClearData();
220         fileThread->Go(allow);
221 }
222
223 //
224 // This slot gets called by the FileThread's run() function when it finds a
225 // match in the filesystem to a ROM on our CRC list.
226 //
227 void FilePickerWindow::AddFileToList(unsigned long index)
228 {
229 printf("FilePickerWindow: Found match [%s]...\n", romList[index].name);
230         // NOTE: The model *ignores* what you send it, so this is crap. !!! FIX !!! [DONE, somewhat]
231 //      model->AddData(QIcon(":/res/generic.png"));
232 //      model->AddData(index);
233 }
234
235 void FilePickerWindow::AddFileToList2(unsigned long index, QString str, QImage * img, unsigned long size)
236 {
237 if (index != 0xFFFFFFFF)
238         printf("FilePickerWindow(2): Found match [%s]...\n", romList[index].name);
239
240         if (img)
241         {
242                 model->AddData(index, str, *img, size);
243 //It would be better to pass the pointer into the model though...
244                 delete img;
245         }
246         else
247                 model->AddData(index, str, QImage(), size);
248 }
249
250 void FilePickerWindow::AddFileToList3(unsigned long index, QString str, QImage * img, unsigned long size, bool haveUniversalHeader, unsigned long fileType, unsigned long crc)
251 {
252 //if (index != 0xFFFFFFFF)
253 //      printf("FilePickerWindow(3): Found match [%s]...\n", romList[index].name);
254
255         if (img)
256         {
257                 model->AddData(index, str, *img, size, haveUniversalHeader, fileType, crc);
258 //It would be better to pass the pointer into the model though...
259                 delete img;
260         }
261         else
262                 model->AddData(index, str, QImage(), size, haveUniversalHeader, fileType, crc);
263 }
264
265 void FilePickerWindow::LoadButtonPressed(void)
266 {
267         // TODO: Get the text of the current selection, call the MainWin slot for loading
268         emit(RequestLoad(currentFile));
269         hide();
270 }
271
272 //
273 // This slot gets called when the QListView gets clicked on. Updates
274 // the cart graphic and accompanying text.
275 //
276 void FilePickerWindow::UpdateSelection(const QModelIndex & current, const QModelIndex &/*previous*/)
277 {
278 #if 0
279         QString s = current.model()->data(current, Qt::EditRole).toString();
280         unsigned long i = current.model()->data(current, Qt::DisplayRole).toUInt();
281         QImage label = current.model()->data(current, Qt::DecorationRole).value<QImage>();
282 //      printf("FPW: %s\n", s.toAscii().data());
283         unsigned long fileSize = current.model()->data(current, Qt::WhatsThisRole).toUInt();
284 #else
285 //      QString s = current.model()->data(current, FLM_FILENAME).toString();
286         currentFile = current.model()->data(current, FLM_FILENAME).toString();
287         unsigned long i = current.model()->data(current, FLM_INDEX).toUInt();
288         QImage label = current.model()->data(current, FLM_LABEL).value<QImage>();
289         unsigned long fileSize = current.model()->data(current, FLM_FILESIZE).toUInt();
290         bool haveUniversalHeader = current.model()->data(current, FLM_UNIVERSALHDR).toBool();
291         unsigned long fileType = current.model()->data(current, FLM_FILETYPE).toUInt();
292         uint32 crc = (uint32)current.model()->data(current, FLM_CRC).toUInt();
293 //      printf("FPW: %s\n", s.toAscii().data());
294         bool haveUnknown = (i == 0xFFFFFFFF ? true : false);
295 #endif
296
297         // Disallow loading completely unknown files, but allow all others.
298         insertCart->setEnabled(haveUnknown && (fileType == JST_NONE) ? false : true);
299 //hm.
300 //currentFile = s;
301
302 //373x172 is label size...
303 //365x168 now...
304         if (!label.isNull())
305         {
306 /*
307         QImage cartImg(":/res/cart-blank.png");
308         QPainter painter(&cartImg);
309         painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
310         painter.end();
311         cartSmall = cartImg.scaled(488/4, 395/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
312 */
313                 QImage cart(":/res/cart-blank.png");
314                 QPainter painter(&cart);
315 //Though this should probably be done when this is loaded, instead of every time here...
316 //QImage scaledImg = label.scaled(373, 172, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
317 //painter.drawPixmap(23, 87, QPixmap::fromImage(scaledImg));
318                 // Now, looks like it is...
319 //              painter.drawPixmap(23, 87, QPixmap::fromImage(label));
320                 painter.drawPixmap(27, 89, QPixmap::fromImage(label));
321 //              painter.drawPixmap(23, 87, 373, 172, QPixmap::fromImage(label));
322
323 // Well, heck. This should be done to the label *before* we get here.
324                 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/upper-left.png")));
325                 painter.drawPixmap(27+355, 89, QPixmap::fromImage(QImage(":/res/upper-right.png")));
326
327                 painter.end();
328                 cartImage->setPixmap(QPixmap::fromImage(cart));
329         }
330         else
331         {
332                 // We should try to be intelligent with our updates here, and only redraw when
333                 // we're going from a selection with a label to a selection without. Now, we
334                 // redraw regardless.
335                 QImage cart;
336
337 // We now have to sources of data for the passed in files:
338 // - The file DB
339 // - The file type detection
340 // This means we have to be mindful of what's passed back by that stuff.
341 // We can assume that if it wasn't found in the DB, then the fileType
342 // should be valid.
343 // The DB takes precedence over the fileType.
344                 if ((!haveUnknown && (romList[i].flags & FF_ROM))
345                         || (haveUnknown && (fileType == JST_ROM)))
346                 {
347                         cart = QImage(":/res/cart-blank.png");
348                         QPainter painter(&cart);
349                         painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/label-blank.png")));
350                         painter.end();
351                 }
352                 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE))
353                         || (haveUnknown && (fileType == JST_ALPINE)))
354                 {
355                         cart = QImage(":/res/alpine-file.png");
356                 }
357                 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2 || fileType == JST_JAGSERVER))
358                 {
359                         cart = QImage(":/res/homebrew-file.png");
360                 }
361                 else
362                         cart = QImage(":/res/unknown-file.png");
363
364                 cartImage->setPixmap(QPixmap::fromImage(cart));
365         }
366
367 //1048576
368 //2097152
369 //4194304
370         if (!haveUnknown)
371                 prettyFilename = romList[i].name;
372         else
373         {
374                 int lastSlashPos = currentFile.lastIndexOf('/');
375                 prettyFilename = "\"" + currentFile.mid(lastSlashPos + 1) + "\"";
376         }
377
378         title->setText(QString("<h2>%1</h2>").arg(prettyFilename));
379
380 //Kludge for now, we'll have to fix this later...
381 // So let's fix it now!
382         QString fileTypeString, crcString, notes, compatibility;
383
384 #if 0
385         if (!haveUnknown)
386         {
387                 if (romList[i].flags & FF_ROM)
388                         fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
389                 else if (romList[i].flags & FF_ALPINE)
390                         fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
391                 else
392                         fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
393         }
394 #else
395         if ((!haveUnknown && (romList[i].flags & FF_ROM)) || (haveUnknown && (fileType == JST_ROM)))
396                 fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
397         else if ((!haveUnknown && (romList[i].flags & FF_ALPINE)) || (haveUnknown && (fileType == JST_ALPINE)))
398                 fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
399         else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2))
400                 fileTypeString = QString(tr("ABS/COF Executable (%1 bytes)")).arg(fileSize);
401         else if (haveUnknown && (fileType == JST_JAGSERVER))
402                 fileTypeString = QString(tr("Jaguar Server Executable (%1 bytes)")).arg(fileSize);
403         else
404                 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
405 #endif
406
407 //      crcString = QString("%1").arg(romList[i].crc32, 8, 16, QChar('0')).toUpper();
408         crcString = QString("%1").arg(crc, 8, 16, QChar('0')).toUpper();
409
410         if (!haveUnknown && (romList[i].flags & FF_NON_WORKING))
411                 compatibility = "DOES NOT WORK";
412         else
413                 compatibility = "Unknown";
414
415         // This is going to need some formatting love before long...
416         if (!haveUnknown && (romList[i].flags & FF_BAD_DUMP))
417                 notes = "<b>BAD DUMP</b>";
418
419         if (haveUniversalHeader)
420                 notes += " Universal Header detected";
421
422         if (!haveUnknown && (romList[i].flags & FF_REQ_DSP))
423                 notes += " Requires DSP";
424
425         if (!haveUnknown && (romList[i].flags & FF_VERIFIED))
426                 notes += " <i>(Verified)</i>";
427
428         data->setText(QString("%1<br>%2<br>%3<br>%4")
429                 .arg(fileTypeString).arg(crcString).arg(compatibility).arg(notes));
430 }
431
432 /*
433     Super Duper Awesome Guy (World)
434
435          Type: 4MB Cartridge
436         CRC32: FEDCBA98
437 Compatibility: DOES NOT WORK
438         Notes: Universal Header detected; Requires DSP
439
440
441     Stupid Homebrew Game That Sux
442
443          Type: ABS/COF Executable (43853 bytes)
444         CRC32: 76543210
445 Compatibility: Unknown
446         Notes: $4000 Load, $4000 Run
447
448
449     Action Hopscotch Plus (Prototype)
450
451          Type: 2MB Alpine ROM
452         CRC32: 44889921
453 Compatibility: 80% (or ****)
454         Notes: EEPROM available
455 */