2 // filepicker.cpp - A ROM chooser
5 // (C) 2010 Underground Software
7 // JLH = James L. Hammons <jlhamm@acm.org>
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
16 #include "filepicker.h"
20 #include "filelistmodel.h"
21 #include "filethread.h"
22 #include "imagedelegate.h"
23 //#include "settings.h"
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.
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.
36 Ideally, the label will go into the archive along with the ROM image, but that's
38 Maybe box art, screenshots will go as well...
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.
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)?
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
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),
60 setWindowTitle(tr("Insert Cartridge..."));
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());
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
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);
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
114 // fileList->setSpacing(4);
115 fileList->setUniformItemSizes(true);
118 // QVBoxLayout * layout = new QVBoxLayout;
119 QHBoxLayout * layout = new QHBoxLayout;
121 layout->addWidget(fileList);
123 // Weird note: This layout has to be added *before* putting anything into it...
124 QVBoxLayout * vLayout = new QVBoxLayout;
125 layout->addLayout(vLayout);
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"));
132 cartImage->setPixmap(QPixmap::fromImage(cartImg));
133 cartImage->setMargin(4);
134 vLayout->addWidget(cartImage);
136 title = new QLabel(QString(tr("<h2>...</h2>")));
138 title->setAlignment(Qt::AlignCenter);
139 vLayout->addWidget(title);
142 QHBoxLayout * dataLayout = new QHBoxLayout;
143 vLayout->addLayout(dataLayout);
145 QLabel * labels = new QLabel(QString(tr(
148 "<b>Compatibility: </b><br>"
151 labels->setAlignment(Qt::AlignRight);
152 labels->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
153 dataLayout->addWidget(labels);
154 data = new QLabel(QString(tr(
160 data->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
161 dataLayout->addWidget(data);
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);
171 QLabel * text2 = new QLabel(QString(tr(
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>"
179 vLayout->addWidget(text2);
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)));
190 // Let's defer this to the main window, so we can have some control over when this is done.
193 New sizes: 373x172 (label), 420x340 (cart)
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 &)));
200 connect(insertCart, SIGNAL(clicked()), this, SLOT(LoadButtonPressed()));
203 void FilePickerWindow::keyPressEvent(QKeyEvent * e)
205 if (e->key() == Qt::Key_Escape)
207 else if (e->key() == Qt::Key_Return)
211 QString FilePickerWindow::GetSelectedPrettyName(void)
213 return prettyFilename;
216 void FilePickerWindow::ScanSoftwareFolder(bool allow/*= false*/)
218 // "allow" is whether or not to allow scanning for unknown software.
220 fileThread->Go(allow);
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.
227 void FilePickerWindow::AddFileToList(unsigned long index)
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);
235 void FilePickerWindow::AddFileToList2(unsigned long index, QString str, QImage * img, unsigned long size)
237 if (index != 0xFFFFFFFF)
238 printf("FilePickerWindow(2): Found match [%s]...\n", romList[index].name);
242 model->AddData(index, str, *img, size);
243 //It would be better to pass the pointer into the model though...
247 model->AddData(index, str, QImage(), size);
250 void FilePickerWindow::AddFileToList3(unsigned long index, QString str, QImage * img, unsigned long size, bool haveUniversalHeader, unsigned long fileType, unsigned long crc)
252 //if (index != 0xFFFFFFFF)
253 // printf("FilePickerWindow(3): Found match [%s]...\n", romList[index].name);
257 model->AddData(index, str, *img, size, haveUniversalHeader, fileType, crc);
258 //It would be better to pass the pointer into the model though...
262 model->AddData(index, str, QImage(), size, haveUniversalHeader, fileType, crc);
265 void FilePickerWindow::LoadButtonPressed(void)
267 // TODO: Get the text of the current selection, call the MainWin slot for loading
268 emit(RequestLoad(currentFile));
273 // This slot gets called when the QListView gets clicked on. Updates
274 // the cart graphic and accompanying text.
276 void FilePickerWindow::UpdateSelection(const QModelIndex & current, const QModelIndex &/*previous*/)
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();
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);
297 // Disallow loading completely unknown files, but allow all others.
298 insertCart->setEnabled(haveUnknown && (fileType == JST_NONE) ? false : true);
302 //373x172 is label size...
307 QImage cartImg(":/res/cart-blank.png");
308 QPainter painter(&cartImg);
309 painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
311 cartSmall = cartImg.scaled(488/4, 395/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
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));
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")));
328 cartImage->setPixmap(QPixmap::fromImage(cart));
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.
337 // We now have to sources of data for the passed in files:
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
343 // The DB takes precedence over the fileType.
344 if ((!haveUnknown && (romList[i].flags & FF_ROM))
345 || (haveUnknown && (fileType == JST_ROM)))
347 cart = QImage(":/res/cart-blank.png");
348 QPainter painter(&cart);
349 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/label-blank.png")));
352 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE))
353 || (haveUnknown && (fileType == JST_ALPINE)))
355 cart = QImage(":/res/alpine-file.png");
357 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2 || fileType == JST_JAGSERVER))
359 cart = QImage(":/res/homebrew-file.png");
362 cart = QImage(":/res/unknown-file.png");
364 cartImage->setPixmap(QPixmap::fromImage(cart));
371 prettyFilename = romList[i].name;
374 int lastSlashPos = currentFile.lastIndexOf('/');
375 prettyFilename = "\"" + currentFile.mid(lastSlashPos + 1) + "\"";
378 title->setText(QString("<h2>%1</h2>").arg(prettyFilename));
380 //Kludge for now, we'll have to fix this later...
381 // So let's fix it now!
382 QString fileTypeString, crcString, notes, compatibility;
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);
392 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
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);
404 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
407 // crcString = QString("%1").arg(romList[i].crc32, 8, 16, QChar('0')).toUpper();
408 crcString = QString("%1").arg(crc, 8, 16, QChar('0')).toUpper();
410 if (!haveUnknown && (romList[i].flags & FF_NON_WORKING))
411 compatibility = "DOES NOT WORK";
413 compatibility = "Unknown";
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>";
419 if (haveUniversalHeader)
420 notes += " Universal Header detected";
422 if (!haveUnknown && (romList[i].flags & FF_REQ_DSP))
423 notes += " Requires DSP";
425 if (!haveUnknown && (romList[i].flags & FF_VERIFIED))
426 notes += " <i>(Verified)</i>";
428 data->setText(QString("%1<br>%2<br>%3<br>%4")
429 .arg(fileTypeString).arg(crcString).arg(compatibility).arg(notes));
433 Super Duper Awesome Guy (World)
437 Compatibility: DOES NOT WORK
438 Notes: Universal Header detected; Requires DSP
441 Stupid Homebrew Game That Sux
443 Type: ABS/COF Executable (43853 bytes)
445 Compatibility: Unknown
446 Notes: $4000 Load, $4000 Run
449 Action Hopscotch Plus (Prototype)
453 Compatibility: 80% (or ****)
454 Notes: EEPROM available