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()));
202 connect(fileList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(CatchDoubleClick(const QModelIndex &)));
204 // connect(fileList, SIGNAL(doubleClicked()), this, SLOT(LoadButtonPressed()));
206 // Object::connect: No such signal QListView::QAbstractItemView::doubleClicked() in src/gui/filepicker.cpp:203
209 void FilePickerWindow::keyPressEvent(QKeyEvent * e)
211 if (e->key() == Qt::Key_Escape)
213 else if (e->key() == Qt::Key_Return)
217 void FilePickerWindow::CatchDoubleClick(const QModelIndex &)
222 QString FilePickerWindow::GetSelectedPrettyName(void)
224 return prettyFilename;
227 void FilePickerWindow::ScanSoftwareFolder(bool allow/*= false*/)
229 // "allow" is whether or not to allow scanning for unknown software.
231 fileThread->Go(allow);
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.
238 void FilePickerWindow::AddFileToList(unsigned long index)
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);
246 void FilePickerWindow::AddFileToList2(unsigned long index, QString str, QImage * img, unsigned long size)
248 if (index != 0xFFFFFFFF)
249 printf("FilePickerWindow(2): Found match [%s]...\n", romList[index].name);
253 model->AddData(index, str, *img, size);
254 //It would be better to pass the pointer into the model though...
258 model->AddData(index, str, QImage(), size);
261 void FilePickerWindow::AddFileToList3(unsigned long index, QString str, QImage * img, unsigned long size, bool haveUniversalHeader, unsigned long fileType, unsigned long crc)
263 //if (index != 0xFFFFFFFF)
264 // printf("FilePickerWindow(3): Found match [%s]...\n", romList[index].name);
268 model->AddData(index, str, *img, size, haveUniversalHeader, fileType, crc);
269 //It would be better to pass the pointer into the model though...
273 model->AddData(index, str, QImage(), size, haveUniversalHeader, fileType, crc);
276 void FilePickerWindow::LoadButtonPressed(void)
278 // TODO: Get the text of the current selection, call the MainWin slot for loading
279 emit(RequestLoad(currentFile));
284 // This slot gets called when the QListView gets clicked on. Updates
285 // the cart graphic and accompanying text.
287 void FilePickerWindow::UpdateSelection(const QModelIndex & current, const QModelIndex &/*previous*/)
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();
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);
308 // Disallow loading completely unknown files, but allow all others.
309 insertCart->setEnabled(haveUnknown && (fileType == JST_NONE) ? false : true);
313 //373x172 is label size...
318 QImage cartImg(":/res/cart-blank.png");
319 QPainter painter(&cartImg);
320 painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
322 cartSmall = cartImg.scaled(488/4, 395/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
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));
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")));
339 cartImage->setPixmap(QPixmap::fromImage(cart));
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.
348 // We now have to sources of data for the passed in files:
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
354 // The DB takes precedence over the fileType.
355 if ((!haveUnknown && (romList[i].flags & FF_ROM))
356 || (haveUnknown && (fileType == JST_ROM)))
358 cart = QImage(":/res/cart-blank.png");
359 QPainter painter(&cart);
360 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/label-blank.png")));
363 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE))
364 || (haveUnknown && (fileType == JST_ALPINE)))
366 cart = QImage(":/res/alpine-file.png");
368 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2 || fileType == JST_JAGSERVER))
370 cart = QImage(":/res/homebrew-file.png");
373 cart = QImage(":/res/unknown-file.png");
375 cartImage->setPixmap(QPixmap::fromImage(cart));
382 prettyFilename = romList[i].name;
385 int lastSlashPos = currentFile.lastIndexOf('/');
386 prettyFilename = "\"" + currentFile.mid(lastSlashPos + 1) + "\"";
389 title->setText(QString("<h2>%1</h2>").arg(prettyFilename));
391 //Kludge for now, we'll have to fix this later...
392 // So let's fix it now!
393 QString fileTypeString, crcString, notes, compatibility;
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);
403 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
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);
415 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
418 // crcString = QString("%1").arg(romList[i].crc32, 8, 16, QChar('0')).toUpper();
419 crcString = QString("%1").arg(crc, 8, 16, QChar('0')).toUpper();
421 if (!haveUnknown && (romList[i].flags & FF_NON_WORKING))
422 compatibility = "DOES NOT WORK";
424 compatibility = "Unknown";
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>";
430 if (haveUniversalHeader)
431 notes += " Universal Header detected";
433 if (!haveUnknown && (romList[i].flags & FF_REQ_DSP))
434 notes += " Requires DSP";
436 if (!haveUnknown && (romList[i].flags & FF_VERIFIED))
437 notes += " <i>(Verified)</i>";
439 data->setText(QString("%1<br>%2<br>%3<br>%4")
440 .arg(fileTypeString).arg(crcString).arg(compatibility).arg(notes));
444 Super Duper Awesome Guy (World)
448 Compatibility: DOES NOT WORK
449 Notes: Universal Header detected; Requires DSP
452 Stupid Homebrew Game That Sux
454 Type: ABS/COF Executable (43853 bytes)
456 Compatibility: Unknown
457 Notes: $4000 Load, $4000 Run
460 Action Hopscotch Plus (Prototype)
464 Compatibility: 80% (or ****)
465 Notes: EEPROM available