]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/filepicker.cpp
06e517d0e2d5235c4a014d6c46f5c46aa0c39c5f
[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         delete vsb;
93
94 //      fileList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
95 //      fileList->verticalScrollBar()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
96         fileList->setFixedWidth((488/4) + 4 + sbWidth);//ick
97 #endif
98
99 //      QVBoxLayout * layout = new QVBoxLayout;
100         QHBoxLayout * layout = new QHBoxLayout;
101         setLayout(layout);
102         layout->addWidget(fileList);
103
104         // Weird note: This layout has to be added *before* putting anything into it...
105         QVBoxLayout * vLayout = new QVBoxLayout;
106         layout->addLayout(vLayout);
107
108         cartImage = new QLabel;
109         QImage cartImg(":/res/cart-blank.png");
110         QPainter painter(&cartImg);
111         painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
112         painter.end();
113         cartImage->setPixmap(QPixmap::fromImage(cartImg));
114         cartImage->setMargin(4);
115         vLayout->addWidget(cartImage);
116
117         title = new QLabel(QString(tr("<h2>...</h2>")));
118         title->setMargin(6);
119         title->setAlignment(Qt::AlignCenter);
120         vLayout->addWidget(title);
121
122 #if 1
123         QHBoxLayout * dataLayout = new QHBoxLayout;
124         vLayout->addLayout(dataLayout);
125
126         QLabel * labels = new QLabel(QString(tr(
127                 "<b>Type: </b><br>"
128                 "<b>CRC32: </b><br>"
129                 "<b>Compatibility: </b><br>"
130                 "<b>Notes:</b>"
131         )));
132         labels->setAlignment(Qt::AlignRight);
133         labels->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
134         dataLayout->addWidget(labels);
135         data = new QLabel(QString(tr(
136                 "?MB Cartridge<br>"
137                 "????????<br>"
138                 "???<br>"
139                 "???"
140         )));
141         data->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
142         dataLayout->addWidget(data);
143
144 //#warning "!!! Icon size for pushbutton is tiny !!!"
145         insertCart = new QPushButton(this);
146         insertCart->setIconSize(QSize(40, 40));
147         insertCart->setIcon(QIcon(":/res/insert.png"));
148         insertCart->setDefault(true);                           // We want this button to be the default
149         insertCart->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
150         dataLayout->addWidget(insertCart);
151 #else
152         QLabel * text2 = new QLabel(QString(tr(
153                 "<table>"
154                 "<tr><td align='right'><b>Type: </b></td><td>4MB Cartridge</td></tr>"
155                 "<tr><td align='right'><b>CRC32: </b></td><td>FEDCBA98</td></tr>"
156                 "<tr><td align='right'><b>Compatibility: </b></td><td>DOES NOT WORK</td></tr>"
157                 "<tr><td align='right'><b>Notes: </b></td><td>Universal Header detected; Requires DSP</td></tr>"
158                 "</table>"
159         )));
160         vLayout->addWidget(text2);
161 #endif
162
163         fileThread = new FileThread(this);
164 //      connect(fileThread, SIGNAL(FoundAFile(unsigned long)), this, SLOT(AddFileToList(unsigned long)));
165 //      connect(fileThread, SIGNAL(FoundAFile2(unsigned long, QString, QImage *, unsigned long)), this, SLOT(AddFileToList2(unsigned long, QString, QImage *, unsigned long)));
166         connect(fileThread, SIGNAL(FoundAFile3(unsigned long, QString, QImage *,
167                 unsigned long, bool, unsigned long, unsigned long)), this,
168                 SLOT(AddFileToList3(unsigned long, QString, QImage *, unsigned long,
169                 bool, unsigned long, unsigned long)));
170
171 // Let's defer this to the main window, so we can have some control over when this is done.
172 //      fileThread->Go();
173 /*
174 New sizes: 373x172 (label), 420x340 (cart)
175 */
176
177 //      QItemSelectionModel * ism = fileList->selectionModel();
178 //      connect(ism, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(UpdateSelection(const QModelIndex &, const QModelIndex &)));
179         connect(fileList->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(UpdateSelection(const QModelIndex &, const QModelIndex &)));
180
181         connect(insertCart, SIGNAL(clicked()), this, SLOT(LoadButtonPressed()));
182 }
183
184 QString FilePickerWindow::GetSelectedPrettyName(void)
185 {
186         return prettyFilename;
187 }
188
189 void FilePickerWindow::ScanSoftwareFolder(bool allow/*= false*/)
190 {
191         // "allow" is whether or not to allow scanning for unknown software.
192         model->ClearData();
193         fileThread->Go(allow);
194 }
195
196 //
197 // This slot gets called by the FileThread's run() function when it finds a
198 // match in the filesystem to a ROM on our CRC list.
199 //
200 void FilePickerWindow::AddFileToList(unsigned long index)
201 {
202 printf("FilePickerWindow: Found match [%s]...\n", romList[index].name);
203         // NOTE: The model *ignores* what you send it, so this is crap. !!! FIX !!! [DONE, somewhat]
204 //      model->AddData(QIcon(":/res/generic.png"));
205 //      model->AddData(index);
206 }
207
208 void FilePickerWindow::AddFileToList2(unsigned long index, QString str, QImage * img, unsigned long size)
209 {
210 if (index != 0xFFFFFFFF)
211         printf("FilePickerWindow(2): Found match [%s]...\n", romList[index].name);
212
213         if (img)
214         {
215                 model->AddData(index, str, *img, size);
216 //It would be better to pass the pointer into the model though...
217                 delete img;
218         }
219         else
220                 model->AddData(index, str, QImage(), size);
221 }
222
223 void FilePickerWindow::AddFileToList3(unsigned long index, QString str, QImage * img, unsigned long size, bool haveUniversalHeader, unsigned long fileType, unsigned long crc)
224 {
225 if (index != 0xFFFFFFFF)
226         printf("FilePickerWindow(3): Found match [%s]...\n", romList[index].name);
227
228         if (img)
229         {
230                 model->AddData(index, str, *img, size, haveUniversalHeader, fileType, crc);
231 //It would be better to pass the pointer into the model though...
232                 delete img;
233         }
234         else
235                 model->AddData(index, str, QImage(), size, haveUniversalHeader, fileType, crc);
236 }
237
238 void FilePickerWindow::LoadButtonPressed(void)
239 {
240         // TODO: Get the text of the current selection, call the MainWin slot for loading
241         emit(RequestLoad(currentFile));
242         this->hide();
243 }
244
245 //
246 // This slot gets called when the QListView gets clicked on. Updates
247 // the cart graphic and accompanying text.
248 //
249 void FilePickerWindow::UpdateSelection(const QModelIndex & current, const QModelIndex &/*previous*/)
250 {
251 #if 0
252         QString s = current.model()->data(current, Qt::EditRole).toString();
253         unsigned long i = current.model()->data(current, Qt::DisplayRole).toUInt();
254         QImage label = current.model()->data(current, Qt::DecorationRole).value<QImage>();
255 //      printf("FPW: %s\n", s.toAscii().data());
256         unsigned long fileSize = current.model()->data(current, Qt::WhatsThisRole).toUInt();
257 #else
258 //      QString s = current.model()->data(current, FLM_FILENAME).toString();
259         currentFile = current.model()->data(current, FLM_FILENAME).toString();
260         unsigned long i = current.model()->data(current, FLM_INDEX).toUInt();
261         QImage label = current.model()->data(current, FLM_LABEL).value<QImage>();
262         unsigned long fileSize = current.model()->data(current, FLM_FILESIZE).toUInt();
263         bool haveUniversalHeader = current.model()->data(current, FLM_UNIVERSALHDR).toBool();
264         unsigned long fileType = current.model()->data(current, FLM_FILETYPE).toUInt();
265         uint32 crc = (uint32)current.model()->data(current, FLM_CRC).toUInt();
266 //      printf("FPW: %s\n", s.toAscii().data());
267         bool haveUnknown = (i == 0xFFFFFFFF ? true : false);
268 #endif
269
270         // Disallow loading completely unknown files, but allow all others.
271         insertCart->setEnabled(haveUnknown && (fileType == JST_NONE) ? false : true);
272 //hm.
273 //currentFile = s;
274
275 //373x172 is label size...
276 //365x168 now...
277         if (!label.isNull())
278         {
279 /*
280         QImage cartImg(":/res/cart-blank.png");
281         QPainter painter(&cartImg);
282         painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
283         painter.end();
284         cartSmall = cartImg.scaled(488/4, 395/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
285 */
286                 QImage cart(":/res/cart-blank.png");
287                 QPainter painter(&cart);
288 //Though this should probably be done when this is loaded, instead of every time here...
289 //QImage scaledImg = label.scaled(373, 172, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
290 //painter.drawPixmap(23, 87, QPixmap::fromImage(scaledImg));
291                 // Now, looks like it is...
292 //              painter.drawPixmap(23, 87, QPixmap::fromImage(label));
293                 painter.drawPixmap(27, 89, QPixmap::fromImage(label));
294 //              painter.drawPixmap(23, 87, 373, 172, QPixmap::fromImage(label));
295
296 // Well, heck. This should be done to the label *before* we get here.
297                 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/upper-left.png")));
298                 painter.drawPixmap(27+355, 89, QPixmap::fromImage(QImage(":/res/upper-right.png")));
299
300                 painter.end();
301                 cartImage->setPixmap(QPixmap::fromImage(cart));
302         }
303         else
304         {
305                 // We should try to be intelligent with our updates here, and only redraw when
306                 // we're going from a selection with a label to a selection without. Now, we
307                 // redraw regardless.
308                 QImage cart;
309
310 // We now have to sources of data for the passed in files:
311 // - The file DB
312 // - The file type detection
313 // This means we have to be mindful of what's passed back by that stuff.
314 // We can assume that if it wasn't found in the DB, then the fileType
315 // should be valid.
316 // The DB takes precedence over the fileType.
317                 if ((!haveUnknown && (romList[i].flags & FF_ROM))
318                         || (haveUnknown && (fileType == JST_ROM)))
319                 {
320                         cart = QImage(":/res/cart-blank.png");
321                         QPainter painter(&cart);
322                         painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/label-blank.png")));
323                         painter.end();
324                 }
325                 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE))
326                         || (haveUnknown && (fileType == JST_ALPINE)))
327                 {
328                         cart = QImage(":/res/alpine-file.png");
329                 }
330                 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2 || fileType == JST_JAGSERVER))
331                 {
332                         cart = QImage(":/res/homebrew-file.png");
333                 }
334                 else
335                         cart = QImage(":/res/unknown-file.png");
336
337                 cartImage->setPixmap(QPixmap::fromImage(cart));
338         }
339
340 //1048576
341 //2097152
342 //4194304
343         if (!haveUnknown)
344                 prettyFilename = romList[i].name;
345         else
346         {
347                 int lastSlashPos = currentFile.lastIndexOf('/');
348                 prettyFilename = "\"" + currentFile.mid(lastSlashPos + 1) + "\"";
349         }
350
351         title->setText(QString("<h2>%1</h2>").arg(prettyFilename));
352
353 //Kludge for now, we'll have to fix this later...
354 // So let's fix it now!
355         QString fileTypeString, crcString, notes, compatibility;
356
357 #if 0
358         if (!haveUnknown)
359         {
360                 if (romList[i].flags & FF_ROM)
361                         fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
362                 else if (romList[i].flags & FF_ALPINE)
363                         fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
364                 else
365                         fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
366         }
367 #else
368         if ((!haveUnknown && (romList[i].flags & FF_ROM)) || (haveUnknown && (fileType == JST_ROM)))
369                 fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
370         else if ((!haveUnknown && (romList[i].flags & FF_ALPINE)) || (haveUnknown && (fileType == JST_ALPINE)))
371                 fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
372         else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2))
373                 fileTypeString = QString(tr("ABS/COF Executable (%1 bytes)")).arg(fileSize);
374         else if (haveUnknown && (fileType == JST_JAGSERVER))
375                 fileTypeString = QString(tr("Jaguar Server Executable (%1 bytes)")).arg(fileSize);
376         else
377                 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
378 #endif
379
380 //      crcString = QString("%1").arg(romList[i].crc32, 8, 16, QChar('0')).toUpper();
381         crcString = QString("%1").arg(crc, 8, 16, QChar('0')).toUpper();
382
383         if (!haveUnknown && (romList[i].flags & FF_NON_WORKING))
384                 compatibility = "DOES NOT WORK";
385         else
386                 compatibility = "Unknown";
387
388         // This is going to need some formatting love before long...
389         if (!haveUnknown && (romList[i].flags & FF_BAD_DUMP))
390                 notes = "<b>BAD DUMP</b>";
391
392         if (haveUniversalHeader)
393                 notes += " Universal Header detected";
394
395         if (!haveUnknown && (romList[i].flags & FF_REQ_DSP))
396                 notes += " Requires DSP";
397
398         if (!haveUnknown && (romList[i].flags & FF_VERIFIED))
399                 notes += " <i>(Verified)</i>";
400
401         data->setText(QString("%1<br>%2<br>%3<br>%4")
402                 .arg(fileTypeString).arg(crcString).arg(compatibility).arg(notes));
403 }
404
405 /*
406     Super Duper Awesome Guy (World)
407
408          Type: 4MB Cartridge
409         CRC32: FEDCBA98
410 Compatibility: DOES NOT WORK
411         Notes: Universal Header detected; Requires DSP
412
413
414     Stupid Homebrew Game That Sux
415
416          Type: ABS/COF Executable (43853 bytes)
417         CRC32: 76543210
418 Compatibility: Unknown
419         Notes: $4000 Load, $4000 Run
420
421
422     Action Hopscotch Plus (Prototype)
423
424          Type: 2MB Alpine ROM
425         CRC32: 44889921
426 Compatibility: 80% (or ****)
427         Notes: EEPROM available
428 */