1 #include <QtGui>
2 #include <QTest>
3 #include <QDir>
4 #include <QFile>
5 #include <QFileDialog>
6 #include <QCache>
7 #include <QApplication>
8 #include <QInputDialog>
9 #include <QWidgetAction>
10 #include <QLocale>
11 #include <QPainterPath>
12 #include <QAbstractButton>
13 #include <QDesktopServices>
14 #include <QHash>
15 #include <QLabel>
16 #include <QChar>
17 
18 #include <algorithm> // std::sort()
19 
20 #include "softwarelist.h"
21 #include "softwaresnapshot.h"
22 #include "machinelist.h"
23 #include "qmc2main.h"
24 #include "options.h"
25 #include "iconlineedit.h"
26 #include "romalyzer.h"
27 #include "processmanager.h"
28 #include "aspectratiolabel.h"
29 #include "itemselect.h"
30 #include "macros.h"
31 
32 // external global variables
33 extern MainWindow *qmc2MainWindow;
34 extern Settings *qmc2Config;
35 extern Options *qmc2Options;
36 extern MachineList *qmc2MachineList;
37 extern bool qmc2CleaningUp;
38 extern bool qmc2EarlyStartup;
39 extern bool qmc2UseSoftwareSnapFile;
40 extern SoftwareList *qmc2SoftwareList;
41 extern SoftwareSnap *qmc2SoftwareSnap;
42 extern SoftwareSnapshot *qmc2SoftwareSnapshot;
43 extern int qmc2SoftwareSnapPosition;
44 extern bool qmc2IgnoreItemActivation;
45 extern bool qmc2SmoothScaling;
46 extern bool qmc2RetryLoadingImages;
47 extern int qmc2UpdateDelay;
48 extern int qmc2DefaultLaunchMode;
49 extern bool qmc2LoadingInterrupted;
50 extern bool qmc2CriticalSection;
51 extern bool qmc2UseDefaultEmulator;
52 extern bool qmc2TemplateCheck;
53 extern bool qmc2VerifyActive;
54 extern bool qmc2ParentImageFallback;
55 extern QCache<QString, ImagePixmap> qmc2ImagePixmapCache;
56 extern QHash<QString, QPair<QString, QAction *> > qmc2ShortcutHash;
57 extern QHash<QString, QString> qmc2CustomShortcutHash;
58 extern ROMAlyzer *qmc2SoftwareROMAlyzer;
59 extern QAbstractItemView::ScrollHint qmc2CursorPositioningMode;
60 
61 QHash<QString, QStringList> systemSoftwareListHash;
62 QHash<QString, QStringList> systemSoftwareFilterHash;
63 QHash<QString, QHash<QString, char> > softwareListStateHash;
64 QHash<QString, SoftwareItem *> softwareItemHash;
65 QHash<QString, SoftwareItem *> softwareHierarchyItemHash;
66 QHash<QString, QString> softwareParentHash;
67 bool SoftwareList::isInitialLoad = true;
68 bool SoftwareList::swlSupported = true;
69 QString SoftwareList::swStatesLastLine;
70 
71 #define swlDb		qmc2MainWindow->swlDb
72 #define userDataDb      qmc2MachineList->userDataDb()
73 
SoftwareList(QString sysName,QWidget * parent)74 SoftwareList::SoftwareList(QString sysName, QWidget *parent) :
75 	QWidget(parent)
76 {
77 	if ( !swlDb ) {
78 		swlDb = new SoftwareListXmlDatabaseManager(qmc2MainWindow);
79 		swlDb->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
80 		swlDb->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
81 	}
82 
83 	m_trL = tr("L:");
84 	m_trC = tr("C:");
85 	m_trM = tr("M:");
86 	m_trI = tr("I:");
87 	m_trN = tr("N:");
88        	m_trU = tr("U:");
89        	m_trS = tr("S:");
90 
91 	setupUi(this);
92 
93 	// loading animation
94 	loadAnimMovie = new QMovie(QString::fromUtf8(":/data/img/loadanim.gif"), QByteArray(), this);
95 	loadAnimMovie->setCacheMode(QMovie::CacheAll);
96 	loadAnimMovie->setSpeed(QMC2_LOADANIM_SPEED);
97 	loadAnimMovie->stop();
98 
99 	// replace loading animation label with an aspect-ratio keeping one
100 	verticalLayout->removeWidget(labelLoadingSoftwareLists);
101 	delete labelLoadingSoftwareLists;
102 	labelLoadingSoftwareLists = new AspectRatioLabel(this);
103 	labelLoadingSoftwareLists->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
104 	labelLoadingSoftwareLists->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
105 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
106 		labelLoadingSoftwareLists->setMovie(loadAnimMovie);
107 	else
108 		labelLoadingSoftwareLists->setMovie(qmc2MainWindow->nullMovie);
109 	verticalLayout->insertWidget(1, labelLoadingSoftwareLists);
110 	labelLoadingSoftwareLists->setVisible(false);
111 
112 	// hide progress bars
113 	progressBar->setVisible(false);
114 	progressBarSearch->setVisible(false);
115 
116 	// hide snapname device selection initially
117 	comboBoxSnapnameDevice->hide();
118 
119 	if ( !qmc2SoftwareSnap )
120 		qmc2SoftwareSnap = new SoftwareSnap(0);
121 
122 	qmc2SoftwareSnap->hide();
123 	snapTimer.setSingleShot(true);
124 	connect(&snapTimer, SIGNAL(timeout()), qmc2SoftwareSnap, SLOT(loadImage()));
125 
126 	systemName = sysName;
127 	loadProc = verifyProc = 0;
128 	exporter = 0;
129 	currentItem = enteredItem = 0;
130 	snapForced = autoSelectSearchItem = interruptLoad = isLoading = isReady = fullyLoaded = updatingMountDevices = negatedMatch = false;
131 	validData = autoMounted = true;
132 	searchActive = stopSearch = false;
133 	uncommittedSwlDbRows = 0;
134 
135 	horizontalLayout->removeItem(horizontalSpacer);
136 
137 	oldMin = 0;
138 	oldMax = 1;
139 	oldFmt = qmc2MainWindow->progressBarMachineList->format();
140 
141 	comboBoxSearch->setLineEdit(new IconLineEdit(QIcon(QString::fromUtf8(":/data/img/find.png")), QMC2_ALIGN_LEFT, comboBoxSearch));
142 	comboBoxSearch->lineEdit()->setPlaceholderText(tr("Enter search string"));
143 	comboBoxSearch->setToolTip(comboBoxSearch->toolTip() + " - " + tr("note: the special characters $, (, ), *, +, ., ?, [, ], ^, {, |, } and \\ must be escaped when they are meant literally!"));
144 
145 	QFontMetrics fm(QApplication::font());
146 	QSize iconSize(fm.height() - 2, fm.height() - 2);
147 	QSize iconSizeMiddle = iconSize + QSize(2, 2);
148 	treeWidgetKnownSoftware->setIconSize(iconSizeMiddle);
149 	treeWidgetKnownSoftwareTree->setIconSize(iconSizeMiddle);
150 	treeWidgetFavoriteSoftware->setIconSize(iconSizeMiddle);
151 	treeWidgetSearchResults->setIconSize(iconSizeMiddle);
152 	toolButtonAddToFavorites->setIconSize(iconSize);
153 	toolButtonRemoveFromFavorites->setIconSize(iconSize);
154 	toolButtonFavoritesOptions->setIconSize(iconSize);
155 	toolButtonPlay->setIconSize(iconSize);
156 	toolButtonReload->setIconSize(iconSize);
157 	toolButtonExport->setIconSize(iconSize);
158 	toolButtonToggleSoftwareInfo->setIconSize(iconSize);
159 	toolButtonCompatFilterToggle->setIconSize(iconSize);
160 	toolButtonToggleSnapnameAdjustment->setIconSize(iconSize);
161 	toolButtonToggleStatenameAdjustment->setIconSize(iconSize);
162 	toolButtonManualOpenInViewer->setIconSize(iconSize);
163 	toolButtonSoftwareStates->setIconSize(iconSize);
164 	toolButtonAnalyzeSoftware->setIconSize(iconSize);
165 	toolButtonRebuildSoftware->setIconSize(iconSize);
166 #if (defined(QMC2_OS_UNIX) && QT_VERSION < 0x050000) || defined(QMC2_OS_WIN)
167 	toolButtonPlayEmbedded->setIconSize(iconSize);
168 #else
169 	toolButtonPlayEmbedded->setVisible(false);
170 #endif
171 	toolBoxSoftwareList->setItemIcon(QMC2_SWLIST_KNOWN_SW_PAGE, QIcon(QPixmap(QString::fromUtf8(":/data/img/view_detail.png")).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
172 	toolBoxSoftwareList->setItemIcon(QMC2_SWLIST_FAVORITES_PAGE, QIcon(QPixmap(QString::fromUtf8(":/data/img/favorites.png")).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
173 	toolBoxSoftwareList->setItemIcon(QMC2_SWLIST_SEARCH_PAGE, QIcon(QPixmap(QString::fromUtf8(":/data/img/hint.png")).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
174 
175 	toolBoxSoftwareList->setEnabled(false);
176 	toolButtonAddToFavorites->setEnabled(false);
177 	toolButtonRemoveFromFavorites->setEnabled(false);
178 	toolButtonFavoritesOptions->setEnabled(false);
179 	toolButtonPlay->setEnabled(false);
180 	toolButtonPlayEmbedded->setEnabled(false);
181 	toolButtonExport->setEnabled(false);
182 	toolButtonToggleSoftwareInfo->setEnabled(false);
183 	toolButtonCompatFilterToggle->setEnabled(false);
184 	toolButtonToggleSnapnameAdjustment->setEnabled(false);
185 	toolButtonToggleStatenameAdjustment->setEnabled(false);
186 	toolButtonSoftwareStates->setEnabled(false);
187 	toolButtonAnalyzeSoftware->setEnabled(false);
188 	toolButtonRebuildSoftware->setEnabled(false);
189 	comboBoxDeviceConfiguration->setEnabled(false);
190 
191 	// software list context menu
192 	softwareListMenu = new QMenu(this);
193 	QString s = tr("Play selected software");
194 	QAction *action = softwareListMenu->addAction(tr("&Play"));
195 	action->setToolTip(s); action->setStatusTip(s);
196 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/launch.png")));
197 	connect(action, SIGNAL(triggered()), this, SLOT(playActivated()));
198 #if (defined(QMC2_OS_UNIX) && QT_VERSION < 0x050000) || defined(QMC2_OS_WIN)
199 	s = tr("Play selected software (embedded)");
200 	action = softwareListMenu->addAction(tr("Play &embedded"));
201 	action->setToolTip(s); action->setStatusTip(s);
202 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/embed.png")));
203 	connect(action, SIGNAL(triggered()), this, SLOT(playEmbeddedActivated()));
204 #endif
205 	softwareListMenu->addSeparator();
206 	s = tr("Add to favorite software list");
207 	actionAddToFavorites = softwareListMenu->addAction(tr("&Add to favorites"));
208 	actionAddToFavorites->setToolTip(s); actionAddToFavorites->setStatusTip(s);
209 	actionAddToFavorites->setIcon(QIcon(QString::fromUtf8(":/data/img/add_to_favorites.png")));
210 	connect(actionAddToFavorites, SIGNAL(triggered()), this, SLOT(addToFavorites()));
211 	s = tr("Remove from favorite software list");
212 	actionRemoveFromFavorites = softwareListMenu->addAction(tr("&Remove from favorites"));
213 	actionRemoveFromFavorites->setToolTip(s); actionRemoveFromFavorites->setStatusTip(s);
214 	actionRemoveFromFavorites->setIcon(QIcon(QString::fromUtf8(":/data/img/remove_from_favorites.png")));
215 	connect(actionRemoveFromFavorites, SIGNAL(triggered()), this, SLOT(removeFromFavorites()));
216 	softwareListMenu->addSeparator();
217 
218 	// analyze sub-menu
219 	QMenu *analyzeMenu = new QMenu(this);
220 	analyzeMenuAction = softwareListMenu->addMenu(analyzeMenu);
221 	analyzeMenuAction->setText(tr("Analy&ze"));
222 	analyzeMenuAction->setIcon(QIcon(QString::fromUtf8(":/data/img/romalyzer_sw.png")));
223 	s = tr("Analyze the currently selected software with the ROMAlyzer");
224 	actionAnalyzeSoftware = analyzeMenu->addAction(tr("Current &software..."));
225 	actionAnalyzeSoftware->setToolTip(s); actionAnalyzeSoftware->setStatusTip(s);
226 	actionAnalyzeSoftware->setIcon(QIcon(QString::fromUtf8(":/data/img/romalyzer_sw.png")));
227 	connect(actionAnalyzeSoftware, SIGNAL(triggered()), this, SLOT(analyzeSoftware()));
228 	s = tr("Analyze the currently selected software's list with the ROMAlyzer");
229 	actionAnalyzeSoftwareList = analyzeMenu->addAction(tr("Current software-lis&t..."));
230 	actionAnalyzeSoftwareList->setToolTip(s); actionAnalyzeSoftwareList->setStatusTip(s);
231 	actionAnalyzeSoftwareList->setIcon(QIcon(QString::fromUtf8(":/data/img/romalyzer_sw.png")));
232 	connect(actionAnalyzeSoftwareList, SIGNAL(triggered()), this, SLOT(analyzeSoftwareList()));
233 	s = tr("Analyze all relevant software-lists for the current system with the ROMAlyzer");
234 	actionAnalyzeSoftwareLists = analyzeMenu->addAction(tr("All supported software-&lists..."));
235 	actionAnalyzeSoftwareLists->setToolTip(s); actionAnalyzeSoftwareLists->setStatusTip(s);
236 	actionAnalyzeSoftwareLists->setIcon(QIcon(QString::fromUtf8(":/data/img/romalyzer_sw.png")));
237 	connect(actionAnalyzeSoftwareLists, SIGNAL(triggered()), this, SLOT(analyzeSoftwareLists()));
238 	toolButtonAnalyzeSoftware->setMenu(analyzeMenu);
239 	connect(analyzeMenu, SIGNAL(aboutToShow()), this, SLOT(analyzeSoftwareMenu_aboutToShow()));
240 
241 	// rebuild sub-menu
242 	QMenu *rebuildMenu = new QMenu(this);
243 	rebuildMenuAction = softwareListMenu->addMenu(rebuildMenu);
244 	rebuildMenuAction->setText(tr("&Rebuild"));
245 	rebuildMenuAction->setIcon(QIcon(QString::fromUtf8(":/data/img/rebuild.png")));
246 	s = tr("Rebuild the currently selected software with the ROMAlyzer");
247 	actionRebuildSoftware = rebuildMenu->addAction(tr("Current &software..."));
248 	actionRebuildSoftware->setToolTip(s); actionRebuildSoftware->setStatusTip(s);
249 	actionRebuildSoftware->setIcon(QIcon(QString::fromUtf8(":/data/img/rebuild.png")));
250 	connect(actionRebuildSoftware, SIGNAL(triggered()), this, SLOT(rebuildSoftware()));
251 	s = tr("Rebuild the currently selected software's list with the ROMAlyzer");
252 	actionRebuildSoftwareList = rebuildMenu->addAction(tr("Current software-lis&t..."));
253 	actionRebuildSoftwareList->setToolTip(s); actionRebuildSoftwareList->setStatusTip(s);
254 	actionRebuildSoftwareList->setIcon(QIcon(QString::fromUtf8(":/data/img/rebuild.png")));
255 	connect(actionRebuildSoftwareList, SIGNAL(triggered()), this, SLOT(rebuildSoftwareList()));
256 	s = tr("Rebuild all relevant software-lists for the current system with the ROMAlyzer");
257 	actionRebuildSoftwareLists = rebuildMenu->addAction(tr("All supported software-&lists..."));
258 	actionRebuildSoftwareLists->setToolTip(s); actionRebuildSoftwareLists->setStatusTip(s);
259 	actionRebuildSoftwareLists->setIcon(QIcon(QString::fromUtf8(":/data/img/rebuild.png")));
260 	connect(actionRebuildSoftwareLists, SIGNAL(triggered()), this, SLOT(rebuildSoftwareLists()));
261 	toolButtonRebuildSoftware->setMenu(rebuildMenu);
262 	connect(rebuildMenu, SIGNAL(aboutToShow()), this, SLOT(rebuildSoftwareMenu_aboutToShow()));
263 
264 	updateRebuildSoftwareMenuVisibility();
265 
266 	// open manual in PDF viewer item
267 	softwareListMenu->addSeparator();
268 	s = tr("Open manual in PDF viewer");
269 	actionManualOpenInViewer = softwareListMenu->addAction(tr("Open in PDF viewer..."));
270 	actionManualOpenInViewer->setToolTip(s);actionManualOpenInViewer->setStatusTip(s);
271 	actionManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/book.png")));
272 	connect(actionManualOpenInViewer, SIGNAL(triggered()), this, SLOT(on_toolButtonManualOpenInViewer_clicked()));
273 
274 	// clear selection item
275 	softwareListMenu->addSeparator();
276 	s = tr("Clear software selection");
277 	actionClearSelection = softwareListMenu->addAction(tr("&Clear selection"));
278 	actionClearSelection->setToolTip(s); actionClearSelection->setStatusTip(s);
279 	actionClearSelection->setIcon(QIcon(QString::fromUtf8(":/data/img/broom.png")));
280 	connect(actionClearSelection, SIGNAL(triggered()), this, SLOT(clearSoftwareSelection()));
281 
282 	// favorites options menu
283 	favoritesOptionsMenu = new QMenu(this);
284 	s = tr("Load favorites from a file...");
285 	action = favoritesOptionsMenu->addAction(s);
286 	action->setToolTip(s); action->setStatusTip(s);
287 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/fileopen.png")));
288 	connect(action, SIGNAL(triggered()), this, SLOT(loadFavoritesFromFile()));
289 	s = tr("Save favorites to a file...");
290 	action = favoritesOptionsMenu->addAction(s);
291 	action->setToolTip(s); action->setStatusTip(s);
292 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/filesaveas.png")));
293 	actionSaveFavoritesToFile = action;
294 	connect(action, SIGNAL(triggered()), this, SLOT(saveFavoritesToFile()));
295 	toolButtonFavoritesOptions->setMenu(favoritesOptionsMenu);
296 
297 	// snapname adjustment menu
298 	menuSnapnameAdjustment = new QMenu(this);
299 	s = tr("Adjust pattern...");
300 	action = menuSnapnameAdjustment->addAction(s);
301 	action->setToolTip(s); action->setStatusTip(s);
302 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/configure.png")));
303 	connect(action, SIGNAL(triggered()), this, SLOT(adjustSnapnamePattern()));
304 	toolButtonToggleSnapnameAdjustment->setMenu(menuSnapnameAdjustment);
305 
306 	// statename adjustment menu
307 	menuStatenameAdjustment = new QMenu(this);
308 	s = tr("Adjust pattern...");
309 	action = menuStatenameAdjustment->addAction(s);
310 	action->setToolTip(s); action->setStatusTip(s);
311 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/configure.png")));
312 	connect(action, SIGNAL(triggered()), this, SLOT(adjustStatenamePattern()));
313 	toolButtonToggleStatenameAdjustment->setMenu(menuStatenameAdjustment);
314 
315 	// software-states menu
316 	menuSoftwareStates = new QMenu(this);
317 	s = tr("Check software-states");
318 	action = menuSoftwareStates->addAction(s);
319 	action->setToolTip(s); action->setStatusTip(s);
320 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/update.png")));
321 	connect(action, SIGNAL(triggered()), this, SLOT(checkSoftwareStates()));
322 	actionCheckSoftwareStates = action;
323 	actionCheckSoftwareStates->setShortcut(QKeySequence(qmc2CustomShortcutHash["F10"]));
324 	actionCheckSoftwareStates->setShortcutContext(Qt::ApplicationShortcut);
325 	qmc2ShortcutHash["F10"].second = actionCheckSoftwareStates;
326 	menuSoftwareStates->addSeparator();
327 	stateFilter = new SoftwareStateFilter(menuSoftwareStates);
328 	stateFilterAction = new QWidgetAction(menuSoftwareStates);
329 	stateFilterAction->setDefaultWidget(stateFilter);
330 	menuSoftwareStates->addAction(stateFilterAction);
331 
332 	// search options menu
333 	menuSearchOptions = new QMenu(this);
334 	s = tr("Negate search");
335 	actionNegateSearch = menuSearchOptions->addAction(s);
336 	actionNegateSearch->setToolTip(s); actionNegateSearch->setStatusTip(s);
337 	actionNegateSearch->setIcon(QIcon(QString::fromUtf8(":/data/img/find_negate.png")));
338 	actionNegateSearch->setCheckable(true);
339 	bool negated = qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/NegateSearch", false).toBool();
340 	actionNegateSearch->setChecked(negated);
341 	negateSearchTriggered(negated);
342 	connect(actionNegateSearch, SIGNAL(triggered(bool)), this, SLOT(negateSearchTriggered(bool)));
343 	IconLineEdit *ile = ((IconLineEdit *)comboBoxSearch->lineEdit());
344 	connect(ile, SIGNAL(returnPressed()), this, SLOT(comboBoxSearch_editTextChanged_delayed()));
345 	ile->button()->setPopupMode(QToolButton::InstantPopup);
346 	ile->button()->setMenu(menuSearchOptions);
347 
348 	// restore widget states
349 	treeWidgetKnownSoftware->header()->restoreState(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/KnownSoftwareHeaderState").toByteArray());
350 	treeWidgetKnownSoftwareTree->header()->restoreState(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/KnownSoftwareTreeHeaderState").toByteArray());
351 	treeWidgetFavoriteSoftware->header()->restoreState(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/FavoriteSoftwareHeaderState").toByteArray());
352 	treeWidgetSearchResults->header()->restoreState(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SearchResultsHeaderState").toByteArray());
353 	toolBoxSoftwareList->setCurrentIndex(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/PageIndex").toInt());
354 	toolButtonToggleSoftwareInfo->setChecked(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/ShowSoftwareInfo", false).toBool());
355 	toolButtonCompatFilterToggle->blockSignals(true);
356 	toolButtonCompatFilterToggle->setChecked(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/CompatFilter", true).toBool());
357 	toolButtonCompatFilterToggle->blockSignals(false);
358 	toolButtonToggleSnapnameAdjustment->setChecked(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/AdjustSnapname", false).toBool());
359 	toolButtonToggleStatenameAdjustment->setChecked(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/AdjustStatename", false).toBool());
360 	toolButtonSoftwareStates->setChecked(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/ShowSoftwareStates", true).toBool());
361 	if ( toolButtonSoftwareStates->isChecked() )
362 		toolButtonSoftwareStates->setMenu(menuSoftwareStates);
363 
364 	connect(treeWidgetKnownSoftware->header(), SIGNAL(sectionClicked(int)), this, SLOT(treeWidgetKnownSoftware_headerSectionClicked(int)));
365 	connect(treeWidgetKnownSoftwareTree->header(), SIGNAL(sectionClicked(int)), this, SLOT(treeWidgetKnownSoftwareTree_headerSectionClicked(int)));
366 	connect(treeWidgetFavoriteSoftware->header(), SIGNAL(sectionClicked(int)), this, SLOT(treeWidgetFavoriteSoftware_headerSectionClicked(int)));
367 	connect(treeWidgetSearchResults->header(), SIGNAL(sectionClicked(int)), this, SLOT(treeWidgetSearchResults_headerSectionClicked(int)));
368 	connect(&searchTimer, SIGNAL(timeout()), this, SLOT(comboBoxSearch_editTextChanged_delayed()));
369 
370 	QHeaderView *header;
371 
372 	// header context menus
373 	menuKnownSoftwareHeader = new QMenu(this);
374 	header = treeWidgetKnownSoftware->header();
375 	action = menuKnownSoftwareHeader->addAction(tr("Title"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_TITLE);
376 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_TITLE));
377 	action = menuKnownSoftwareHeader->addAction(tr("Name"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_NAME);
378 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_NAME));
379 	action = menuKnownSoftwareHeader->addAction(tr("Publisher"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_PUBLISHER);
380 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_PUBLISHER));
381 	action = menuKnownSoftwareHeader->addAction(tr("Year"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_YEAR);
382 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_YEAR));
383 	action = menuKnownSoftwareHeader->addAction(tr("Part"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_PART);
384 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_PART));
385 	action = menuKnownSoftwareHeader->addAction(tr("Interface"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_INTERFACE);
386 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_INTERFACE));
387 	action = menuKnownSoftwareHeader->addAction(tr("List"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_LIST);
388 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_LIST));
389 	action = menuKnownSoftwareHeader->addAction(tr("Supported"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_SUPPORTED);
390 	action->setChecked(!treeWidgetKnownSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_SUPPORTED));
391 	menuKnownSoftwareHeader->addSeparator();
392 	action = menuKnownSoftwareHeader->addAction(QIcon(":data/img/reset.png"), tr("Reset"), this, SLOT(actionKnownSoftwareHeader_triggered())); action->setData(QMC2_SWLIST_RESET);
393 	header->setContextMenuPolicy(Qt::CustomContextMenu);
394 	connect(header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(treeWidgetKnownSoftwareHeader_customContextMenuRequested(const QPoint &)));
395 	header = treeWidgetKnownSoftwareTree->header();
396 	header->setContextMenuPolicy(Qt::CustomContextMenu);
397 	connect(header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(treeWidgetKnownSoftwareTreeHeader_customContextMenuRequested(const QPoint &)));
398 
399 	menuFavoriteSoftwareHeader = new QMenu(this);
400 	header = treeWidgetFavoriteSoftware->header();
401 	action = menuFavoriteSoftwareHeader->addAction(tr("Title"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_TITLE);
402 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_TITLE));
403 	action = menuFavoriteSoftwareHeader->addAction(tr("Name"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_NAME);
404 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_NAME));
405 	action = menuFavoriteSoftwareHeader->addAction(tr("Publisher"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_PUBLISHER);
406 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_PUBLISHER));
407 	action = menuFavoriteSoftwareHeader->addAction(tr("Year"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_YEAR);
408 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_YEAR));
409 	action = menuFavoriteSoftwareHeader->addAction(tr("Part"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_PART);
410 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_PART));
411 	action = menuFavoriteSoftwareHeader->addAction(tr("Interface"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_INTERFACE);
412 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_INTERFACE));
413 	action = menuFavoriteSoftwareHeader->addAction(tr("List"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_LIST);
414 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_LIST));
415 	action = menuFavoriteSoftwareHeader->addAction(tr("Supported"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_SUPPORTED);
416 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_LIST));
417 	action = menuFavoriteSoftwareHeader->addAction(tr("Device configuration"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_DEVICECFG);
418 	action->setChecked(!treeWidgetFavoriteSoftware->isColumnHidden(QMC2_SWLIST_COLUMN_DEVICECFG));
419 	menuFavoriteSoftwareHeader->addSeparator();
420 	action = menuFavoriteSoftwareHeader->addAction(QIcon(":data/img/reset.png"), tr("Reset"), this, SLOT(actionFavoriteSoftwareHeader_triggered())); action->setData(QMC2_SWLIST_RESET);
421 	header->setContextMenuPolicy(Qt::CustomContextMenu);
422 	connect(header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(treeWidgetFavoriteSoftwareHeader_customContextMenuRequested(const QPoint &)));
423 
424 	menuSearchResultsHeader = new QMenu(this);
425 	header = treeWidgetSearchResults->header();
426 	action = menuSearchResultsHeader->addAction(tr("Title"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_TITLE);
427 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_TITLE));
428 	action = menuSearchResultsHeader->addAction(tr("Name"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_NAME);
429 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_NAME));
430 	action = menuSearchResultsHeader->addAction(tr("Publisher"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_PUBLISHER);
431 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_PUBLISHER));
432 	action = menuSearchResultsHeader->addAction(tr("Year"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_YEAR);
433 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_YEAR));
434 	action = menuSearchResultsHeader->addAction(tr("Part"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_PART);
435 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_PART));
436 	action = menuSearchResultsHeader->addAction(tr("Interface"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_INTERFACE);
437 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_INTERFACE));
438 	action = menuSearchResultsHeader->addAction(tr("List"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_LIST);
439 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_LIST));
440 	action = menuSearchResultsHeader->addAction(tr("Supported"), this, SLOT(actionSearchResultsHeader_triggered())); action->setCheckable(true); action->setData(QMC2_SWLIST_COLUMN_SUPPORTED);
441 	action->setChecked(!treeWidgetSearchResults->isColumnHidden(QMC2_SWLIST_COLUMN_LIST));
442 	menuSearchResultsHeader->addSeparator();
443 	action = menuSearchResultsHeader->addAction(QIcon(":data/img/reset.png"), tr("Reset"), this, SLOT(actionSearchResultsHeader_triggered())); action->setData(QMC2_SWLIST_RESET);
444 	header->setContextMenuPolicy(Qt::CustomContextMenu);
445 	connect(header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(treeWidgetSearchResultsHeader_customContextMenuRequested(const QPoint &)));
446 
447 	// detail update timer
448 	connect(&detailUpdateTimer, SIGNAL(timeout()), this, SLOT(updateDetail()));
449 
450 	// tool box menu hack (for "known software" page)
451 	toolBoxButtonKnownSoftware = 0;
452 	foreach(QWidget *w, toolBoxSoftwareList->findChildren<QWidget*>())
453 	{
454 		if( w->inherits("QToolBoxButton") ) {
455 			QAbstractButton *button = qobject_cast<QAbstractButton*>(w);
456 			if ( button->text() == tr("Known software") ) {
457 				toolBoxButtonKnownSoftware = new QToolButton(w);
458 				toolBoxButtonKnownSoftware->setToolButtonStyle(Qt::ToolButtonIconOnly);
459 				toolBoxButtonKnownSoftware->setPopupMode(QToolButton::InstantPopup);
460 				toolBoxButtonKnownSoftware->setIconSize(iconSize);
461 				toolBoxButtonKnownSoftware->setAutoRaise(true);
462 				QPalette pal = toolBoxButtonKnownSoftware->palette();
463 				pal.setBrush(QPalette::Window, Qt::transparent);
464 				pal.setBrush(QPalette::WindowText, Qt::transparent);
465 				pal.setBrush(QPalette::Text, Qt::transparent);
466 				pal.setBrush(QPalette::Button, Qt::transparent);
467 				pal.setBrush(QPalette::Base, Qt::transparent);
468 				pal.setBrush(QPalette::AlternateBase, Qt::transparent);
469 				pal.setBrush(QPalette::Light, Qt::transparent);
470 				pal.setBrush(QPalette::Midlight, Qt::transparent);
471 				pal.setBrush(QPalette::Dark, Qt::transparent);
472 				pal.setBrush(QPalette::Mid, Qt::transparent);
473 				pal.setBrush(QPalette::Shadow, Qt::transparent);
474 				pal.setBrush(QPalette::Highlight, Qt::transparent);
475 				pal.setBrush(QPalette::HighlightedText, Qt::transparent);
476 				pal.setBrush(QPalette::Link, Qt::transparent);
477 				pal.setBrush(QPalette::LinkVisited, Qt::transparent);
478 				pal.setBrush(QPalette::NoRole, Qt::transparent);
479 				toolBoxButtonKnownSoftware->setPalette(pal);
480 				toolBoxButtonKnownSoftware->setToolTip(tr("Click to open the view menu"));
481 				break;
482 			}
483 		}
484 	}
485 
486 	knownSoftwareMenu = toggleListMenu = 0;
487 	if ( toolBoxButtonKnownSoftware ) {
488 		knownSoftwareMenu = new QMenu(this);
489 		viewFlatAction = knownSoftwareMenu->addAction(QIcon(":data/img/view_detail.png"), tr("View flat"), this, SLOT(actionViewFlat_triggered()));
490 		s = tr("View known software as a flat list");
491 		viewFlatAction->setToolTip(s); viewFlatAction->setStatusTip(s);
492 		viewFlatAction->setCheckable(true);
493 		viewFlatAction->setShortcut(QKeySequence(qmc2CustomShortcutHash["Shift+F5"]));
494 		viewFlatAction->setShortcutContext(Qt::ApplicationShortcut);
495 		qmc2ShortcutHash["Shift+F5"].second = viewFlatAction;
496 		viewTreeAction = knownSoftwareMenu->addAction(QIcon(":data/img/view_tree.png"), tr("View tree"), this, SLOT(actionViewTree_triggered()));
497 		s = tr("View known software as a parent/clone tree");
498 		viewTreeAction->setToolTip(s); viewTreeAction->setStatusTip(s);
499 		viewTreeAction->setCheckable(true);
500 		viewTreeAction->setShortcut(QKeySequence(qmc2CustomShortcutHash["Shift+F6"]));
501 		viewTreeAction->setShortcutContext(Qt::ApplicationShortcut);
502 		qmc2ShortcutHash["Shift+F6"].second = viewTreeAction;
503 		toolBoxButtonKnownSoftware->setMenu(knownSoftwareMenu);
504 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/ViewTree", false).toBool() )
505 			actionViewTree_triggered();
506 		else
507 			actionViewFlat_triggered();
508 	}
509 
510 	isReady = true;
511 }
512 
~SoftwareList()513 SoftwareList::~SoftwareList()
514 {
515 	if ( exporter )
516 		exporter->close();
517 
518 	// save widget states
519 	if ( viewTree() ) {
520 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/KnownSoftwareHeaderState", treeWidgetKnownSoftwareTree->header()->saveState());
521 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/KnownSoftwareTreeHeaderState", treeWidgetKnownSoftwareTree->header()->saveState());
522 	} else {
523 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/KnownSoftwareHeaderState", treeWidgetKnownSoftware->header()->saveState());
524 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/KnownSoftwareTreeHeaderState", treeWidgetKnownSoftware->header()->saveState());
525 	}
526 
527 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/FavoriteSoftwareHeaderState", treeWidgetFavoriteSoftware->header()->saveState());
528 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SearchResultsHeaderState", treeWidgetSearchResults->header()->saveState());
529 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/PageIndex", toolBoxSoftwareList->currentIndex());
530 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/ShowSoftwareInfo", toolButtonToggleSoftwareInfo->isChecked());
531 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/CompatFilter", toolButtonCompatFilterToggle->isChecked());
532 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/AdjustSnapname", toolButtonToggleSnapnameAdjustment->isChecked());
533 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/AdjustStatename", toolButtonToggleStatenameAdjustment->isChecked());
534 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/ShowSoftwareStates", toolButtonSoftwareStates->isChecked());
535 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/NegateSearch", actionNegateSearch->isChecked());
536 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/ViewTree", viewTree());
537 	delete loadAnimMovie;
538 }
539 
adjustSnapnamePattern()540 void SoftwareList::adjustSnapnamePattern()
541 {
542 	bool ok;
543 	QStringList items;
544 	items << "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$" << "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$/%i";
545 	QString storedPattern(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SnapnamePattern", "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$").toString());
546 	int index = items.indexOf(storedPattern);
547 	if ( index < 0 ) {
548 		items << storedPattern;
549 		index = 2;
550 	}
551 	QString pattern(QInputDialog::getItem(this, tr("Snapname adjustment pattern"), tr("Enter the pattern used for snapname adjustment:\n(Allowed macros: $SOFTWARE_LIST$, $SOFTWARE_NAME$)"), items, index, true, &ok));
552 	if ( ok )
553 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SnapnamePattern", pattern);
554 }
555 
adjustStatenamePattern()556 void SoftwareList::adjustStatenamePattern()
557 {
558 	bool ok;
559 	QStringList items;
560 	items << "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$";
561 	QString storedPattern(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/StatenamePattern", "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$").toString());
562 	int index = items.indexOf(storedPattern);
563 	if ( index < 0 ) {
564 		items << storedPattern;
565 		index = 2;
566 	}
567 	QString pattern(QInputDialog::getItem(this, tr("Statename adjustment pattern"), tr("Enter the pattern used for statename adjustment:\n(Allowed macros: $SOFTWARE_LIST$, $SOFTWARE_NAME$)"), items, index, true, &ok));
568 	if ( ok )
569 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/StatenamePattern", pattern);
570 }
571 
clearSoftwareSelection()572 void SoftwareList::clearSoftwareSelection()
573 {
574 	switch ( toolBoxSoftwareList->currentIndex() ) {
575 		case QMC2_SWLIST_KNOWN_SW_PAGE:
576 			treeWidgetKnownSoftware->clearSelection();
577 			treeWidgetKnownSoftwareTree->clearSelection();
578 			break;
579 		case QMC2_SWLIST_FAVORITES_PAGE:
580 			treeWidgetFavoriteSoftware->clearSelection();
581 			break;
582 		case QMC2_SWLIST_SEARCH_PAGE:
583 			treeWidgetSearchResults->clearSelection();
584 			break;
585 	}
586 }
587 
negateSearchTriggered(bool negate)588 void SoftwareList::negateSearchTriggered(bool negate)
589 {
590 	IconLineEdit *ile = ((IconLineEdit *)comboBoxSearch->lineEdit());
591 	if ( negate )
592 		ile->button()->setIcon(QIcon(QString::fromUtf8(":/data/img/find_negate.png")));
593 	else
594 		ile->button()->setIcon(QIcon(QString::fromUtf8(":/data/img/find.png")));
595 	negatedMatch = negate;
596 
597 	searchTimer.start(QMC2_SEARCH_DELAY);
598 }
599 
rebuildSoftware()600 void SoftwareList::rebuildSoftware()
601 {
602 	if ( qmc2SoftwareROMAlyzer && qmc2SoftwareROMAlyzer->rebuilderActive() ) {
603 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("please wait for ROMAlyzer to finish the current rebuild and try again"));
604 		return;
605 	}
606 
607 	bool initial = false;
608 	if ( !qmc2SoftwareROMAlyzer ) {
609 		qmc2SoftwareROMAlyzer = new ROMAlyzer(0, QMC2_ROMALYZER_MODE_SOFTWARE);
610 		initial = true;
611 	}
612 
613 	if ( qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabCollectionRebuilder )
614 		qmc2SoftwareROMAlyzer->tabWidgetAnalysis->setCurrentWidget(qmc2SoftwareROMAlyzer->tabCollectionRebuilder);
615 
616 	if ( qmc2SoftwareROMAlyzer->isHidden() )
617 		qmc2SoftwareROMAlyzer->show();
618 	else if ( qmc2SoftwareROMAlyzer->isMinimized() )
619 		qmc2SoftwareROMAlyzer->showNormal();
620 
621 	CollectionRebuilder *cr = qmc2SoftwareROMAlyzer->collectionRebuilder();
622 	if ( cr ) {
623 		QTreeWidget *treeWidget = 0;
624 		switch ( toolBoxSoftwareList->currentIndex() ) {
625 			case QMC2_SWLIST_KNOWN_SW_PAGE:
626 				if ( viewTree() )
627 					treeWidget = treeWidgetKnownSoftwareTree;
628 				else
629 					treeWidget = treeWidgetKnownSoftware;
630 				break;
631 			case QMC2_SWLIST_FAVORITES_PAGE:
632 				treeWidget = treeWidgetFavoriteSoftware;
633 				break;
634 			case QMC2_SWLIST_SEARCH_PAGE:
635 				treeWidget = treeWidgetSearchResults;
636 				break;
637 		}
638 		if ( treeWidget ) {
639 			QList<QTreeWidgetItem *> selectedItems = treeWidget->selectedItems();
640 			if ( !selectedItems.isEmpty() ) {
641 				QTreeWidgetItem *item = selectedItems[0];
642 				if ( viewTree() ) {
643 					while ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && item->parent() )
644 						item = item->parent();
645 				} else {
646 					while ( item->parent() )
647 						item = item->parent();
648 				}
649 				cr->comboBoxXmlSource->setCurrentIndex(0);
650 				cr->setIgnoreCheckpoint(true);
651 				cr->checkBoxFilterExpression->setChecked(true);
652 				cr->comboBoxFilterSyntax->setCurrentIndex(4);
653 				cr->comboBoxFilterType->setCurrentIndex(0);
654 				cr->toolButtonExactMatch->setChecked(true);
655 				cr->checkBoxFilterStates->setChecked(false);
656 				cr->lineEditFilterExpression->setText(item->text(QMC2_SWLIST_COLUMN_NAME));
657 				cr->checkBoxFilterExpressionSoftwareLists->setChecked(true);
658 				cr->comboBoxFilterSyntaxSoftwareLists->setCurrentIndex(4);
659 				cr->comboBoxFilterTypeSoftwareLists->setCurrentIndex(0);
660 				cr->toolButtonExactMatchSoftwareLists->setChecked(true);
661 				cr->lineEditFilterExpressionSoftwareLists->setText(item->text(QMC2_SWLIST_COLUMN_LIST));
662 				if ( !initial )
663 					cr->plainTextEditLog->clear();
664 				QTimer::singleShot(0, cr->pushButtonStartStop, SLOT(animateClick()));
665 			}
666 		}
667 	}
668 
669 	QTimer::singleShot(0, qmc2SoftwareROMAlyzer, SLOT(raise()));
670 }
671 
rebuildSoftwareList()672 void SoftwareList::rebuildSoftwareList()
673 {
674 	if ( qmc2SoftwareROMAlyzer && qmc2SoftwareROMAlyzer->rebuilderActive() ) {
675 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("please wait for ROMAlyzer to finish the current rebuild and try again"));
676 		return;
677 	}
678 
679 	bool initial = false;
680 	if ( !qmc2SoftwareROMAlyzer ) {
681 		qmc2SoftwareROMAlyzer = new ROMAlyzer(0, QMC2_ROMALYZER_MODE_SOFTWARE);
682 		initial = true;
683 	}
684 
685 	if ( qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabCollectionRebuilder )
686 		qmc2SoftwareROMAlyzer->tabWidgetAnalysis->setCurrentWidget(qmc2SoftwareROMAlyzer->tabCollectionRebuilder);
687 
688 	if ( qmc2SoftwareROMAlyzer->isHidden() )
689 		qmc2SoftwareROMAlyzer->show();
690 	else if ( qmc2SoftwareROMAlyzer->isMinimized() )
691 		qmc2SoftwareROMAlyzer->showNormal();
692 
693 	CollectionRebuilder *cr = qmc2SoftwareROMAlyzer->collectionRebuilder();
694 	if ( cr ) {
695 		QTreeWidget *treeWidget = 0;
696 		switch ( toolBoxSoftwareList->currentIndex() ) {
697 			case QMC2_SWLIST_KNOWN_SW_PAGE:
698 				if ( viewTree() )
699 					treeWidget = treeWidgetKnownSoftwareTree;
700 				else
701 					treeWidget = treeWidgetKnownSoftware;
702 				break;
703 			case QMC2_SWLIST_FAVORITES_PAGE:
704 				treeWidget = treeWidgetFavoriteSoftware;
705 				break;
706 			case QMC2_SWLIST_SEARCH_PAGE:
707 				treeWidget = treeWidgetSearchResults;
708 				break;
709 		}
710 		if ( treeWidget ) {
711 			QList<QTreeWidgetItem *> selectedItems = treeWidget->selectedItems();
712 			if ( !selectedItems.isEmpty() ) {
713 				QTreeWidgetItem *item = selectedItems[0];
714 				if ( viewTree() ) {
715 					while ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && item->parent() )
716 						item = item->parent();
717 				} else {
718 					while ( item->parent() )
719 						item = item->parent();
720 				}
721 				cr->comboBoxXmlSource->setCurrentIndex(0);
722 				cr->setIgnoreCheckpoint(true);
723 				cr->checkBoxFilterExpression->setChecked(false);
724 				cr->checkBoxFilterExpressionSoftwareLists->setChecked(true);
725 				cr->comboBoxFilterSyntaxSoftwareLists->setCurrentIndex(4);
726 				cr->comboBoxFilterTypeSoftwareLists->setCurrentIndex(0);
727 				cr->toolButtonExactMatchSoftwareLists->setChecked(true);
728 				cr->lineEditFilterExpressionSoftwareLists->setText(item->text(QMC2_SWLIST_COLUMN_LIST));
729 				if ( !initial )
730 					cr->plainTextEditLog->clear();
731 				QTimer::singleShot(0, cr->pushButtonStartStop, SLOT(animateClick()));
732 			}
733 		}
734 	}
735 
736 	QTimer::singleShot(0, qmc2SoftwareROMAlyzer, SLOT(raise()));
737 }
738 
rebuildSoftwareLists()739 void SoftwareList::rebuildSoftwareLists()
740 {
741 	if ( qmc2SoftwareROMAlyzer && qmc2SoftwareROMAlyzer->rebuilderActive() ) {
742 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("please wait for ROMAlyzer to finish the current rebuild and try again"));
743 		return;
744 	}
745 
746 	bool initial = false;
747 	if ( !qmc2SoftwareROMAlyzer ) {
748 		qmc2SoftwareROMAlyzer = new ROMAlyzer(0, QMC2_ROMALYZER_MODE_SOFTWARE);
749 		initial = true;
750 	}
751 
752 	if ( qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabCollectionRebuilder )
753 		qmc2SoftwareROMAlyzer->tabWidgetAnalysis->setCurrentWidget(qmc2SoftwareROMAlyzer->tabCollectionRebuilder);
754 
755 	if ( qmc2SoftwareROMAlyzer->isHidden() )
756 		qmc2SoftwareROMAlyzer->show();
757 	else if ( qmc2SoftwareROMAlyzer->isMinimized() )
758 		qmc2SoftwareROMAlyzer->showNormal();
759 
760 	CollectionRebuilder *cr = qmc2SoftwareROMAlyzer->collectionRebuilder();
761 	if ( cr ) {
762 		cr->comboBoxXmlSource->setCurrentIndex(0);
763 		cr->setIgnoreCheckpoint(true);
764 		cr->checkBoxFilterExpression->setChecked(false);
765 		cr->checkBoxFilterExpressionSoftwareLists->setChecked(true);
766 		cr->comboBoxFilterSyntaxSoftwareLists->setCurrentIndex(0);
767 		cr->comboBoxFilterTypeSoftwareLists->setCurrentIndex(0);
768 		cr->toolButtonExactMatchSoftwareLists->setChecked(true);
769 		cr->lineEditFilterExpressionSoftwareLists->setText(systemSoftwareListHash.value(systemName).join("|"));
770 		if ( !initial )
771 			cr->plainTextEditLog->clear();
772 		QTimer::singleShot(0, cr->pushButtonStartStop, SLOT(animateClick()));
773 	}
774 
775 	QTimer::singleShot(0, qmc2SoftwareROMAlyzer, SLOT(raise()));
776 }
777 
updateRebuildSoftwareMenuVisibility()778 void SoftwareList::updateRebuildSoftwareMenuVisibility()
779 {
780 	bool enable = false;
781 	if ( qmc2SoftwareROMAlyzer )
782 		enable = (qmc2SoftwareROMAlyzer->groupBoxCheckSumDatabase->isChecked() && qmc2SoftwareROMAlyzer->groupBoxSetRewriter->isChecked());
783 	else
784 		enable = (qmc2Config->value(QMC2_FRONTEND_PREFIX + "SoftwareROMAlyzer/EnableCheckSumDb", false).toBool() && qmc2Config->value(QMC2_FRONTEND_PREFIX + "SoftwareROMAlyzer/EnableSetRewriter", false).toBool());
785 	rebuildMenuAction->setVisible(enable);
786 	toolButtonRebuildSoftware->setVisible(enable);
787 }
788 
analyzeSoftwareMenu_aboutToShow()789 void SoftwareList::analyzeSoftwareMenu_aboutToShow()
790 {
791 	QTreeWidget *treeWidget = 0;
792 	switch ( toolBoxSoftwareList->currentIndex() ) {
793 		case QMC2_SWLIST_FAVORITES_PAGE:
794 			treeWidget = treeWidgetFavoriteSoftware;
795 			break;
796 		case QMC2_SWLIST_SEARCH_PAGE:
797 			treeWidget = treeWidgetSearchResults;
798 			break;
799 		case QMC2_SWLIST_KNOWN_SW_PAGE:
800 		default:
801 			if ( viewTree() )
802 				treeWidget = treeWidgetKnownSoftwareTree;
803 			else
804 				treeWidget = treeWidgetKnownSoftware;
805 			break;
806 	}
807 	bool enable = qmc2SoftwareROMAlyzer ? !qmc2SoftwareROMAlyzer->active() : true;
808 	actionAnalyzeSoftware->setVisible(!treeWidget->selectedItems().isEmpty());
809 	actionAnalyzeSoftware->setEnabled(enable);
810 	actionAnalyzeSoftwareList->setVisible(!treeWidget->selectedItems().isEmpty());
811 	actionAnalyzeSoftwareList->setEnabled(enable);
812 	actionAnalyzeSoftwareLists->setVisible(actionAnalyzeSoftwareList->isVisible() ? (systemSoftwareListHash.value(systemName).count() > 1) : true);
813 	actionAnalyzeSoftwareLists->setEnabled(enable);
814 }
815 
rebuildSoftwareMenu_aboutToShow()816 void SoftwareList::rebuildSoftwareMenu_aboutToShow()
817 {
818 	QTreeWidget *treeWidget = 0;
819 	switch ( toolBoxSoftwareList->currentIndex() ) {
820 		case QMC2_SWLIST_FAVORITES_PAGE:
821 			treeWidget = treeWidgetFavoriteSoftware;
822 			break;
823 		case QMC2_SWLIST_SEARCH_PAGE:
824 			treeWidget = treeWidgetSearchResults;
825 			break;
826 		case QMC2_SWLIST_KNOWN_SW_PAGE:
827 		default:
828 			if ( viewTree() )
829 				treeWidget = treeWidgetKnownSoftwareTree;
830 			else
831 				treeWidget = treeWidgetKnownSoftware;
832 			break;
833 	}
834 	bool enable = qmc2SoftwareROMAlyzer ? !qmc2SoftwareROMAlyzer->rebuilderActive() : true;
835 	actionRebuildSoftware->setVisible(!treeWidget->selectedItems().isEmpty());
836 	actionRebuildSoftware->setEnabled(enable);
837 	actionRebuildSoftwareList->setVisible(!treeWidget->selectedItems().isEmpty());
838 	actionRebuildSoftwareList->setEnabled(enable);
839 	actionRebuildSoftwareLists->setVisible(actionRebuildSoftwareList->isVisible() ? (systemSoftwareListHash.value(systemName).count() > 1) : true);
840 	actionRebuildSoftwareLists->setEnabled(enable);
841 }
842 
actionViewFlat_triggered()843 void SoftwareList::actionViewFlat_triggered()
844 {
845 	QFontMetrics fm(QApplication::font());
846 	QSize iconSize(fm.height() - 2, fm.height() - 2);
847 	toolBoxSoftwareList->setItemIcon(QMC2_SWLIST_KNOWN_SW_PAGE, QIcon(QPixmap(QString::fromUtf8(":/data/img/view_detail.png")).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
848 	viewFlatAction->setChecked(true);
849 	viewTreeAction->setChecked(false);
850 	stateFilterAction->setEnabled(true);
851 	setViewTree(false);
852 	stackedWidgetKnownSoftware->setCurrentIndex(QMC2_SWLIST_KNOWN_SW_PAGE_FLAT);
853 }
854 
actionViewTree_triggered()855 void SoftwareList::actionViewTree_triggered()
856 {
857 	QFontMetrics fm(QApplication::font());
858 	QSize iconSize(fm.height() - 2, fm.height() - 2);
859 	toolBoxSoftwareList->setItemIcon(QMC2_SWLIST_KNOWN_SW_PAGE, QIcon(QPixmap(QString::fromUtf8(":/data/img/view_tree.png")).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
860 	viewFlatAction->setChecked(false);
861 	viewTreeAction->setChecked(true);
862 	stateFilterAction->setEnabled(false);
863 	setViewTree(true);
864 	stackedWidgetKnownSoftware->setCurrentIndex(QMC2_SWLIST_KNOWN_SW_PAGE_TREE);
865 }
866 
toggleSoftwareList()867 void SoftwareList::toggleSoftwareList()
868 {
869 	QStringList newHiddenList;
870 	foreach (QAction *a, toggleListMenu->actions()) {
871 		if ( a->isCheckable() ) {
872 			if ( !a->isChecked() )
873 				newHiddenList << a->text();
874 		}
875 	}
876 	userDataDb->setHiddenLists(systemName, newHiddenList);
877 	QTimer::singleShot(0, toolButtonReload, SLOT(animateClick()));
878 }
879 
showAllSoftwareLists()880 void SoftwareList::showAllSoftwareLists()
881 {
882 	foreach (QAction *a, toggleListMenu->actions()) {
883 		if ( a->isCheckable() ) {
884 			if ( !a->isChecked() )
885 				a->setChecked(true);
886 		}
887 	}
888 	userDataDb->setHiddenLists(systemName, QStringList());
889 	QTimer::singleShot(0, toolButtonReload, SLOT(animateClick()));
890 }
891 
showOnlyThisSoftwareList()892 void SoftwareList::showOnlyThisSoftwareList()
893 {
894 	QAction *action = (QAction *)sender();
895 	QStringList newHiddenList;
896 	foreach (QAction *a, toggleListMenu->actions()) {
897 		if ( a->isCheckable() ) {
898 			if ( a->text() != action->text() )
899 				newHiddenList << a->text();
900 		}
901 	}
902 	userDataDb->setHiddenLists(systemName, newHiddenList);
903 	QTimer::singleShot(0, toolButtonReload, SLOT(animateClick()));
904 }
905 
showAllExceptThisSoftwareList()906 void SoftwareList::showAllExceptThisSoftwareList()
907 {
908 	QAction *action = (QAction *)sender();
909 	QStringList newHiddenList;
910 	foreach (QAction *a, toggleListMenu->actions()) {
911 		if ( a->isCheckable() ) {
912 			if ( a->text() == action->text() ) {
913 				newHiddenList << a->text();
914 				break;
915 			}
916 		}
917 	}
918 	userDataDb->setHiddenLists(systemName, newHiddenList);
919 	QTimer::singleShot(0, toolButtonReload, SLOT(animateClick()));
920 }
921 
getSoftwareListXmlData(QString listName)922 QString &SoftwareList::getSoftwareListXmlData(QString listName)
923 {
924 	static QString softwareListBuffer;
925 	softwareListBuffer = swlDb->allXml(listName);
926 	return softwareListBuffer;
927 }
928 
getXmlDataWithEnabledSlots(QStringList swlArgs)929 QString &SoftwareList::getXmlDataWithEnabledSlots(QStringList swlArgs)
930 {
931 	static QString xmlBuffer;
932 
933 	xmlBuffer.clear();
934 
935 	qmc2CriticalSection = true;
936 
937 	QStringList args;
938 	args << systemName << swlArgs << "-listxml";
939 
940 #ifdef QMC2_DEBUG
941 	QMC2_PRINT_STRTXT(QString("SoftwareList::getXmlDataWithEnabledSlots(): args = %1").arg(args.join(" ")));
942 #endif
943 
944 	bool commandProcStarted = false;
945 	int retries = 0;
946 	QProcess commandProc;
947 	commandProc.start(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ExecutableFile").toString(), args);
948 	bool started = commandProc.waitForStarted(QMC2_PROCESS_POLL_TIME);
949 	while ( !started && retries++ < QMC2_PROCESS_POLL_RETRIES ) {
950 		started = commandProc.waitForStarted(QMC2_PROCESS_POLL_TIME_LONG);
951 		qApp->processEvents();
952 	}
953 	if ( started ) {
954 		commandProcStarted = true;
955 		bool commandProcRunning = (commandProc.state() == QProcess::Running);
956 		while ( !commandProc.waitForFinished(QMC2_PROCESS_POLL_TIME) && commandProcRunning ) {
957 			qApp->processEvents();
958 			commandProcRunning = (commandProc.state() == QProcess::Running);
959 		}
960 	} else {
961 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't start emulator executable within a reasonable time frame, giving up") + " (" + tr("error text = %1").arg(ProcessManager::errorText(commandProc.error())) + ")");
962 		qmc2CriticalSection = false;
963 		return xmlBuffer;
964 	}
965 	if ( commandProcStarted ) {
966 		xmlBuffer = commandProc.readAllStandardOutput();
967 #if defined(QMC2_OS_WIN)
968 		xmlBuffer.replace("\r\n", "\n"); // convert WinDOS's "0x0D 0x0A" to just "0x0A"
969 #endif
970 		if ( !xmlBuffer.isEmpty() ) {
971 			QStringList xmlLines = xmlBuffer.split("\n");
972 			qApp->processEvents();
973 			xmlBuffer.clear();
974 			if ( !xmlLines.isEmpty() ) {
975 				int i = 0;
976 				QString s = "<machine name=\"" + systemName + "\"";
977 				while ( i < xmlLines.count() && !xmlLines[i].contains(s) )
978 					i++;
979 				xmlBuffer = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
980 				if ( i < xmlLines.count() ) {
981 					while ( i < xmlLines.count() && !xmlLines[i].contains("</machine>") )
982 						xmlBuffer += xmlLines[i++].simplified() + "\n";
983 					if ( i == xmlLines.count() && !xmlLines[i - 1].contains("</machine>") ) {
984 						qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: invalid XML data retrieved for '%1'").arg(systemName));
985 						xmlBuffer.clear();
986 					} else
987 						xmlBuffer += "</machine>\n";
988 				} else {
989 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: invalid XML data retrieved for '%1'").arg(systemName));
990 					xmlBuffer.clear();
991 				}
992 			}
993 		}
994 	}
995 
996 	qmc2CriticalSection = false;
997 	return xmlBuffer;
998 }
999 
on_comboBoxDeviceConfiguration_currentIndexChanged(int index)1000 void SoftwareList::on_comboBoxDeviceConfiguration_currentIndexChanged(int index)
1001 {
1002 	QTimer::singleShot(0, this, SLOT(updateMountDevices()));
1003 }
1004 
lookupMountDevice(QString device,QString deviceInterface,QStringList * mountList)1005 QString &SoftwareList::lookupMountDevice(QString device, QString deviceInterface, QStringList *mountList)
1006 {
1007 	static QString softwareListDeviceName;
1008 
1009 	QHash<QString, QStringList> deviceInstanceHash;
1010 	softwareListDeviceName.clear();
1011 
1012 	QStringList xmlLines(qmc2MachineList->xmlDb()->xml(systemName).split('\n', QString::SkipEmptyParts));
1013 	QStringList *xmlData = &xmlLines;
1014 	QStringList dynamicXmlData;
1015 	if ( comboBoxDeviceConfiguration->currentIndex() > 0 ) {
1016 		qmc2Config->beginGroup(QMC2_EMULATOR_PREFIX + QString("Configuration/Devices/%1/%2").arg(systemName).arg(comboBoxDeviceConfiguration->currentText()));
1017 		QStringList instances(qmc2Config->value("Instances").toStringList());
1018 		QStringList files(qmc2Config->value("Files").toStringList());
1019 		QStringList slotNames(qmc2Config->value("Slots").toStringList());
1020 		QStringList slotOptions(qmc2Config->value("SlotOptions").toStringList());
1021 		QStringList slotBIOSs(qmc2Config->value("SlotBIOSs").toStringList());
1022 		qmc2Config->endGroup();
1023 		QStringList swlArgs;
1024 		for (int j = 0; j < slotNames.count(); j++) {
1025 			if ( !slotOptions.at(j).isEmpty() ) {
1026 				QString slotOpt = slotOptions.at(j);
1027 				if ( !slotBIOSs.at(j).isEmpty() )
1028 					slotOpt += ",bios=" + slotBIOSs.at(j);
1029 				swlArgs << QString("-%1").arg(slotNames.at(j)) << slotOpt;
1030 			}
1031 		}
1032 		for (int j = 0; j < instances.count(); j++) {
1033 #if defined(QMC2_OS_WIN)
1034 			swlArgs << QString("-%1").arg(instances.at(j)) << files[j].replace('/', '\\');
1035 #else
1036 			swlArgs << QString("-%1").arg(instances.at(j)) << files[j].replace("~", "$HOME");
1037 #endif
1038 		}
1039 		foreach (QString line, getXmlDataWithEnabledSlots(swlArgs).split('\n', QString::SkipEmptyParts))
1040 			dynamicXmlData << line.trimmed();
1041 		xmlData = &dynamicXmlData;
1042 #ifdef QMC2_DEBUG
1043 		QMC2_PRINT_STRTXT(QString("SoftwareList::getXmlDataWithEnabledSlots(): XML data start"));
1044 		foreach (QString line, dynamicXmlData)
1045 			QMC2_PRINT_STRTXT(line);
1046 		QMC2_PRINT_STRTXT(QString("SoftwareList::getXmlDataWithEnabledSlots(): XML data end"));
1047 #endif
1048 	}
1049 
1050 	int i = 0;
1051 	while ( i < xmlData->count() && !xmlData->at(i).contains("</machine>") ) {
1052 		QString line(xmlData->at(i++).simplified());
1053 		if ( line.startsWith("<device type=\"") ) {
1054 			int startIndex = line.indexOf("interface=\"");
1055 			int endIndex = -1;
1056 			QString devName;
1057 			if ( startIndex >= 0 ) {
1058 				startIndex += 11;
1059 				endIndex = line.indexOf("\"", startIndex);
1060 				QStringList devInterfaces(line.mid(startIndex, endIndex - startIndex).split(',', QString::SkipEmptyParts));
1061 				line = xmlData->at(i++).simplified();
1062 				startIndex = line.indexOf("briefname=\"");
1063 				if ( startIndex >= 0 ) {
1064 					startIndex += 11;
1065 					endIndex = line.indexOf("\"", startIndex);
1066 					devName = line.mid(startIndex, endIndex - startIndex);
1067 				}
1068 				if ( !devName.isEmpty() )
1069 					foreach (QString devIf, devInterfaces)
1070 						deviceInstanceHash[devIf] << devName;
1071 			} else {
1072 				line = xmlData->at(i++).simplified();
1073 				startIndex = line.indexOf("briefname=\"");
1074 				if ( startIndex >= 0 ) {
1075 					startIndex += 11;
1076 					endIndex = line.indexOf("\"", startIndex);
1077 					devName = line.mid(startIndex, endIndex - startIndex);
1078 				}
1079 				if ( !devName.isEmpty() )
1080 					deviceInstanceHash[devName] << devName;
1081 			}
1082 		}
1083 	}
1084 
1085 	QStringList briefNames(deviceInstanceHash.value(deviceInterface));
1086 	briefNames.sort();
1087 
1088 	if ( briefNames.contains(device) )
1089 		softwareListDeviceName = device;
1090 	else for (int i = 0; i < briefNames.count() && softwareListDeviceName.isEmpty(); i++) {
1091 			softwareListDeviceName = briefNames.at(i);
1092 			if ( successfulLookups.contains(softwareListDeviceName) )
1093 				softwareListDeviceName.clear();
1094 	}
1095 
1096 	if ( successfulLookups.contains(softwareListDeviceName) )
1097 		softwareListDeviceName.clear();
1098 
1099 	if ( mountList )
1100 		*mountList = briefNames;
1101 
1102 	if ( !softwareListDeviceName.isEmpty() )
1103 		successfulLookups << softwareListDeviceName;
1104 
1105 	return softwareListDeviceName;
1106 }
1107 
getXmlData()1108 void SoftwareList::getXmlData()
1109 {
1110 	QStringList softwareList(systemSoftwareListHash.value(systemName));
1111 	if ( softwareList.isEmpty() || softwareList.contains("NO_SOFTWARE_LIST") ) {
1112 		softwareList.clear();
1113 		int i = 0;
1114 		QString filter;
1115 		QStringList xmlLines = qmc2MachineList->xmlDb()->xml(systemName).split("\n", QString::SkipEmptyParts);
1116 		while ( !interruptLoad && i < xmlLines.count() && !xmlLines[i].contains("</machine>") ) {
1117 			QString line = xmlLines[i++];
1118 			if ( line.startsWith("<softwarelist name=\"") ) {
1119 				int startIndex = line.indexOf("\"") + 1;
1120 				int endIndex = line.indexOf("\"", startIndex);
1121 				softwareList << line.mid(startIndex, endIndex - startIndex);
1122 				startIndex = line.indexOf(" filter=\"");
1123 				if ( startIndex >= 0 ) {
1124 					startIndex += 9;
1125 					endIndex = line.indexOf("\"", startIndex);
1126 					filter = line.mid(startIndex, endIndex - startIndex);
1127 				}
1128 			}
1129 		}
1130 
1131 		if ( softwareList.isEmpty() )
1132 			softwareList << "NO_SOFTWARE_LIST";
1133 		else
1134 			softwareList.sort();
1135 		systemSoftwareListHash.insert(systemName, softwareList);
1136 
1137 #ifdef QMC2_DEBUG
1138 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: systemSoftwareListHash[%1] = %2").arg(systemName).arg(softwareList.join(", ")));
1139 #endif
1140 
1141 		if ( !filter.isEmpty() )
1142 			systemSoftwareFilterHash.insert(systemName, filter.split(',', QString::SkipEmptyParts));
1143 	}
1144 #ifdef QMC2_DEBUG
1145 	else
1146 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: systemSoftwareListHash[%1] = %2 (cached)").arg(systemName).arg(systemSoftwareListHash.value(systemName).join(", ")));
1147 #endif
1148 
1149 	if ( !softwareList.isEmpty() && !softwareList.contains("NO_SOFTWARE_LIST") ) {
1150 		toolBoxSoftwareList->setEnabled(true);
1151 		// load stored device configurations, if any...
1152 		qmc2Config->beginGroup(QString(QMC2_EMULATOR_PREFIX + "Configuration/Devices/%1").arg(systemName));
1153 		QStringList configurationList = qmc2Config->childGroups();
1154 		qmc2Config->endGroup();
1155 		if ( !configurationList.isEmpty() ) {
1156 			comboBoxDeviceConfiguration->insertItems(1, configurationList);
1157 			comboBoxDeviceConfiguration->setEnabled(true);
1158 		}
1159 	} else {
1160 		toolBoxSoftwareList->setItemText(QMC2_SWLIST_KNOWN_SW_PAGE, tr("Known software") + " | " + tr("no data available"));
1161 		toolBoxSoftwareList->setItemText(QMC2_SWLIST_FAVORITES_PAGE, tr("Favorites") + " | " + tr("no data available"));
1162 		toolBoxSoftwareList->setItemText(QMC2_SWLIST_SEARCH_PAGE, tr("Search") + " | " + tr("no data available"));
1163 	}
1164 }
1165 
updateMountDevices()1166 void SoftwareList::updateMountDevices()
1167 {
1168 	if ( updatingMountDevices )
1169 		return;
1170 
1171 	updatingMountDevices = true;
1172 	autoMounted = true;
1173 
1174 	QTreeWidget *treeWidget = 0;
1175 	switch ( toolBoxSoftwareList->currentIndex() ) {
1176 		case QMC2_SWLIST_KNOWN_SW_PAGE:
1177 			if ( viewTree() )
1178 				treeWidget = treeWidgetKnownSoftwareTree;
1179 			else
1180 				treeWidget = treeWidgetKnownSoftware;
1181 			break;
1182 		case QMC2_SWLIST_FAVORITES_PAGE:
1183 			treeWidget = treeWidgetFavoriteSoftware;
1184 			break;
1185 		case QMC2_SWLIST_SEARCH_PAGE:
1186 			treeWidget = treeWidgetSearchResults;
1187 			break;
1188 	}
1189 
1190 	QTreeWidgetItemIterator it(treeWidget);
1191 	while ( *it ) {
1192 		QComboBox *comboBox = (QComboBox *)treeWidget->itemWidget(*it, QMC2_SWLIST_COLUMN_PUBLISHER);
1193 		if ( comboBox ) {
1194 			comboBox->blockSignals(true);
1195 			comboBox->setUpdatesEnabled(false);
1196 			comboBox->clear();
1197 			QStringList mountList;
1198 			successfulLookups.clear();
1199 			QString mountDev(lookupMountDevice((*it)->text(QMC2_SWLIST_COLUMN_PART), (*it)->text(QMC2_SWLIST_COLUMN_INTERFACE), &mountList));
1200 			if ( mountList.count() > 0 ) {
1201 				mountList.prepend(QObject::tr("Don't mount"));
1202 				mountList.prepend(QObject::tr("Auto mount"));
1203 				comboBox->insertItems(0, mountList);
1204 				comboBox->insertSeparator(QMC2_SWLIST_MSEL_SEPARATOR);
1205 				comboBox->setCurrentIndex(QMC2_SWLIST_MSEL_AUTO_MOUNT);
1206 				if ( mountDev.isEmpty() )
1207 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
1208 				else
1209 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Mounted on:") + " " + mountDev);
1210 			} else {
1211 				(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("No mount device"));
1212 				(*it)->setText(QMC2_SWLIST_COLUMN_PUBLISHER, QObject::tr("Unmanaged"));
1213 			}
1214 			comboBox->setUpdatesEnabled(true);
1215 			comboBox->blockSignals(false);
1216 		}
1217 		it++;
1218 	}
1219 
1220 	updatingMountDevices = false;
1221 }
1222 
load()1223 bool SoftwareList::load()
1224 {
1225 	setEnabled(qmc2UseDefaultEmulator);
1226 
1227 	autoMounted = isLoading = true;
1228 	interruptLoad = fullyLoaded = false;
1229 	validData = swlSupported;
1230 	numSoftwareTotal = numSoftwareCorrect = numSoftwareIncorrect = numSoftwareMostlyCorrect = numSoftwareNotFound = numSoftwareUnknown = numSoftwareMatches = 0;
1231 	updateStats();
1232 
1233 	toolButtonReload->setEnabled(false);
1234 
1235 	treeWidgetKnownSoftware->clear();
1236 	treeWidgetKnownSoftwareTree->clear();
1237 	treeWidgetFavoriteSoftware->clear();
1238 	treeWidgetSearchResults->clear();
1239 
1240 	if ( knownSoftwareMenu )
1241 		if ( toggleListMenu )
1242 			toggleListMenu->clear();
1243 
1244 	softwareItemHash.clear();
1245 	softwareHierarchyItemHash.clear();
1246 	softwareParentHash.clear();
1247 
1248 	treeWidgetKnownSoftware->setSortingEnabled(false);
1249 	treeWidgetKnownSoftware->header()->setSortIndicatorShown(false);
1250 	treeWidgetKnownSoftwareTree->setSortingEnabled(false);
1251 	treeWidgetKnownSoftwareTree->header()->setSortIndicatorShown(false);
1252 	treeWidgetFavoriteSoftware->setSortingEnabled(false);
1253 	treeWidgetFavoriteSoftware->header()->setSortIndicatorShown(false);
1254 	treeWidgetSearchResults->setSortingEnabled(false);
1255 	treeWidgetSearchResults->header()->setSortIndicatorShown(false);
1256 
1257 	QStringList hiddenLists(userDataDb->hiddenLists(systemName));
1258 	userDataDb->getSelectedSoftware(systemName, &m_selectedSoftwareList, &m_selectedSoftwareName);
1259 
1260 	bool swlCacheOkay = (swlDb->swlRowCount() > 0) && (qmc2MachineList->emulatorVersion == swlDb->emulatorVersion());
1261 
1262 	if ( swlSupported && !swlCacheOkay ) {
1263 		isInitialLoad = true;
1264 		oldMin = qmc2MainWindow->progressBarMachineList->minimum();
1265 		oldMax = qmc2MainWindow->progressBarMachineList->maximum();
1266 		oldFmt = qmc2MainWindow->progressBarMachineList->format();
1267           	qmc2MainWindow->tabSoftwareList->setUpdatesEnabled(true);
1268 		((AspectRatioLabel *)labelLoadingSoftwareLists)->setLabelText(tr("Loading software-lists, please wait..."));
1269 		toolBoxSoftwareList->setVisible(false);
1270 		labelLoadingSoftwareLists->setVisible(true);
1271 		show();
1272 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
1273 			loadAnimMovie->start();
1274 		qApp->processEvents();
1275 		loadTimer.start();
1276 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading XML software list data and recreating cache"));
1277 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
1278 			qmc2MainWindow->progressBarMachineList->setFormat(tr("SWL data - %p%"));
1279 		else
1280 			qmc2MainWindow->progressBarMachineList->setFormat("%p%");
1281 		swlLastLine.clear();
1282 		swlDb->recreateDatabase(true);
1283 		uncommittedSwlDbRows = 0;
1284 		loadProc = new QProcess(this);
1285 		connect(loadProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(loadError(QProcess::ProcessError)));
1286 		connect(loadProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(loadFinished(int, QProcess::ExitStatus)));
1287 		connect(loadProc, SIGNAL(readyReadStandardOutput()), this, SLOT(loadReadyReadStandardOutput()));
1288 		connect(loadProc, SIGNAL(readyReadStandardError()), this, SLOT(loadReadyReadStandardError()));
1289 		connect(loadProc, SIGNAL(started()), this, SLOT(loadStarted()));
1290 
1291 		QString command(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ExecutableFile", QString()).toString());
1292 		QStringList args;
1293 		args << "-listsoftware";
1294 #if defined(QMC2_OS_WIN)
1295 		QString hashPath(qmc2Config->value(QMC2_EMULATOR_PREFIX + "Configuration/Global/hashpath", QString()).toString());
1296 #else
1297 		QString hashPath(qmc2Config->value(QMC2_EMULATOR_PREFIX + "Configuration/Global/hashpath", QString()).toString().replace("~", "$HOME"));
1298 #endif
1299 		if ( !hashPath.isEmpty() )
1300 			args << "-hashpath" << QString("%1").arg(hashPath);
1301 
1302 		if ( !qmc2LoadingInterrupted ) {
1303 			validData = true;
1304 			loadFinishedFlag = false;
1305 			QString emuWorkDir = qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/WorkingDirectory", QString()).toString();
1306 			if ( !emuWorkDir.isEmpty() )
1307 				loadProc->setWorkingDirectory(emuWorkDir);
1308 			loadProc->start(command, args);
1309 			// FIXME: this is blocking the GUI shortly
1310 			if ( loadProc->waitForStarted() && !qmc2LoadingInterrupted ) {
1311 				while ( !loadFinishedFlag && !qmc2LoadingInterrupted ) {
1312 					qApp->processEvents();
1313 #if defined(QMC2_OS_MAC)
1314 					QTest::qWait(10);
1315 #else
1316 					loadProc->waitForFinished(10);
1317 #endif
1318 				}
1319 			} else
1320 				validData = false;
1321 		}
1322 
1323 		if ( qmc2LoadingInterrupted ) {
1324 			if ( loadProc->state() == QProcess::Running ) {
1325 				loadProc->kill();
1326 				validData = false;
1327 			}
1328 		}
1329 
1330 		if ( !validData ) {
1331 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_KNOWN_SW_PAGE, tr("Known software") + " | " + tr("no data available"));
1332 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_FAVORITES_PAGE, tr("Favorites") + " | " + tr("no data available"));
1333 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_SEARCH_PAGE, tr("Search") + " | " + tr("no data available"));
1334 			labelLoadingSoftwareLists->setVisible(false);
1335 			toolBoxSoftwareList->setVisible(true);
1336 			loadAnimMovie->setPaused(true);
1337 			isLoading = false;
1338 			isInitialLoad = false;
1339 			emit loadFinished(false);
1340 			return false;
1341 		}
1342 	}
1343 
1344 	labelLoadingSoftwareLists->setVisible(false);
1345 	toolBoxSoftwareList->setVisible(true);
1346 	loadAnimMovie->setPaused(true);
1347 
1348 #ifdef QMC2_DEBUG
1349 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: SoftwareList::load(): validData = %1").arg(validData));
1350 #endif
1351 
1352 	getXmlData();
1353 
1354 	QStringList softwareList(systemSoftwareListHash.value(systemName));
1355 	bool hasSoftwareLists = !softwareList.contains("NO_SOFTWARE_LIST");
1356 	if ( hasSoftwareLists && !interruptLoad ) {
1357 		foreach (QString swList, softwareList) {
1358 			if ( interruptLoad )
1359 				break;
1360 			QString softwareListXml(getSoftwareListXmlData(swList));
1361 			if ( interruptLoad )
1362 				break;
1363 			if ( softwareListXml.size() > QMC2_SWLIST_SIZE_THRESHOLD ) {
1364 				((AspectRatioLabel *)labelLoadingSoftwareLists)->setLabelText(tr("Loading software-list '%1', please wait...").arg(swList));
1365 				toolBoxSoftwareList->setVisible(false);
1366 				labelLoadingSoftwareLists->setVisible(true);
1367 				qmc2MainWindow->tabSoftwareList->update();
1368 				if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
1369 					loadAnimMovie->start();
1370 				qApp->processEvents();
1371 			}
1372 			if ( !softwareListXml.isEmpty() ) {
1373 #ifdef QMC2_DEBUG
1374 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: SoftwareList::load(): XML data for software list '%1' follows:\n%2").arg(swList).arg(softwareListXml));
1375 #endif
1376 				QXmlInputSource xmlInputSource;
1377 				xmlInputSource.setData(softwareListXml);
1378 				SoftwareListXmlHandler xmlHandler(treeWidgetKnownSoftware, &hiddenLists);
1379 				QXmlSimpleReader xmlReader;
1380 				xmlReader.setContentHandler(&xmlHandler);
1381 				if ( !xmlReader.parse(xmlInputSource) && !interruptLoad )
1382 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: error while parsing XML data for software list '%1'").arg(swList));
1383 				else if ( xmlHandler.newSoftwareStates )
1384 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, QObject::tr("state info for software-list '%1': L:%2 C:%3 M:%4 I:%5 N:%6 U:%7").arg(swList).arg(xmlHandler.numTotal).arg(xmlHandler.numCorrect).arg(xmlHandler.numMostlyCorrect).arg(xmlHandler.numIncorrect).arg(xmlHandler.numNotFound).arg(xmlHandler.numUnknown));
1385 				numSoftwareTotal += xmlHandler.numTotal;
1386 				numSoftwareCorrect += xmlHandler.numCorrect;
1387 				numSoftwareIncorrect += xmlHandler.numIncorrect;
1388 				numSoftwareMostlyCorrect += xmlHandler.numMostlyCorrect;
1389 				numSoftwareNotFound += xmlHandler.numNotFound;
1390 				numSoftwareUnknown += xmlHandler.numUnknown;
1391 			}
1392 			updateStats();
1393 		}
1394 
1395 		if ( viewTree() )
1396 			QTimer::singleShot(0, this, SLOT(loadTree()));
1397 
1398 		labelLoadingSoftwareLists->setVisible(false);
1399 		toolBoxSoftwareList->setVisible(true);
1400 		loadAnimMovie->setPaused(true);
1401 
1402 		// load favorites / migrate from qmc2.ini to user data database if required
1403 		QStringList softwareNames;
1404 		QString oldSettingsKey(QString(QMC2_EMULATOR_PREFIX + "Favorites/%1/SoftwareNames").arg(systemName));
1405 		if ( qmc2Config->contains(oldSettingsKey) ) {
1406 			softwareNames = qmc2Config->value(oldSettingsKey).toStringList();
1407 			qmc2Config->remove(oldSettingsKey);
1408 		} else
1409 			softwareNames = userDataDb->listFavorites(systemName);
1410 		QStringList configNames;
1411 		oldSettingsKey = QString(QMC2_EMULATOR_PREFIX + "Favorites/%1/DeviceConfigs").arg(systemName);
1412 		if ( qmc2Config->contains(oldSettingsKey) ) {
1413 			configNames = qmc2Config->value(oldSettingsKey).toStringList();
1414 			qmc2Config->remove(oldSettingsKey);
1415 		} else
1416 			configNames = userDataDb->deviceConfigs(systemName);
1417 
1418 		QStringList compatFilters(systemSoftwareFilterHash.value(systemName));
1419 		QTreeWidgetItem *selectionItem = 0;
1420 		for (int i = 0; i < softwareNames.count() && !interruptLoad; i++) {
1421 			if ( interruptLoad )
1422 				break;
1423 			QString software(softwareNames.at(i));
1424 			QTreeWidgetItem *swItem = 0;
1425 			if ( softwareItemHash.contains(software) )
1426 				swItem = softwareItemHash.value(software);
1427 			if ( !swItem ) {
1428 				// try fallback to legacy method
1429 				QList<QTreeWidgetItem *> matchedSoftware = treeWidgetKnownSoftware->findItems(software, Qt::MatchExactly | Qt::MatchCaseSensitive, QMC2_SWLIST_COLUMN_NAME);
1430 				if ( !matchedSoftware.isEmpty() )
1431 					swItem = matchedSoftware.first();
1432 			}
1433 			if ( swItem ) {
1434 				SoftwareItem *item = new SoftwareItem(treeWidgetFavoriteSoftware);
1435 				item->setWhatsThis(QMC2_SWLIST_COLUMN_TITLE, swItem->whatsThis(QMC2_SWLIST_COLUMN_TITLE));
1436 				item->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, swItem->whatsThis(QMC2_SWLIST_COLUMN_NAME));
1437 				item->setWhatsThis(QMC2_SWLIST_COLUMN_PART, swItem->whatsThis(QMC2_SWLIST_COLUMN_PART));
1438 				bool softwareListHidden = hiddenLists.contains(swItem->text(QMC2_SWLIST_COLUMN_LIST));
1439 				bool showItem = true;
1440 				if ( softwareListHidden )
1441 					showItem = false;
1442 				else if ( toolButtonCompatFilterToggle->isChecked() ) {
1443 					QStringList compatList(item->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(",", QString::SkipEmptyParts));
1444 					showItem = compatList.isEmpty() || compatFilters.isEmpty();
1445 					for (int i = 0; i < compatList.count() && !showItem; i++)
1446 						for (int j = 0; j < compatFilters.count() && !showItem; j++)
1447 							showItem = (compatList.at(i) == compatFilters.at(j));
1448 				}
1449 				item->setHidden(!showItem);
1450 				item->setText(QMC2_SWLIST_COLUMN_TITLE, swItem->text(QMC2_SWLIST_COLUMN_TITLE));
1451 				item->setIcon(QMC2_SWLIST_COLUMN_TITLE, swItem->icon(QMC2_SWLIST_COLUMN_TITLE));
1452 				item->setText(QMC2_SWLIST_COLUMN_NAME, swItem->text(QMC2_SWLIST_COLUMN_NAME));
1453 				item->setText(QMC2_SWLIST_COLUMN_PUBLISHER, swItem->text(QMC2_SWLIST_COLUMN_PUBLISHER));
1454 				item->setText(QMC2_SWLIST_COLUMN_YEAR, swItem->text(QMC2_SWLIST_COLUMN_YEAR));
1455 				item->setText(QMC2_SWLIST_COLUMN_PART, swItem->text(QMC2_SWLIST_COLUMN_PART));
1456 				item->setText(QMC2_SWLIST_COLUMN_INTERFACE, swItem->text(QMC2_SWLIST_COLUMN_INTERFACE));
1457 				item->setText(QMC2_SWLIST_COLUMN_LIST, swItem->text(QMC2_SWLIST_COLUMN_LIST));
1458 				item->setText(QMC2_SWLIST_COLUMN_SUPPORTED, swItem->text(QMC2_SWLIST_COLUMN_SUPPORTED));
1459 				SoftwareItem *subItem = new SoftwareItem(item);
1460 				subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
1461 				if ( configNames.count() > i )
1462 					item->setText(QMC2_SWLIST_COLUMN_DEVICECFG, configNames.at(i));
1463 				if ( showItem && !selectionItem )
1464 					if ( m_selectedSoftwareList == item->text(QMC2_SWLIST_COLUMN_LIST) && m_selectedSoftwareName == item->text(QMC2_SWLIST_COLUMN_NAME) )
1465 						selectionItem = item;
1466 			}
1467 		}
1468 		if ( selectionItem ) {
1469 			if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/RestoreSoftwareSelection", true).toBool() ) {
1470 				treeWidgetFavoriteSoftware->clearSelection();
1471 				treeWidgetFavoriteSoftware->setCurrentItem(selectionItem);
1472 				treeWidgetFavoriteSoftware->scrollToItem(selectionItem, qmc2CursorPositioningMode);
1473 			}
1474 		}
1475 		actionSaveFavoritesToFile->setEnabled(softwareNames.count() > 0);
1476 		toolButtonFavoritesOptions->setEnabled(true);
1477 		toolButtonExport->setEnabled(true);
1478 	}
1479 
1480 	treeWidgetKnownSoftware->setSortingEnabled(true);
1481 	treeWidgetKnownSoftware->header()->setSortIndicatorShown(true);
1482 	treeWidgetKnownSoftwareTree->setSortingEnabled(true);
1483 	treeWidgetKnownSoftwareTree->header()->setSortIndicatorShown(true);
1484 	treeWidgetFavoriteSoftware->setSortingEnabled(true);
1485 	treeWidgetFavoriteSoftware->header()->setSortIndicatorShown(true);
1486 	treeWidgetSearchResults->setSortingEnabled(true);
1487 	treeWidgetSearchResults->header()->setSortIndicatorShown(true);
1488 
1489 	toolButtonReload->setEnabled(true);
1490 	toolButtonToggleSoftwareInfo->setEnabled(true);
1491 	toolButtonCompatFilterToggle->setEnabled(toolBoxSoftwareList->currentIndex() != QMC2_SWLIST_KNOWN_SW_PAGE || !viewTree());
1492 	toolButtonToggleSnapnameAdjustment->setEnabled(true);
1493 	toolButtonToggleStatenameAdjustment->setEnabled(true);
1494 	if ( hasSoftwareLists ) {
1495 		toolButtonSoftwareStates->setEnabled(true);
1496 		toolButtonAnalyzeSoftware->setEnabled(true);
1497 		toolButtonRebuildSoftware->setEnabled(true);
1498 	}
1499 
1500 	if ( hasSoftwareLists && !interruptLoad ) {
1501 		if ( knownSoftwareMenu ) {
1502 			if ( !toggleListMenu ) {
1503 				toggleListMenu = new QMenu(0);
1504 				QAction *a = knownSoftwareMenu->insertMenu(viewFlatAction, toggleListMenu);
1505 				knownSoftwareMenu->insertSeparator(viewFlatAction);
1506 				a->setText(tr("Visible software lists"));
1507 				a->setIcon(QIcon(QString::fromUtf8(":/data/img/find_softlist.png")));
1508 				QString s(tr("Select software lists to be shown / hidden"));
1509 				a->setToolTip(s); a->setStatusTip(s);
1510 			}
1511 			QStringList softwareLists(systemSoftwareListHash.value(systemName));
1512 			addMenuSectionHeader(toggleListMenu, tr("Individual"));
1513 			QString swlString;
1514 			foreach (QString list, softwareLists) {
1515 				QAction *a = toggleListMenu->addAction(list);
1516 				a->setCheckable(true);
1517 				if ( hiddenLists.contains(list) ) {
1518 					a->setChecked(false);
1519 					swlString += " " + list + " ";
1520 				} else {
1521 					a->setChecked(true);
1522 					swlString += "[" + list + "] ";
1523 				}
1524 				QString s(tr("Toggle visibility of software list '%1'").arg(list));
1525 				a->setToolTip(s); a->setStatusTip(s);
1526 				connect(a, SIGNAL(triggered()), this, SLOT(toggleSoftwareList()));
1527 			}
1528 			if ( softwareLists.count() > 1 ) {
1529 				if ( hiddenLists.count() > 0 ) {
1530 					addMenuSectionHeader(toggleListMenu, tr("All"));
1531 					QAction *a = toggleListMenu->addAction(tr("show all"));
1532 					QString s(tr("Show all software lists"));
1533 					a->setToolTip(s); a->setStatusTip(s);
1534 					connect(a, SIGNAL(triggered()), this, SLOT(showAllSoftwareLists()));
1535 				}
1536 			}
1537 			if ( softwareLists.count() > 2 ) {
1538 				addMenuSectionHeader(toggleListMenu, tr("Only"));
1539 				foreach (QString list, softwareLists) {
1540 					QAction *a = toggleListMenu->addAction(list);
1541 					QString s(tr("Show only software list '%1'").arg(list));
1542 					a->setToolTip(s); a->setStatusTip(s);
1543 					connect(a, SIGNAL(triggered()), this, SLOT(showOnlyThisSoftwareList()));
1544 				}
1545 				addMenuSectionHeader(toggleListMenu, tr("Except"));
1546 				foreach (QString list, softwareLists) {
1547 					QAction *a = toggleListMenu->addAction(list);
1548 					QString s(tr("Show all software lists except '%1'").arg(list));
1549 					a->setToolTip(s); a->setStatusTip(s);
1550 					connect(a, SIGNAL(triggered()), this, SLOT(showAllExceptThisSoftwareList()));
1551 				}
1552 			}
1553 			swlString = swlString.simplified();
1554 			QString filterString;
1555 			if ( toolButtonSoftwareStates->isChecked() && stateFilter->checkBoxStateFilter->isChecked() )
1556 				filterString = " | " + tr("filtered");
1557 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_KNOWN_SW_PAGE, tr("Known software") + " | " + swlString + filterString);
1558 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_FAVORITES_PAGE, tr("Favorites") + " | " + swlString);
1559 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_SEARCH_PAGE, tr("Search") + " | " + swlString);
1560 		}
1561 	}
1562 
1563 	isLoading = false;
1564 	fullyLoaded = !interruptLoad;
1565 	isInitialLoad = false;
1566 	emit loadFinished(true);
1567 
1568 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/RestoreSoftwareSelection", true).toBool() ) {
1569 		if ( !m_selectedSoftwareList.isEmpty() && !m_selectedSoftwareName.isEmpty() )
1570 			scrollToItem(m_selectedSoftwareList, m_selectedSoftwareName);
1571 	}
1572 
1573 	return true;
1574 }
1575 
addMenuSectionHeader(QMenu * menu,QString text)1576 void SoftwareList::addMenuSectionHeader(QMenu *menu, QString text)
1577 {
1578 	menu->addSeparator();
1579 	QWidgetAction *wa = new QWidgetAction(menu);
1580 	QLabel *sectionLabel = new QLabel("<b>&nbsp;" + text + "&nbsp;</b>", menu);
1581 	sectionLabel->setFixedHeight(sectionLabel->height() - 4);
1582 	sectionLabel->setAlignment(Qt::AlignCenter);
1583 	wa->setDefaultWidget(sectionLabel);
1584 	menu->addAction(wa);
1585 	menu->addSeparator();
1586 }
1587 
loadTree()1588 void SoftwareList::loadTree()
1589 {
1590 	QHash<QString, SoftwareItem *> parentItemHash;
1591 	QList<QTreeWidgetItem *> itemList;
1592 	QList<QTreeWidgetItem *> hideList;
1593 	QStringList hiddenLists(userDataDb->hiddenLists(systemName));
1594 	foreach (QString setKey, softwareParentHash.keys()) {
1595 		if ( interruptLoad )
1596 			break;
1597 		QString parentSetKey(softwareParentHash.value(setKey));
1598 		if ( parentSetKey == "<np>" ) {
1599 			if ( parentItemHash.contains(setKey) )
1600 				continue;
1601 			SoftwareItem *baseItem = softwareItemHash.value(setKey);
1602 			if ( baseItem ) {
1603 				SoftwareItem *parentItem = new SoftwareItem((QTreeWidget *)0);
1604 				parentItem->setText(QMC2_SWLIST_COLUMN_TITLE, baseItem->text(QMC2_SWLIST_COLUMN_TITLE));
1605 				parentItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, baseItem->icon(QMC2_SWLIST_COLUMN_TITLE));
1606 				parentItem->setText(QMC2_SWLIST_COLUMN_NAME, baseItem->text(QMC2_SWLIST_COLUMN_NAME));
1607 				parentItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, baseItem->text(QMC2_SWLIST_COLUMN_PUBLISHER));
1608 				parentItem->setText(QMC2_SWLIST_COLUMN_YEAR, baseItem->text(QMC2_SWLIST_COLUMN_YEAR));
1609 				parentItem->setText(QMC2_SWLIST_COLUMN_PART, baseItem->text(QMC2_SWLIST_COLUMN_PART));
1610 				parentItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, baseItem->text(QMC2_SWLIST_COLUMN_INTERFACE));
1611 				parentItem->setText(QMC2_SWLIST_COLUMN_LIST, baseItem->text(QMC2_SWLIST_COLUMN_LIST));
1612 				parentItem->setText(QMC2_SWLIST_COLUMN_SUPPORTED, baseItem->text(QMC2_SWLIST_COLUMN_SUPPORTED));
1613 				parentItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "p");
1614 				parentItemHash.insert(setKey, parentItem);
1615 				softwareHierarchyItemHash.insert(setKey, parentItem);
1616 				itemList << parentItem;
1617 				SoftwareItem *subItem = new SoftwareItem(parentItem);
1618 				subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
1619 				if ( hiddenLists.contains(parentItem->text(QMC2_SWLIST_COLUMN_LIST)) )
1620 					hideList << parentItem;
1621 			}
1622 		} else {
1623 			SoftwareItem *parentItem = 0;
1624 			if ( parentItemHash.contains(parentSetKey) )
1625 				parentItem = parentItemHash.value(parentSetKey);
1626 			if ( !parentItem ) {
1627 				SoftwareItem *baseItem = softwareItemHash.value(parentSetKey);
1628 				if ( baseItem ) {
1629 					parentItem = new SoftwareItem((QTreeWidget *)0);
1630 					parentItem->setText(QMC2_SWLIST_COLUMN_TITLE, baseItem->text(QMC2_SWLIST_COLUMN_TITLE));
1631 					parentItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, baseItem->icon(QMC2_SWLIST_COLUMN_TITLE));
1632 					parentItem->setText(QMC2_SWLIST_COLUMN_NAME, baseItem->text(QMC2_SWLIST_COLUMN_NAME));
1633 					parentItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, baseItem->text(QMC2_SWLIST_COLUMN_PUBLISHER));
1634 					parentItem->setText(QMC2_SWLIST_COLUMN_YEAR, baseItem->text(QMC2_SWLIST_COLUMN_YEAR));
1635 					parentItem->setText(QMC2_SWLIST_COLUMN_PART, baseItem->text(QMC2_SWLIST_COLUMN_PART));
1636 					parentItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, baseItem->text(QMC2_SWLIST_COLUMN_INTERFACE));
1637 					parentItem->setText(QMC2_SWLIST_COLUMN_LIST, baseItem->text(QMC2_SWLIST_COLUMN_LIST));
1638 					parentItem->setText(QMC2_SWLIST_COLUMN_SUPPORTED, baseItem->text(QMC2_SWLIST_COLUMN_SUPPORTED));
1639 					parentItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "p");
1640 					parentItemHash.insert(parentSetKey, parentItem);
1641 					softwareHierarchyItemHash.insert(parentSetKey, parentItem);
1642 					itemList << parentItem;
1643 					SoftwareItem *subItem = new SoftwareItem(parentItem);
1644 					subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
1645 					if ( hiddenLists.contains(parentItem->text(QMC2_SWLIST_COLUMN_LIST)) )
1646 						hideList << parentItem;
1647 				}
1648 			}
1649 			if ( parentItem ) {
1650 				SoftwareItem *baseItem = softwareItemHash.value(setKey);
1651 				if ( baseItem ) {
1652 					SoftwareItem *childItem = new SoftwareItem(parentItem);
1653 					childItem->setText(QMC2_SWLIST_COLUMN_TITLE, baseItem->text(QMC2_SWLIST_COLUMN_TITLE));
1654 					childItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, baseItem->icon(QMC2_SWLIST_COLUMN_TITLE));
1655 					childItem->setText(QMC2_SWLIST_COLUMN_NAME, baseItem->text(QMC2_SWLIST_COLUMN_NAME));
1656 					childItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, baseItem->text(QMC2_SWLIST_COLUMN_PUBLISHER));
1657 					childItem->setText(QMC2_SWLIST_COLUMN_YEAR, baseItem->text(QMC2_SWLIST_COLUMN_YEAR));
1658 					childItem->setText(QMC2_SWLIST_COLUMN_PART, baseItem->text(QMC2_SWLIST_COLUMN_PART));
1659 					childItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, baseItem->text(QMC2_SWLIST_COLUMN_INTERFACE));
1660 					childItem->setText(QMC2_SWLIST_COLUMN_LIST, baseItem->text(QMC2_SWLIST_COLUMN_LIST));
1661 					childItem->setText(QMC2_SWLIST_COLUMN_SUPPORTED, baseItem->text(QMC2_SWLIST_COLUMN_SUPPORTED));
1662 					childItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "c");
1663 					softwareHierarchyItemHash.insert(setKey, childItem);
1664 					SoftwareItem *subItem = new SoftwareItem(childItem);
1665 					subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
1666 					if ( hiddenLists.contains(childItem->text(QMC2_SWLIST_COLUMN_LIST)) )
1667 						hideList << childItem;
1668 				}
1669 			}
1670 		}
1671 	}
1672 	if ( !interruptLoad ) {
1673 		treeWidgetKnownSoftwareTree->insertTopLevelItems(0, itemList);
1674 		foreach (QTreeWidgetItem *item, hideList)
1675 			item->setHidden(true);
1676 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/RestoreSoftwareSelection", true).toBool() ) {
1677 			if ( !m_selectedSoftwareList.isEmpty() && !m_selectedSoftwareName.isEmpty() )
1678 				scrollToItem(m_selectedSoftwareList, m_selectedSoftwareName);
1679 		}
1680 	}
1681 }
1682 
scrollToItem(const QString & listName,const QString & softwareName)1683 void SoftwareList::scrollToItem(const QString &listName, const QString &softwareName)
1684 {
1685 	QString setKey(listName + ':' + softwareName);
1686 	if ( viewTree() ) {
1687 		if ( softwareHierarchyItemHash.contains(setKey) ) {
1688 			QTreeWidgetItem *item = softwareHierarchyItemHash.value(setKey);
1689 			treeWidgetKnownSoftwareTree->clearSelection();
1690 			treeWidgetKnownSoftwareTree->setCurrentItem(item);
1691 			treeWidgetKnownSoftwareTree->scrollToItem(item, qmc2CursorPositioningMode);
1692 			currentItem = item;
1693 		}
1694 	} else {
1695 		if ( softwareItemHash.contains(setKey) ) {
1696 			QTreeWidgetItem *item = softwareItemHash.value(setKey);
1697 			treeWidgetKnownSoftware->clearSelection();
1698 			treeWidgetKnownSoftware->setCurrentItem(item);
1699 			treeWidgetKnownSoftware->scrollToItem(item, qmc2CursorPositioningMode);
1700 			item->setSelected(true);
1701 			currentItem = item;
1702 		}
1703 	}
1704 }
1705 
on_toolButtonManualOpenInViewer_clicked()1706 void SoftwareList::on_toolButtonManualOpenInViewer_clicked()
1707 {
1708 	if ( !currentItem )
1709 		return;
1710 	QStringList manualPaths(userDataDb->softwareManualPaths(currentItem->text(QMC2_SWLIST_COLUMN_LIST), currentItem->text(QMC2_SWLIST_COLUMN_NAME)));
1711 	if ( manualPaths.isEmpty() ) {
1712 		QString parentKey(softwareParentHash.value(currentItem->text(QMC2_SWLIST_COLUMN_LIST) + ':' + currentItem->text(QMC2_SWLIST_COLUMN_NAME)));
1713 		if ( !parentKey.isEmpty() && parentKey != "<np>" ) {
1714 			QStringList parentWords(parentKey.split(':', QString::SkipEmptyParts));
1715 			manualPaths = userDataDb->softwareManualPaths(parentWords.at(0), parentWords.at(1));
1716 		}
1717 	}
1718 	if ( manualPaths.count() > 1 ) {
1719 		ItemSelector itemSelector(this, manualPaths);
1720 		itemSelector.setWindowTitle(tr("Manual selection"));
1721 		itemSelector.labelMessage->setText(tr("Multiple PDF manuals exist. Select the ones you want to open:"));
1722 		itemSelector.listWidgetItems->setSelectionMode(QAbstractItemView::ExtendedSelection);
1723 		if ( itemSelector.exec() != QDialog::Rejected ) {
1724 			QList<QListWidgetItem *> itemList(itemSelector.listWidgetItems->selectedItems());
1725 			for (int i = 0; i < itemList.count(); i++) {
1726 				if ( qmc2MainWindow->actionManualInternalViewer->isChecked() )
1727 					qmc2MainWindow->viewPdf(itemList.at(i)->text());
1728 				else
1729 					QDesktopServices::openUrl(QUrl::fromUserInput(itemList.at(i)->text()));
1730 			}
1731 		}
1732 	} else if ( manualPaths.count() > 0 ) {
1733 		if ( qmc2MainWindow->actionManualInternalViewer->isChecked() )
1734 			qmc2MainWindow->viewPdf(manualPaths.at(0));
1735 		else
1736 			QDesktopServices::openUrl(QUrl::fromUserInput(manualPaths.at(0)));
1737 	}
1738 }
1739 
checkSoftwareManualAvailability()1740 void SoftwareList::checkSoftwareManualAvailability()
1741 {
1742 	if ( !currentItem ) {
1743 		toolButtonManualOpenInViewer->setEnabled(false);
1744 		toolButtonManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/no_book.png")));
1745 		actionManualOpenInViewer->setEnabled(false);
1746 		actionManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/no_book.png")));
1747 		return;
1748 	}
1749 	bool enable = !userDataDb->softwareManualPaths(currentItem->text(QMC2_SWLIST_COLUMN_LIST), currentItem->text(QMC2_SWLIST_COLUMN_NAME)).isEmpty();
1750 	if ( !enable ) {
1751 		QString parentKey(softwareParentHash.value(currentItem->text(QMC2_SWLIST_COLUMN_LIST) + ':' + currentItem->text(QMC2_SWLIST_COLUMN_NAME)));
1752 		if ( !parentKey.isEmpty() && parentKey != "<np>" ) {
1753 			QStringList parentWords(parentKey.split(':', QString::SkipEmptyParts));
1754 			enable = !userDataDb->softwareManualPaths(parentWords.at(0), parentWords.at(1)).isEmpty();
1755 		}
1756 	}
1757 	toolButtonManualOpenInViewer->setEnabled(enable);
1758 	actionManualOpenInViewer->setEnabled(enable);
1759 	if ( enable ) {
1760 		toolButtonManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/book.png")));
1761 		actionManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/book.png")));
1762 	} else {
1763 		toolButtonManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/no_book.png")));
1764 		actionManualOpenInViewer->setIcon(QIcon(QString::fromUtf8(":/data/img/no_book.png")));
1765 	}
1766 }
1767 
save()1768 bool SoftwareList::save()
1769 {
1770 	if ( !fullyLoaded ) {
1771 		currentItem = enteredItem = 0;
1772 		return false;
1773 	}
1774 
1775 	QStringList softwareNames;
1776 	bool allConfigsEmpty = true;
1777 	QStringList configNames;
1778 
1779 	for (int i = 0; i < treeWidgetFavoriteSoftware->topLevelItemCount(); i++) {
1780 		QTreeWidgetItem *item = treeWidgetFavoriteSoftware->topLevelItem(i);
1781 		softwareNames << item->text(QMC2_SWLIST_COLUMN_LIST) + ":" + item->text(QMC2_SWLIST_COLUMN_NAME);
1782 		QString configName(item->text(QMC2_SWLIST_COLUMN_DEVICECFG));
1783 		if ( !configName.isEmpty() )
1784 			allConfigsEmpty = false;
1785 		configNames << configName;
1786 	}
1787 
1788 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/SaveSoftwareSelection", true).toBool() ) {
1789 		if ( currentItem )
1790 			userDataDb->setSelectedSoftware(systemName, currentItem->text(QMC2_SWLIST_COLUMN_LIST), currentItem->text(QMC2_SWLIST_COLUMN_NAME));
1791 		else {
1792 			QTreeWidgetItem *item = 0;
1793 			if ( toolBoxSoftwareList->currentIndex() == QMC2_SWLIST_FAVORITES_PAGE ) {
1794 				QList<QTreeWidgetItem *> sl(treeWidgetFavoriteSoftware->selectedItems());
1795 				if ( !sl.isEmpty() )
1796 					item = sl.at(0);
1797 			} else if ( toolBoxSoftwareList->currentIndex() == QMC2_SWLIST_SEARCH_PAGE ) {
1798 				QList<QTreeWidgetItem *> sl(treeWidgetSearchResults->selectedItems());
1799 				if ( !sl.isEmpty() )
1800 					item = sl.at(0);
1801 			}
1802 			if ( item )
1803 				userDataDb->setSelectedSoftware(systemName, item->text(QMC2_SWLIST_COLUMN_LIST), item->text(QMC2_SWLIST_COLUMN_NAME));
1804 			else
1805 				userDataDb->setSelectedSoftware(systemName, QString(), QString());
1806 		}
1807 	}
1808 
1809 	userDataDb->setListFavorites(systemName, softwareNames);
1810 	if ( allConfigsEmpty )
1811 		userDataDb->setDeviceConfigs(systemName, QStringList());
1812 	else
1813 		userDataDb->setDeviceConfigs(systemName, configNames);
1814 
1815 	currentItem = enteredItem = 0;
1816 	return true;
1817 }
1818 
leaveEvent(QEvent * e)1819 void SoftwareList::leaveEvent(QEvent *e)
1820 {
1821 	snapForced = false;
1822 
1823 	if ( qmc2SoftwareSnap )
1824 		if ( qmc2SoftwareSnap->geometry().contains(QCursor::pos()) ) {
1825 			snapForced = true;
1826 			snapTimer.start(QMC2_SWSNAP_DELAY);
1827 		}
1828 
1829 	if ( !snapForced )
1830 		cancelSoftwareSnap();
1831 	else
1832 		QTimer::singleShot(QMC2_SWSNAP_UNFORCE_DELAY, this, SLOT(checkSoftwareSnap()));
1833 
1834 	QWidget::leaveEvent(e);
1835 }
1836 
checkSoftwareSnap()1837 void SoftwareList::checkSoftwareSnap()
1838 {
1839 	if ( qmc2SoftwareSnap && qmc2SoftwareSnap->isVisible() ) {
1840 		if ( !qmc2SoftwareSnap->geometry().contains(QCursor::pos()) && !qmc2SoftwareSnap->ctxMenuRequested )
1841 			cancelSoftwareSnap();
1842 		else {
1843 			qmc2SoftwareSnap->ctxMenuRequested = qmc2SoftwareSnap->contextMenu->isVisible();
1844 			QTimer::singleShot(QMC2_SWSNAP_UNFORCE_DELAY, this, SLOT(checkSoftwareSnap()));
1845 		}
1846 	}
1847 }
1848 
updateDetail()1849 void SoftwareList::updateDetail()
1850 {
1851 	qmc2MainWindow->tabWidgetSoftwareDetail_updateCurrent();
1852 	detailUpdateTimer.stop();
1853 }
1854 
mouseMoveEvent(QMouseEvent * e)1855 void SoftwareList::mouseMoveEvent(QMouseEvent *e)
1856 {
1857 	cancelSoftwareSnap();
1858 	QWidget::mouseMoveEvent(e);
1859 }
1860 
loadStarted()1861 void SoftwareList::loadStarted()
1862 {
1863 	// we don't know how many items there are...
1864 	loadFinishedFlag = false;
1865 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
1866 		qmc2MainWindow->progressBarMachineList->setFormat(tr("SWL data - %p%"));
1867 	else
1868 		qmc2MainWindow->progressBarMachineList->setFormat("%p%");
1869 	qmc2MainWindow->progressBarMachineList->setRange(0, 0);
1870 	qmc2MainWindow->progressBarMachineList->reset();
1871 }
1872 
loadFinished(int exitCode,QProcess::ExitStatus exitStatus)1873 void SoftwareList::loadFinished(int exitCode, QProcess::ExitStatus exitStatus)
1874 {
1875 	if ( exitStatus == QProcess::NormalExit && exitCode == 0 ) {
1876 		validData = true;
1877 	} else {
1878 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the external process called to load the MAME software lists didn't exit cleanly -- exitCode = %1, exitStatus = %2").arg(exitCode).arg(exitStatus == QProcess::NormalExit ? tr("normal") : tr("crashed")));
1879 		validData = false;
1880 	}
1881 	QTime elapsedTime(0, 0, 0, 0);
1882 	elapsedTime = elapsedTime.addMSecs(loadTimer.elapsed());
1883 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading XML software list data and recreating cache, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
1884 	swlDb->commitTransaction();
1885 	uncommittedSwlDbRows = 0;
1886 	loadFinishedFlag = true;
1887 	qmc2MainWindow->progressBarMachineList->setRange(oldMin, oldMax);
1888 	qmc2MainWindow->progressBarMachineList->setFormat(oldFmt);
1889 	qmc2MainWindow->progressBarMachineList->reset();
1890 }
1891 
loadReadyReadStandardOutput()1892 void SoftwareList::loadReadyReadStandardOutput()
1893 {
1894 	QProcess *proc = (QProcess *)sender();
1895 
1896 	static QString dtdBuffer;
1897 	static QString currentListName;
1898 	static QString currentSetName;
1899 	static QString setXmlBuffer;
1900 	static bool dtdReady = false;
1901 	static bool processEventsActive = false;
1902 
1903 	// this avoids a possible crash caused by repeatedly calling qApp->processEvents() below (while qApp->processEvents() possibly hasn't returned yet which would cause the call stack to grow... and finally blow up)
1904 	if ( processEventsActive )
1905 		return;
1906 	processEventsActive = true;
1907 	if ( qmc2MainWindow->progressBarMachineList->minimum() != 0 || qmc2MainWindow->progressBarMachineList->maximum() != 0 ) {
1908 		qmc2MainWindow->progressBarMachineList->setRange(0, 0);
1909 		qmc2MainWindow->progressBarMachineList->reset();
1910 	}
1911 	// this makes the GUI much more responsive, but is HAS to be called before proc->readAllStandardOutput()!
1912 	if ( !qmc2VerifyActive )
1913 		qApp->processEvents();
1914 	processEventsActive = false;
1915 
1916 #if defined(QMC2_OS_WIN)
1917 	QString readBuffer(swlLastLine + QString::fromUtf8(proc->readAllStandardOutput()));
1918 #else
1919 	QString readBuffer(swlLastLine + proc->readAllStandardOutput());
1920 #endif
1921 
1922 	QStringList lines(readBuffer.split('\n'));
1923 
1924 	if ( readBuffer.endsWith('\n') )
1925 		swlLastLine.clear();
1926 	else {
1927 		swlLastLine = lines.last();
1928 		lines.removeLast();
1929 	}
1930 
1931 	if ( uncommittedSwlDbRows == 0 )
1932 		swlDb->beginTransaction();
1933 
1934 	foreach (QString line, lines) {
1935 		line = line.trimmed();
1936 		if ( !line.isEmpty() ) {
1937 			if ( !line.startsWith("<?xml") ) {
1938 				if ( line.startsWith("<!") )
1939 					dtdBuffer += line + "\n";
1940 				else if ( line.startsWith("]>") ) {
1941 					dtdBuffer += line;
1942 					dtdReady = true;
1943 					swlDb->setDtd(dtdBuffer);
1944 					dtdBuffer.clear();
1945 				} else if ( dtdReady ) {
1946 					int startIndex = line.indexOf("<softwarelist name=\"");
1947 					int endIndex = -1;
1948 					if ( startIndex >= 0 ) {
1949 						startIndex += 20;
1950 						endIndex = line.indexOf("\"", startIndex);
1951 						if ( endIndex >= 0 )
1952 							currentListName = line.mid(startIndex, endIndex - startIndex);
1953 					} else if ( line.startsWith("</softwarelist>") )
1954 						currentListName.clear();
1955 					else if ( !currentListName.isEmpty() ) {
1956 						startIndex = line.indexOf("<software name=\"");
1957 						if ( startIndex >= 0 ) {
1958 							startIndex += 16;
1959 							endIndex = line.indexOf("\"", startIndex);
1960 							if ( endIndex >= 0 )
1961 								currentSetName = line.mid(startIndex, endIndex - startIndex);
1962 						} else if ( line.startsWith("</software>") ) {
1963 							if ( swlDb->exists(currentListName, currentSetName) )
1964 								qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: software-list XML bug: the software name '%1' is used multiple times within software-list '%2'").arg(currentSetName).arg(currentListName));
1965 							else if ( !currentSetName.isEmpty() ) {
1966 								setXmlBuffer += line;
1967 								swlDb->setXml(currentListName, currentSetName, setXmlBuffer);
1968 								uncommittedSwlDbRows++;
1969 							}
1970 							currentSetName.clear();
1971 							setXmlBuffer.clear();
1972 						}
1973 						if ( !currentSetName.isEmpty() )
1974 							setXmlBuffer += line + "\n";
1975 					}
1976 				}
1977 			} else {
1978 				dtdBuffer.clear();
1979 				setXmlBuffer.clear();
1980 				currentListName.clear();
1981 				currentSetName.clear();
1982 				dtdReady = false;
1983 			}
1984 		}
1985 	}
1986 	if ( uncommittedSwlDbRows >= QMC2_SWLCACHE_COMMIT ) {
1987 		swlDb->commitTransaction();
1988 		uncommittedSwlDbRows = 0;
1989 	}
1990 }
1991 
loadReadyReadStandardError()1992 void SoftwareList::loadReadyReadStandardError()
1993 {
1994 	QProcess *proc = (QProcess *)sender();
1995 
1996 	QString data(proc->readAllStandardError());
1997 	data = data.trimmed();
1998 
1999 #ifdef QMC2_DEBUG
2000 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: SoftwareList::loadReadyReadStandardError(): data = '%1'").arg(data));
2001 #endif
2002 
2003 	if ( data.contains("unknown option: -listsoftware") || data.contains("Unknown command 'listsoftware' specified") ) {
2004 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the currently selected MAME emulator doesn't support software lists"));
2005 		swlSupported = false;
2006 	}
2007 }
2008 
loadError(QProcess::ProcessError processError)2009 void SoftwareList::loadError(QProcess::ProcessError processError)
2010 {
2011 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the external process called to load the MAME software lists caused an error -- processError = %1").arg(processError));
2012 	validData = false;
2013 	loadFinishedFlag = true;
2014 	qmc2MainWindow->progressBarMachineList->setRange(0, 1);
2015 	qmc2MainWindow->progressBarMachineList->reset();
2016 }
2017 
checkSoftwareStates()2018 void SoftwareList::checkSoftwareStates()
2019 {
2020 	QStringList softwareLists(systemSoftwareListHash.value(systemName));
2021 	progressBar->setFormat(tr("Checking software-states - %p%"));
2022 	progressBar->setRange(0, treeWidgetKnownSoftware->topLevelItemCount());
2023 	progressBar->setValue(0);
2024 
2025 	QWidget *focusWidget = qApp->focusWidget();
2026 	qmc2MainWindow->tabWidgetMachineList->setEnabled(false);
2027 	qmc2MainWindow->menuBar()->setEnabled(false);
2028 	qmc2MainWindow->toolbar->setEnabled(false);
2029 	actionCheckSoftwareStates->setEnabled(false);
2030 
2031 	((AspectRatioLabel *)labelLoadingSoftwareLists)->setLabelText(tr("Checking software-states, please wait..."));
2032 	toolBoxSoftwareList->setVisible(false);
2033 	labelLoadingSoftwareLists->setVisible(true);
2034 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
2035 		loadAnimMovie->start();
2036 	qApp->processEvents();
2037 
2038 	numSoftwareCorrect = numSoftwareIncorrect = numSoftwareMostlyCorrect = numSoftwareNotFound = numSoftwareUnknown = 0;
2039 	updateStats();
2040 
2041 	foreach (QString softwareList, softwareLists) {
2042 		oldSoftwareCorrect = numSoftwareCorrect;
2043 		oldSoftwareIncorrect = numSoftwareIncorrect;
2044 		oldSoftwareMostlyCorrect = numSoftwareMostlyCorrect;
2045 		oldSoftwareNotFound = numSoftwareNotFound;
2046 		oldSoftwareUnknown = numSoftwareUnknown;
2047 		if ( softwareList == "NO_SOFTWARE_LIST" )
2048 			break;
2049 		if ( verifyProc )
2050 			delete verifyProc;
2051 		verifyProc = new QProcess(this);
2052 		connect(verifyProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(verifyError(QProcess::ProcessError)));
2053 		connect(verifyProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(verifyFinished(int, QProcess::ExitStatus)));
2054 		connect(verifyProc, SIGNAL(readyReadStandardOutput()), this, SLOT(verifyReadyReadStandardOutput()));
2055 		connect(verifyProc, SIGNAL(readyReadStandardError()), this, SLOT(verifyReadyReadStandardError()));
2056 		connect(verifyProc, SIGNAL(started()), this, SLOT(verifyStarted()));
2057 		softwareListName = softwareList;
2058 		swStatesLastLine.clear();
2059 		softwareListStateHash[softwareListName].clear();
2060 		softwareListItems = treeWidgetKnownSoftware->findItems(softwareList, Qt::MatchExactly, QMC2_SWLIST_COLUMN_LIST);
2061 		favoritesListItems = treeWidgetFavoriteSoftware->findItems(softwareList, Qt::MatchExactly, QMC2_SWLIST_COLUMN_LIST);
2062 		searchListItems = treeWidgetSearchResults->findItems(softwareList, Qt::MatchExactly, QMC2_SWLIST_COLUMN_LIST);
2063 		QString softwareStateCachePath(QDir::toNativeSeparators(QDir::cleanPath(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareStateCache").toString() + '/' + softwareList + ".ssc")));
2064 		softwareStateFile.setFileName(softwareStateCachePath);
2065 		if ( !softwareStateFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) )
2066 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: can't open software state cache file '%1' for writing, please check access permissions").arg(softwareStateCachePath));
2067 		else {
2068 			softwareStateStream.setDevice(&softwareStateFile);
2069 			softwareStateStream << "# THIS FILE IS AUTO-GENERATED - PLEASE DO NOT EDIT!\n";
2070 		}
2071 		QString command(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ExecutableFile").toString());
2072 		QStringList args;
2073 		args << "-verifysoftlist" << softwareList;
2074 		QString romPath(qmc2Config->value(QMC2_EMULATOR_PREFIX + "Configuration/Global/rompath", QString()).toString().replace("~", "$HOME"));
2075 		if ( !romPath.isEmpty() )
2076 			args << "-rompath" << romPath;
2077 		QString hashPath(qmc2Config->value(QMC2_EMULATOR_PREFIX + "Configuration/Global/hashpath", QString()).toString().replace("~", "$HOME"));
2078 		if ( !hashPath.isEmpty() )
2079 			args << "-hashpath" << hashPath;
2080 		if ( !qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/WorkingDirectory", QString()).toString().isEmpty() )
2081 			verifyProc->setWorkingDirectory(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/WorkingDirectory", QString()).toString());
2082 		verifyProc->setProcessChannelMode(QProcess::MergedChannels);
2083 		verifyProc->start(command, args);
2084 		verifyReadingStdout = false;
2085 		int retries = 0;
2086 		bool started = verifyProc->waitForStarted(QMC2_PROCESS_POLL_TIME);
2087 		while ( !started && retries++ < QMC2_PROCESS_POLL_RETRIES )
2088 			started = verifyProc->waitForStarted(QMC2_PROCESS_POLL_TIME_LONG);
2089 		if ( started ) {
2090 			bool verifyProcRunning = (verifyProc->state() == QProcess::Running);
2091 			while ( !verifyProc->waitForFinished(QMC2_PROCESS_POLL_TIME) && verifyProcRunning ) {
2092 				qApp->processEvents();
2093 				verifyProcRunning = (verifyProc->state() == QProcess::Running);
2094 			}
2095 			verifyProc->waitForFinished();
2096 		} else {
2097 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't start emulator executable within a reasonable time frame, giving up") + " (" + tr("error text = %1").arg(ProcessManager::errorText(verifyProc->error())) + ")");
2098 			break;
2099 		}
2100 	}
2101 
2102 	labelLoadingSoftwareLists->setVisible(false);
2103 	toolBoxSoftwareList->setVisible(true);
2104 	loadAnimMovie->setPaused(true);
2105 	qmc2MainWindow->tabWidgetMachineList->setEnabled(true);
2106 	qmc2MainWindow->menuBar()->setEnabled(true);
2107 	qmc2MainWindow->toolbar->setEnabled(true);
2108 	actionCheckSoftwareStates->setEnabled(true);
2109 	if ( focusWidget )
2110 		focusWidget->setFocus();
2111 	QTimer::singleShot(0, progressBar, SLOT(hide()));
2112 }
2113 
verifyStarted()2114 void SoftwareList::verifyStarted()
2115 {
2116 	progressBar->setVisible(true);
2117 }
2118 
verifyFinished(int exitCode,QProcess::ExitStatus exitStatus)2119 void SoftwareList::verifyFinished(int exitCode, QProcess::ExitStatus exitStatus)
2120 {
2121 	while ( verifyReadingStdout ) {
2122 		QTest::qWait(10);
2123 		qApp->processEvents();
2124 	}
2125 
2126 	bool notFoundState = true;
2127 
2128 	if ( (exitStatus != QProcess::NormalExit || exitCode != 0) && exitCode != 2 ) {
2129 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the external process called to verify the states for software-list '%1' didn't exit cleanly -- exitCode = %2, exitStatus = %3").arg(softwareListName).arg(exitCode).arg(exitStatus == QProcess::NormalExit ? tr("normal") : tr("crashed")));
2130 		notFoundState = false;
2131 	}
2132 
2133 	for (int i = 0; i < softwareListItems.count(); i++) {
2134 		QTreeWidgetItem *softwareItem = softwareListItems.at(i);
2135 		QString softwareName(softwareItem->text(QMC2_SWLIST_COLUMN_NAME));
2136 		QTreeWidgetItem *favoriteItem = 0;
2137 		foreach (QTreeWidgetItem *item, favoritesListItems) {
2138 			if ( item->text(QMC2_SWLIST_COLUMN_NAME) == softwareName ) {
2139 				favoriteItem = item;
2140 				break;
2141 			}
2142 		}
2143 		QTreeWidgetItem *searchItem = 0;
2144 		foreach (QTreeWidgetItem *item, searchListItems) {
2145 			if ( item->text(QMC2_SWLIST_COLUMN_NAME) == softwareName ) {
2146 				searchItem = item;
2147 				break;
2148 			}
2149 		}
2150 		QString listName(softwareItem->text(QMC2_SWLIST_COLUMN_LIST));
2151 		if ( !softwareListStateHash.value(listName).contains(softwareName) ) {
2152 			progressBar->setValue(progressBar->value() + 1);
2153 			SoftwareItem *hierarchyItem = 0;
2154 			QString setKey(listName + ':' + softwareName);
2155 			if ( notFoundState ) {
2156 				softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_notfound.png")));
2157 				softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "N");
2158 				if ( stateFilter->checkBoxStateFilter->isChecked() )
2159 					softwareItem->setHidden(!stateFilter->toolButtonNotFound->isChecked());
2160 				else
2161 					softwareItem->setHidden(false);
2162 				if ( softwareHierarchyItemHash.contains(setKey) )
2163 					hierarchyItem = softwareHierarchyItemHash.value(setKey);
2164 				if ( hierarchyItem ) {
2165 					hierarchyItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_notfound.png")));
2166 					hierarchyItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "N");
2167 				}
2168 				if ( favoriteItem )
2169 					favoriteItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_notfound.png")));
2170 				if ( searchItem )
2171 					searchItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_notfound.png")));
2172 				if ( softwareStateFile.isOpen() )
2173 					softwareStateStream << softwareName << " N\n";
2174 				softwareListStateHash[listName][softwareName] = 'N';
2175 				numSoftwareNotFound++;
2176 			} else {
2177 				softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2178 				softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "U");
2179 				if ( stateFilter->checkBoxStateFilter->isChecked() )
2180 					softwareItem->setHidden(!stateFilter->toolButtonUnknown->isChecked());
2181 				else
2182 					softwareItem->setHidden(false);
2183 				if ( softwareHierarchyItemHash.contains(setKey) )
2184 					hierarchyItem = softwareHierarchyItemHash.value(setKey);
2185 				if ( hierarchyItem ) {
2186 					hierarchyItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2187 					hierarchyItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "U");
2188 				}
2189 				if ( favoriteItem )
2190 					favoriteItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2191 				if ( searchItem )
2192 					searchItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2193 				if ( softwareStateFile.isOpen() )
2194 					softwareStateStream << softwareName << " U\n";
2195 				softwareListStateHash[listName][softwareName] = 'U';
2196 				numSoftwareUnknown++;
2197 			}
2198 		}
2199 		if ( i % QMC2_SWLIST_CHECK_RESPONSE == 0 ) {
2200 			updateStats();
2201 			qApp->processEvents();
2202 		}
2203 	}
2204 	updateStats();
2205 	if ( softwareStateFile.isOpen() )
2206 		softwareStateFile.close();
2207 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("state info for software-list '%1': L:%2 C:%3 M:%4 I:%5 N:%6 U:%7").arg(softwareListName).arg(softwareListItems.count()).arg(numSoftwareCorrect - oldSoftwareCorrect).arg(numSoftwareMostlyCorrect - oldSoftwareMostlyCorrect).arg(numSoftwareIncorrect - oldSoftwareIncorrect).arg(numSoftwareNotFound - oldSoftwareNotFound).arg(numSoftwareUnknown - oldSoftwareUnknown));
2208 	if ( toolButtonCompatFilterToggle->isChecked() )
2209 		on_toolButtonCompatFilterToggle_clicked(true);
2210 	softwareListItems.clear();
2211 }
2212 
verifyReadyReadStandardOutput()2213 void SoftwareList::verifyReadyReadStandardOutput()
2214 {
2215 	// this makes the GUI much more responsive, but is HAS to be called before verifyProc->readAllStandardOutput()!
2216 	if ( QCoreApplication::hasPendingEvents() )
2217 		qApp->processEvents();
2218 	verifyReadingStdout = true;
2219 	QString s(swStatesLastLine + verifyProc->readAllStandardOutput());
2220 	QStringList lines(s.split('\n'));
2221 	if ( s.endsWith('\n') ) {
2222 		swStatesLastLine.clear();
2223 	} else {
2224 		swStatesLastLine = lines.last();
2225 		lines.removeLast();
2226 	}
2227 	foreach (QString line, lines) {
2228 		line = line.simplified();
2229 		if ( !line.isEmpty() ) {
2230 			QStringList words(line.split(QRegExp("\\s+"), QString::SkipEmptyParts));
2231 			if ( line.startsWith("romset") ) {
2232 				progressBar->setValue(progressBar->value() + 1);
2233 				QStringList romsetWords(words.at(1).split(':', QString::SkipEmptyParts));
2234 				QString listName(romsetWords.at(0));
2235 				QString softwareName(romsetWords.at(1));
2236 				QString status(words.last());
2237 				QTreeWidgetItem *softwareItem = 0;
2238 				foreach (QTreeWidgetItem *item, softwareListItems) {
2239 					if ( item->text(QMC2_SWLIST_COLUMN_NAME) == softwareName ) {
2240 						softwareItem = item;
2241 						break;
2242 					}
2243 				}
2244 				if ( !softwareItem )
2245 					continue;
2246 				QTreeWidgetItem *favoriteItem = 0;
2247 				foreach (QTreeWidgetItem *item, favoritesListItems) {
2248 					if ( item->text(QMC2_SWLIST_COLUMN_NAME) == softwareName ) {
2249 						favoriteItem = item;
2250 						break;
2251 					}
2252 				}
2253 				QTreeWidgetItem *searchItem = 0;
2254 				foreach (QTreeWidgetItem *item, searchListItems) {
2255 					if ( item->text(QMC2_SWLIST_COLUMN_NAME) == softwareName ) {
2256 						searchItem = item;
2257 						break;
2258 					}
2259 				}
2260 				char charStatus = 'U';
2261 				if ( status == "good" )
2262 					charStatus = 'C';
2263 				else if ( status == "bad" )
2264 					charStatus = 'I';
2265 				else if ( status == "available" )
2266 					charStatus = 'M';
2267 				softwareListStateHash[listName][softwareName] = charStatus;
2268 				SoftwareItem *hierarchyItem = 0;
2269 				QString setKey(listName + ':' + softwareName);
2270 				if ( softwareHierarchyItemHash.contains(setKey) )
2271 					hierarchyItem = softwareHierarchyItemHash.value(setKey);
2272 				switch ( charStatus ) {
2273 					case 'C':
2274 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_correct.png")));
2275 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "C");
2276 						if ( stateFilter->checkBoxStateFilter->isChecked() )
2277 							softwareItem->setHidden(!stateFilter->toolButtonCorrect->isChecked());
2278 						else
2279 							softwareItem->setHidden(false);
2280 						if ( hierarchyItem ) {
2281 							hierarchyItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_correct.png")));
2282 							hierarchyItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "C");
2283 						}
2284 						if ( favoriteItem )
2285 							favoriteItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_correct.png")));
2286 						if ( searchItem )
2287 							searchItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_correct.png")));
2288 						if ( softwareStateFile.isOpen() )
2289 							softwareStateStream << softwareName << " C\n";
2290 						numSoftwareCorrect++;
2291 						break;
2292 					case 'M':
2293 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_mostlycorrect.png")));
2294 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "M");
2295 						if ( stateFilter->checkBoxStateFilter->isChecked() )
2296 							softwareItem->setHidden(!stateFilter->toolButtonMostlyCorrect->isChecked());
2297 						else
2298 							softwareItem->setHidden(false);
2299 						if ( hierarchyItem ) {
2300 							hierarchyItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_mostlycorrect.png")));
2301 							hierarchyItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "M");
2302 						}
2303 						if ( favoriteItem )
2304 							favoriteItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_mostlycorrect.png")));
2305 						if ( searchItem )
2306 							searchItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_mostlycorrect.png")));
2307 						if ( softwareStateFile.isOpen() )
2308 							softwareStateStream << softwareName << " M\n";
2309 						numSoftwareMostlyCorrect++;
2310 						break;
2311 					case 'I':
2312 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_incorrect.png")));
2313 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "I");
2314 						if ( stateFilter->checkBoxStateFilter->isChecked() )
2315 							softwareItem->setHidden(!stateFilter->toolButtonIncorrect->isChecked());
2316 						else
2317 							softwareItem->setHidden(false);
2318 						if ( hierarchyItem ) {
2319 							hierarchyItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_incorrect.png")));
2320 							hierarchyItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "I");
2321 						}
2322 						if ( favoriteItem )
2323 							favoriteItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_incorrect.png")));
2324 						if ( searchItem )
2325 							searchItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_incorrect.png")));
2326 						if ( softwareStateFile.isOpen() )
2327 							softwareStateStream << softwareName << " I\n";
2328 						numSoftwareIncorrect++;
2329 						break;
2330 					case 'U':
2331 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2332 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "U");
2333 						if ( stateFilter->checkBoxStateFilter->isChecked() )
2334 							softwareItem->setHidden(!stateFilter->toolButtonUnknown->isChecked());
2335 						else
2336 							softwareItem->setHidden(false);
2337 						if ( hierarchyItem ) {
2338 							hierarchyItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2339 							hierarchyItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "U");
2340 						}
2341 						if ( favoriteItem )
2342 							favoriteItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2343 						if ( searchItem )
2344 							searchItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
2345 						if ( softwareStateFile.isOpen() )
2346 							softwareStateStream << softwareName << " U\n";
2347 						numSoftwareUnknown++;
2348 						break;
2349 				}
2350 			}
2351 		}
2352 	}
2353 	updateStats();
2354 	verifyReadingStdout = false;
2355 }
2356 
verifyReadyReadStandardError()2357 void SoftwareList::verifyReadyReadStandardError()
2358 {
2359 #ifdef QMC2_DEBUG
2360 	QString data = verifyProc->readAllStandardError();
2361 	data = data.trimmed();
2362 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: SoftwareList::verifyReadyReadStandardError(): data = '%1'").arg(data));
2363 #endif
2364 }
2365 
verifyError(QProcess::ProcessError processError)2366 void SoftwareList::verifyError(QProcess::ProcessError processError)
2367 {
2368 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the external process called to verify software-states caused an error -- processError = %1").arg(processError));
2369 	progressBar->setVisible(false);
2370 }
2371 
on_toolButtonToggleSnapnameAdjustment_clicked(bool checked)2372 void SoftwareList::on_toolButtonToggleSnapnameAdjustment_clicked(bool checked)
2373 {
2374 	if ( checked && mountedSoftware.count() > 1 )
2375 		comboBoxSnapnameDevice->show();
2376 	else
2377 		comboBoxSnapnameDevice->hide();
2378 }
2379 
on_toolButtonToggleStatenameAdjustment_clicked(bool checked)2380 void SoftwareList::on_toolButtonToggleStatenameAdjustment_clicked(bool checked)
2381 {
2382 	if ( checked && mountedSoftware.count() > 1 )
2383 		comboBoxSnapnameDevice->show();
2384 	else
2385 		comboBoxSnapnameDevice->hide();
2386 }
2387 
on_toolButtonSoftwareStates_toggled(bool checked)2388 void SoftwareList::on_toolButtonSoftwareStates_toggled(bool checked)
2389 {
2390 	QString itemText(toolBoxSoftwareList->itemText(QMC2_SWLIST_KNOWN_SW_PAGE));
2391 	itemText.remove(QRegExp(" \\| " + tr("filtered") + "$"));
2392 	if ( checked ) {
2393 		toolButtonSoftwareStates->setMenu(menuSoftwareStates);
2394 		if ( isReady ) {
2395 			if ( stateFilter->checkBoxStateFilter->isChecked() )
2396 				toolBoxSoftwareList->setItemText(QMC2_SWLIST_KNOWN_SW_PAGE, itemText + " | " + tr("filtered"));
2397 			else
2398 				toolBoxSoftwareList->setItemText(QMC2_SWLIST_KNOWN_SW_PAGE, itemText);
2399 		}
2400 	} else {
2401 		toolButtonSoftwareStates->setMenu(0);
2402 		if ( isReady )
2403 			toolBoxSoftwareList->setItemText(QMC2_SWLIST_KNOWN_SW_PAGE, itemText);
2404 	}
2405 	updateStats();
2406 	if ( isReady )
2407 		QTimer::singleShot(0, toolButtonReload, SLOT(animateClick()));
2408 	qApp->processEvents();
2409 }
2410 
on_stackedWidgetKnownSoftware_currentChanged(int index)2411 void SoftwareList::on_stackedWidgetKnownSoftware_currentChanged(int index)
2412 {
2413 	static bool previousViewWasTree = false;
2414 	QList<QTreeWidgetItem *> selectedItems;
2415 	switch ( index ) {
2416 		case QMC2_SWLIST_KNOWN_SW_PAGE_TREE:
2417 			if ( !previousViewWasTree )
2418 				treeWidgetKnownSoftwareTree->header()->restoreState(treeWidgetKnownSoftware->header()->saveState());
2419 			if ( fullyLoaded && treeWidgetKnownSoftwareTree->topLevelItemCount() == 0 )
2420 				loadTree();
2421 			selectedItems = treeWidgetKnownSoftware->selectedItems();
2422 			if ( !selectedItems.isEmpty() ) {
2423 				QTreeWidgetItem *flatItem = selectedItems.first();
2424 				while ( flatItem->parent() )
2425 					flatItem = flatItem->parent();
2426 				QString setKey(flatItem->text(QMC2_SWLIST_COLUMN_LIST) + ":" + flatItem->text(QMC2_SWLIST_COLUMN_NAME));
2427 				if ( softwareHierarchyItemHash.contains(setKey) ) {
2428 					SoftwareItem *treeItem = softwareHierarchyItemHash.value(setKey);
2429 					selectedItems = treeWidgetKnownSoftwareTree->selectedItems();
2430 					if ( toolBoxSoftwareList->currentIndex() != QMC2_SWLIST_KNOWN_SW_PAGE )
2431 						treeWidgetKnownSoftwareTree->blockSignals(true);
2432 					if ( !selectedItems.isEmpty() )
2433 						selectedItems.first()->setSelected(false);
2434 					treeItem->setSelected(true);
2435 					treeWidgetKnownSoftwareTree->setCurrentItem(treeItem);
2436 					treeWidgetKnownSoftwareTree->scrollToItem(treeItem, qmc2CursorPositioningMode);
2437 					treeWidgetKnownSoftwareTree->blockSignals(false);
2438 				}
2439 			}
2440 			if ( toolBoxSoftwareList->currentIndex() == QMC2_SWLIST_KNOWN_SW_PAGE )
2441 				toolButtonCompatFilterToggle->setEnabled(false);
2442 			previousViewWasTree = true;
2443 			break;
2444 		case QMC2_SWLIST_KNOWN_SW_PAGE_FLAT:
2445 			if ( previousViewWasTree )
2446 				treeWidgetKnownSoftware->header()->restoreState(treeWidgetKnownSoftwareTree->header()->saveState());
2447 			selectedItems = treeWidgetKnownSoftwareTree->selectedItems();
2448 			if ( !selectedItems.isEmpty() ) {
2449 				QTreeWidgetItem *treeItem = selectedItems.first();
2450 				while ( treeItem->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && treeItem->parent() )
2451 					treeItem = treeItem->parent();
2452 				QString setKey(treeItem->text(QMC2_SWLIST_COLUMN_LIST) + ":" + treeItem->text(QMC2_SWLIST_COLUMN_NAME));
2453 				if ( softwareItemHash.contains(setKey) ) {
2454 					SoftwareItem *flatItem = softwareItemHash.value(setKey);
2455 					selectedItems = treeWidgetKnownSoftware->selectedItems();
2456 					if ( toolBoxSoftwareList->currentIndex() != QMC2_SWLIST_KNOWN_SW_PAGE )
2457 						treeWidgetKnownSoftware->blockSignals(true);
2458 					if ( !selectedItems.isEmpty() )
2459 						selectedItems.first()->setSelected(false);
2460 					flatItem->setSelected(true);
2461 					treeWidgetKnownSoftware->setCurrentItem(flatItem);
2462 					treeWidgetKnownSoftware->scrollToItem(flatItem, qmc2CursorPositioningMode);
2463 					treeWidgetKnownSoftware->blockSignals(false);
2464 				}
2465 			}
2466 			toolButtonCompatFilterToggle->setEnabled(true);
2467 			previousViewWasTree = false;
2468 			break;
2469 		default:
2470 			break;
2471 	}
2472 }
2473 
on_toolButtonToggleSoftwareInfo_clicked(bool checked)2474 void SoftwareList::on_toolButtonToggleSoftwareInfo_clicked(bool checked)
2475 {
2476 	QTreeWidget *treeWidget = 0;
2477 	switch ( toolBoxSoftwareList->currentIndex() ) {
2478 		case QMC2_SWLIST_KNOWN_SW_PAGE:
2479 			if ( viewTree() )
2480 				treeWidget = treeWidgetKnownSoftwareTree;
2481 			else
2482 				treeWidget = treeWidgetKnownSoftware;
2483 			break;
2484 		case QMC2_SWLIST_FAVORITES_PAGE:
2485 			treeWidget = treeWidgetFavoriteSoftware;
2486 			break;
2487 		case QMC2_SWLIST_SEARCH_PAGE:
2488 			treeWidget = treeWidgetSearchResults;
2489 			break;
2490 	}
2491 	if ( !treeWidget ) {
2492 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2493 		qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2494 		qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2495 		return;
2496 	}
2497 	checked &= (treeWidget->selectedItems().count() > 0);
2498 	if ( checked ) {
2499 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_SOFTWARE_PAGE);
2500 		qmc2MainWindow->tabWidgetSoftwareDetail_updateCurrent();
2501 		if ( qmc2MainWindow->floatToggleButtonSoftwareDetail->isChecked() )
2502 			qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizesSoftwareDetail);
2503 	} else {
2504 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2505 		qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2506 		qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2507 	}
2508 }
2509 
on_toolButtonCompatFilterToggle_clicked(bool checked)2510 void SoftwareList::on_toolButtonCompatFilterToggle_clicked(bool checked)
2511 {
2512 	QStringList compatFilters(systemSoftwareFilterHash.value(systemName));
2513 	QStringList hiddenLists(userDataDb->hiddenLists(systemName));
2514 	for (int count = 0; count < treeWidgetKnownSoftware->topLevelItemCount(); count++) {
2515 		QTreeWidgetItem *item = treeWidgetKnownSoftware->topLevelItem(count);
2516 		QStringList compatList(item->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(',', QString::SkipEmptyParts));
2517 		bool softwareListHidden = hiddenLists.contains(item->text(QMC2_SWLIST_COLUMN_LIST));
2518 		bool showItem = true;
2519 		if ( softwareListHidden )
2520 			showItem = false;
2521 		else if ( checked ) {
2522 			showItem = compatList.isEmpty() || compatFilters.isEmpty();
2523 			for (int i = 0; i < compatList.count() && !showItem; i++)
2524 				for (int j = 0; j < compatFilters.count() && !showItem; j++)
2525 					showItem = (compatList.at(i) == compatFilters.at(j));
2526 		}
2527 		if ( toolButtonSoftwareStates->isChecked() && stateFilter->checkBoxStateFilter->isChecked() ) {
2528 			switch ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).at(0).toLatin1() ) {
2529 				case 'C':
2530 					item->setHidden(!stateFilter->toolButtonCorrect->isChecked() || !showItem);
2531 					break;
2532 				case 'M':
2533 					item->setHidden(!stateFilter->toolButtonMostlyCorrect->isChecked() || !showItem);
2534 					break;
2535 				case 'I':
2536 					item->setHidden(!stateFilter->toolButtonIncorrect->isChecked() || !showItem);
2537 					break;
2538 				case 'N':
2539 					item->setHidden(!stateFilter->toolButtonNotFound->isChecked() || !showItem);
2540 					break;
2541 				case 'U':
2542 				default:
2543 					item->setHidden(!stateFilter->toolButtonUnknown->isChecked() || !showItem);
2544 					break;
2545 			}
2546 			if ( item->isHidden() && item->isSelected() )
2547 				item->setSelected(false);
2548 		} else {
2549 			if ( !showItem ) {
2550 				item->setHidden(true);
2551 				if ( item->isSelected() )
2552 					item->setSelected(false);
2553 			} else
2554 				item->setHidden(false);
2555 		}
2556 	}
2557 	for (int count = 0; count < treeWidgetFavoriteSoftware->topLevelItemCount(); count++) {
2558 		QTreeWidgetItem *item = treeWidgetFavoriteSoftware->topLevelItem(count);
2559 		QStringList compatList(item->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(',', QString::SkipEmptyParts));
2560 		bool softwareListHidden = hiddenLists.contains(item->text(QMC2_SWLIST_COLUMN_LIST));
2561 		bool showItem = true;
2562 		if ( softwareListHidden )
2563 			showItem = false;
2564 		else if ( checked ) {
2565 			showItem = compatList.isEmpty() || compatFilters.isEmpty();
2566 			for (int i = 0; i < compatList.count() && !showItem; i++)
2567 				for (int j = 0; j < compatFilters.count() && !showItem; j++)
2568 					showItem = (compatList.at(i) == compatFilters.at(j));
2569 		}
2570 		if ( !showItem ) {
2571 			item->setHidden(true);
2572 			if ( item->isSelected() )
2573 				item->setSelected(false);
2574 		} else
2575 			item->setHidden(false);
2576 	}
2577 	for (int count = 0; count < treeWidgetSearchResults->topLevelItemCount(); count++) {
2578 		QTreeWidgetItem *item = treeWidgetSearchResults->topLevelItem(count);
2579 		QStringList compatList(item->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(',', QString::SkipEmptyParts));
2580 		bool softwareListHidden = hiddenLists.contains(item->text(QMC2_SWLIST_COLUMN_LIST));
2581 		bool showItem = true;
2582 		if ( softwareListHidden )
2583 			showItem = false;
2584 		else if ( checked ) {
2585 			showItem = compatList.isEmpty() || compatFilters.isEmpty();
2586 			for (int i = 0; i < compatList.count() && !showItem; i++)
2587 				for (int j = 0; j < compatFilters.count() && !showItem; j++)
2588 					showItem = (compatList.at(i) == compatFilters.at(j));
2589 		}
2590 		if ( !showItem ) {
2591 			item->setHidden(true);
2592 			if ( item->isSelected() )
2593 				item->setSelected(false);
2594 		} else
2595 			item->setHidden(false);
2596 	}
2597 }
2598 
on_toolButtonReload_clicked(bool)2599 void SoftwareList::on_toolButtonReload_clicked(bool)
2600 {
2601 	save();
2602 	toolBoxSoftwareList->setEnabled(false);
2603 	toolButtonAddToFavorites->setEnabled(false);
2604 	toolButtonRemoveFromFavorites->setEnabled(false);
2605 	toolButtonFavoritesOptions->setEnabled(false);
2606 	toolButtonPlay->setEnabled(false);
2607 	toolButtonPlayEmbedded->setEnabled(false);
2608 	toolButtonExport->setEnabled(false);
2609 	toolButtonToggleSoftwareInfo->setEnabled(false);
2610 	toolButtonCompatFilterToggle->setEnabled(false);
2611 	toolButtonToggleSnapnameAdjustment->setEnabled(false);
2612 	toolButtonToggleStatenameAdjustment->setEnabled(false);
2613 	toolButtonSoftwareStates->setEnabled(false);
2614 	toolButtonAnalyzeSoftware->setEnabled(false);
2615 	toolButtonRebuildSoftware->setEnabled(false);
2616 	comboBoxDeviceConfiguration->setEnabled(false);
2617 	comboBoxDeviceConfiguration->clear();
2618 	comboBoxDeviceConfiguration->insertItem(0, tr("Default configuration"));
2619 	load();
2620 	if ( !comboBoxSearch->currentText().isEmpty() )
2621 		searchTimer.start(QMC2_SEARCH_DELAY);
2622 }
2623 
on_toolButtonExport_clicked(bool)2624 void SoftwareList::on_toolButtonExport_clicked(bool)
2625 {
2626 	if ( !exporter )
2627 		exporter = new SoftwareListExporter(this);
2628 	exporter->show();
2629 }
2630 
on_toolButtonAddToFavorites_clicked(bool)2631 void SoftwareList::on_toolButtonAddToFavorites_clicked(bool)
2632 {
2633 	QList<QTreeWidgetItem *> selectedItems;
2634 	switch ( toolBoxSoftwareList->currentIndex() ) {
2635 		case QMC2_SWLIST_KNOWN_SW_PAGE:
2636 			if ( viewTree() )
2637 				selectedItems = treeWidgetKnownSoftwareTree->selectedItems();
2638 			else
2639 				selectedItems = treeWidgetKnownSoftware->selectedItems();
2640 			break;
2641 		case QMC2_SWLIST_SEARCH_PAGE:
2642 			selectedItems = treeWidgetSearchResults->selectedItems();
2643 			break;
2644 		case QMC2_SWLIST_FAVORITES_PAGE:
2645 			selectedItems = treeWidgetFavoriteSoftware->selectedItems();
2646 			break;
2647 	}
2648 	QTreeWidgetItem *si = 0;
2649 	if ( selectedItems.count() > 0 )
2650 		si = selectedItems.at(0);
2651 	QStringList compatFilters(systemSoftwareFilterHash.value(systemName));
2652 	QStringList hiddenLists(userDataDb->hiddenLists(systemName));
2653 	if ( si ) {
2654 		if ( viewTree() ) {
2655 			while ( si->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && si->parent() )
2656 				si = si->parent();
2657 		} else {
2658 			while ( si->parent() )
2659 				si = si->parent();
2660 		}
2661 		SoftwareItem *item = 0;
2662 		QList<QTreeWidgetItem *> matchedItems = treeWidgetFavoriteSoftware->findItems(si->text(QMC2_SWLIST_COLUMN_NAME), Qt::MatchExactly | Qt::MatchCaseSensitive, QMC2_SWLIST_COLUMN_NAME);
2663 		if ( matchedItems.count() > 0 )
2664 			item = (SoftwareItem *)matchedItems.at(0);
2665 		else {
2666 			item = new SoftwareItem(treeWidgetFavoriteSoftware);
2667 			SoftwareItem *subItem = new SoftwareItem(item);
2668 			subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
2669 		}
2670 		if ( item ) {
2671 			item->setText(QMC2_SWLIST_COLUMN_TITLE, si->text(QMC2_SWLIST_COLUMN_TITLE));
2672 			item->setIcon(QMC2_SWLIST_COLUMN_TITLE, si->icon(QMC2_SWLIST_COLUMN_TITLE));
2673 			item->setWhatsThis(QMC2_SWLIST_COLUMN_TITLE, si->whatsThis(QMC2_SWLIST_COLUMN_TITLE));
2674 			item->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, si->whatsThis(QMC2_SWLIST_COLUMN_NAME));
2675 			item->setWhatsThis(QMC2_SWLIST_COLUMN_PART, si->whatsThis(QMC2_SWLIST_COLUMN_PART));
2676 			QStringList compatList = item->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(",", QString::SkipEmptyParts);
2677 			bool softwareListHidden = hiddenLists.contains(item->text(QMC2_SWLIST_COLUMN_LIST));
2678 			bool showItem = true;
2679 			if ( softwareListHidden )
2680 				showItem = false;
2681 			else if ( toolButtonCompatFilterToggle->isChecked() ) {
2682 				showItem = compatList.isEmpty() || compatFilters.isEmpty();
2683 				for (int i = 0; i < compatList.count() && !showItem; i++)
2684 					for (int j = 0; j < compatFilters.count() && !showItem; j++)
2685 						showItem = (compatList.at(i) == compatFilters.at(j));
2686 			}
2687 			item->setHidden(!showItem);
2688 			item->setText(QMC2_SWLIST_COLUMN_NAME, si->text(QMC2_SWLIST_COLUMN_NAME));
2689 			item->setText(QMC2_SWLIST_COLUMN_PUBLISHER, si->text(QMC2_SWLIST_COLUMN_PUBLISHER));
2690 			item->setText(QMC2_SWLIST_COLUMN_YEAR, si->text(QMC2_SWLIST_COLUMN_YEAR));
2691 			item->setText(QMC2_SWLIST_COLUMN_PART, si->text(QMC2_SWLIST_COLUMN_PART));
2692 			item->setText(QMC2_SWLIST_COLUMN_INTERFACE, si->text(QMC2_SWLIST_COLUMN_INTERFACE));
2693 			item->setText(QMC2_SWLIST_COLUMN_LIST, si->text(QMC2_SWLIST_COLUMN_LIST));
2694 			item->setText(QMC2_SWLIST_COLUMN_SUPPORTED, si->text(QMC2_SWLIST_COLUMN_SUPPORTED));
2695 			if ( comboBoxDeviceConfiguration->currentIndex() > 0 )
2696 				item->setText(QMC2_SWLIST_COLUMN_DEVICECFG, comboBoxDeviceConfiguration->currentText());
2697 			else
2698 				item->setText(QMC2_SWLIST_COLUMN_DEVICECFG, QString());
2699 		}
2700 	}
2701 	actionSaveFavoritesToFile->setEnabled(treeWidgetFavoriteSoftware->topLevelItemCount() > 0);
2702 }
2703 
on_toolButtonRemoveFromFavorites_clicked(bool)2704 void SoftwareList::on_toolButtonRemoveFromFavorites_clicked(bool)
2705 {
2706 	if ( toolBoxSoftwareList->currentIndex() != QMC2_SWLIST_FAVORITES_PAGE )
2707 		return;
2708 	QList<QTreeWidgetItem *> selectedItems = treeWidgetFavoriteSoftware->selectedItems();
2709 	QTreeWidgetItem *si = 0;
2710 	if ( selectedItems.count() > 0 ) {
2711 		si = selectedItems.at(0);
2712 		while ( si->parent() )
2713 			si = si->parent();
2714 	}
2715 	if ( si ) {
2716 		QTreeWidgetItem *itemToBeRemoved = treeWidgetFavoriteSoftware->takeTopLevelItem(treeWidgetFavoriteSoftware->indexOfTopLevelItem(si));
2717 		if ( itemToBeRemoved )
2718 			delete itemToBeRemoved;
2719 	}
2720 	actionSaveFavoritesToFile->setEnabled(treeWidgetFavoriteSoftware->topLevelItemCount() > 0);
2721 }
2722 
on_toolButtonPlay_clicked(bool)2723 void SoftwareList::on_toolButtonPlay_clicked(bool)
2724 {
2725 	QTimer::singleShot(0, qmc2MainWindow, SLOT(on_actionPlay_triggered()));
2726 }
2727 
on_toolButtonPlayEmbedded_clicked(bool)2728 void SoftwareList::on_toolButtonPlayEmbedded_clicked(bool)
2729 {
2730 	QTimer::singleShot(0, qmc2MainWindow, SLOT(on_actionPlayEmbedded_triggered()));
2731 }
2732 
treeWidgetKnownSoftware_headerSectionClicked(int)2733 void SoftwareList::treeWidgetKnownSoftware_headerSectionClicked(int)
2734 {
2735 	QList<QTreeWidgetItem *> selectedItems = treeWidgetKnownSoftware->selectedItems();
2736 	if ( !selectedItems.isEmpty() )
2737 		treeWidgetKnownSoftware->scrollToItem(selectedItems.first(), qmc2CursorPositioningMode);
2738 }
2739 
treeWidgetKnownSoftwareTree_headerSectionClicked(int)2740 void SoftwareList::treeWidgetKnownSoftwareTree_headerSectionClicked(int)
2741 {
2742 	QList<QTreeWidgetItem *> selectedItems = treeWidgetKnownSoftwareTree->selectedItems();
2743 	if ( !selectedItems.isEmpty() )
2744 		treeWidgetKnownSoftwareTree->scrollToItem(selectedItems.first(), qmc2CursorPositioningMode);
2745 }
2746 
treeWidgetFavoriteSoftware_headerSectionClicked(int)2747 void SoftwareList::treeWidgetFavoriteSoftware_headerSectionClicked(int)
2748 {
2749 	QList<QTreeWidgetItem *> selectedItems = treeWidgetFavoriteSoftware->selectedItems();
2750 	if ( !selectedItems.isEmpty() )
2751 		treeWidgetFavoriteSoftware->scrollToItem(selectedItems.first(), qmc2CursorPositioningMode);
2752 }
2753 
treeWidgetSearchResults_headerSectionClicked(int)2754 void SoftwareList::treeWidgetSearchResults_headerSectionClicked(int)
2755 {
2756 	QList<QTreeWidgetItem *> selectedItems = treeWidgetSearchResults->selectedItems();
2757 	if ( !selectedItems.isEmpty() )
2758 		treeWidgetSearchResults->scrollToItem(selectedItems.first(), qmc2CursorPositioningMode);
2759 }
2760 
on_treeWidgetKnownSoftware_itemExpanded(QTreeWidgetItem * item)2761 void SoftwareList::on_treeWidgetKnownSoftware_itemExpanded(QTreeWidgetItem *item)
2762 {
2763 	if ( item->childCount() < 1 )
2764 		return;
2765 	if ( item->child(0)->text(QMC2_SWLIST_COLUMN_TITLE) == MachineList::trWaitingForData ) {
2766 		QString softwareXml(swlDb->xml(item->text(QMC2_SWLIST_COLUMN_LIST), item->text(QMC2_SWLIST_COLUMN_NAME)));
2767 		if ( !softwareXml.isEmpty() ) {
2768 			QXmlInputSource xmlInputSource;
2769 			xmlInputSource.setData(softwareXml);
2770 			successfulLookups.clear();
2771 			SoftwareEntryXmlHandler xmlHandler(item);
2772 			QXmlSimpleReader xmlReader;
2773 			xmlReader.setContentHandler(&xmlHandler);
2774 			xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
2775 			xmlReader.setFeature("http://trolltech.com/xml/features/report-whitespace-only-CharData", false);
2776 			treeWidgetKnownSoftware->setSortingEnabled(false);
2777 			item->child(0)->setText(QMC2_SWLIST_COLUMN_TITLE, tr("Searching"));
2778 			treeWidgetKnownSoftware->viewport()->update();
2779 			qApp->processEvents();
2780 			if ( !xmlReader.parse(xmlInputSource) )
2781 				if ( !xmlHandler.success )
2782 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: error while parsing XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2783 			treeWidgetKnownSoftware->setSortingEnabled(true);
2784 		} else
2785 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: couldn't find XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2786 	}
2787 }
2788 
on_treeWidgetKnownSoftwareTree_itemExpanded(QTreeWidgetItem * item)2789 void SoftwareList::on_treeWidgetKnownSoftwareTree_itemExpanded(QTreeWidgetItem *item)
2790 {
2791 	if ( item->childCount() < 1 )
2792 		return;
2793 	if ( item->child(0)->text(QMC2_SWLIST_COLUMN_TITLE) == MachineList::trWaitingForData ) {
2794 		QString softwareXml(swlDb->xml(item->text(QMC2_SWLIST_COLUMN_LIST), item->text(QMC2_SWLIST_COLUMN_NAME)));
2795 		if ( !softwareXml.isEmpty() ) {
2796 			QXmlInputSource xmlInputSource;
2797 			xmlInputSource.setData(softwareXml);
2798 			successfulLookups.clear();
2799 			SoftwareEntryXmlHandler xmlHandler(item, true);
2800 			QXmlSimpleReader xmlReader;
2801 			xmlReader.setContentHandler(&xmlHandler);
2802 			xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
2803 			xmlReader.setFeature("http://trolltech.com/xml/features/report-whitespace-only-CharData", false);
2804 			treeWidgetKnownSoftwareTree->setSortingEnabled(false);
2805 			item->child(0)->setText(QMC2_SWLIST_COLUMN_TITLE, tr("Searching"));
2806 			treeWidgetKnownSoftwareTree->viewport()->update();
2807 			qApp->processEvents();
2808 			if ( !xmlReader.parse(xmlInputSource) )
2809 				if ( !xmlHandler.success )
2810 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: error while parsing XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2811 			treeWidgetKnownSoftwareTree->setSortingEnabled(true);
2812 		} else
2813 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: couldn't find XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2814 	}
2815 }
2816 
on_treeWidgetFavoriteSoftware_itemExpanded(QTreeWidgetItem * item)2817 void SoftwareList::on_treeWidgetFavoriteSoftware_itemExpanded(QTreeWidgetItem *item)
2818 {
2819 	if ( item->childCount() < 1 )
2820 		return;
2821 	if ( item->child(0)->text(QMC2_SWLIST_COLUMN_TITLE) == MachineList::trWaitingForData ) {
2822 		QString softwareXml(swlDb->xml(item->text(QMC2_SWLIST_COLUMN_LIST), item->text(QMC2_SWLIST_COLUMN_NAME)));
2823 		if ( !softwareXml.isEmpty() ) {
2824 			QXmlInputSource xmlInputSource;
2825 			xmlInputSource.setData(softwareXml);
2826 			successfulLookups.clear();
2827 			SoftwareEntryXmlHandler xmlHandler(item);
2828 			QXmlSimpleReader xmlReader;
2829 			xmlReader.setContentHandler(&xmlHandler);
2830 			xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
2831 			xmlReader.setFeature("http://trolltech.com/xml/features/report-whitespace-only-CharData", false);
2832 			treeWidgetFavoriteSoftware->setSortingEnabled(false);
2833 			item->child(0)->setText(QMC2_SWLIST_COLUMN_TITLE, tr("Searching"));
2834 			treeWidgetFavoriteSoftware->viewport()->update();
2835 			qApp->processEvents();
2836 			if ( !xmlReader.parse(xmlInputSource) )
2837 				if ( !xmlHandler.success )
2838 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: error while parsing XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2839 			treeWidgetFavoriteSoftware->setSortingEnabled(true);
2840 		} else
2841 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: couldn't find XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2842 	}
2843 }
2844 
on_treeWidgetSearchResults_itemExpanded(QTreeWidgetItem * item)2845 void SoftwareList::on_treeWidgetSearchResults_itemExpanded(QTreeWidgetItem *item)
2846 {
2847 	if ( item->childCount() < 1 )
2848 		return;
2849 	if ( item->child(0)->text(QMC2_SWLIST_COLUMN_TITLE) == MachineList::trWaitingForData ) {
2850 		QString softwareXml(swlDb->xml(item->text(QMC2_SWLIST_COLUMN_LIST), item->text(QMC2_SWLIST_COLUMN_NAME)));
2851 		if ( !softwareXml.isEmpty() ) {
2852 			QXmlInputSource xmlInputSource;
2853 			xmlInputSource.setData(softwareXml);
2854 			successfulLookups.clear();
2855 			SoftwareEntryXmlHandler xmlHandler(item);
2856 			QXmlSimpleReader xmlReader;
2857 			xmlReader.setContentHandler(&xmlHandler);
2858 			xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
2859 			xmlReader.setFeature("http://trolltech.com/xml/features/report-whitespace-only-CharData", false);
2860 			treeWidgetSearchResults->setSortingEnabled(false);
2861 			item->child(0)->setText(QMC2_SWLIST_COLUMN_TITLE, tr("Searching"));
2862 			treeWidgetSearchResults->viewport()->update();
2863 			qApp->processEvents();
2864 			if ( !xmlReader.parse(xmlInputSource) )
2865 				if ( !xmlHandler.success )
2866 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: error while parsing XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2867 			treeWidgetSearchResults->setSortingEnabled(true);
2868 		} else
2869 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: couldn't find XML data for software list entry '%1:%2'").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)));
2870 	}
2871 }
2872 
on_toolBoxSoftwareList_currentChanged(int index)2873 void SoftwareList::on_toolBoxSoftwareList_currentChanged(int index)
2874 {
2875 	comboBoxDeviceConfiguration->setCurrentIndex(0);
2876 	switch ( index ) {
2877 		case QMC2_SWLIST_KNOWN_SW_PAGE:
2878 			updateMountDevices();
2879 			if ( viewTree() )
2880 				on_treeWidgetKnownSoftwareTree_itemSelectionChanged();
2881 			else
2882 				on_treeWidgetKnownSoftware_itemSelectionChanged();
2883 			toolButtonCompatFilterToggle->setEnabled(!viewTree());
2884 			break;
2885 		case QMC2_SWLIST_FAVORITES_PAGE:
2886 			on_treeWidgetFavoriteSoftware_itemSelectionChanged();
2887 			toolButtonCompatFilterToggle->setEnabled(true);
2888 			break;
2889 		case QMC2_SWLIST_SEARCH_PAGE:
2890 			updateMountDevices();
2891 			on_treeWidgetSearchResults_itemSelectionChanged();
2892 			toolButtonCompatFilterToggle->setEnabled(true);
2893 			break;
2894 		default:
2895 			break;
2896 	}
2897 }
2898 
on_treeWidgetKnownSoftware_itemSelectionChanged()2899 void SoftwareList::on_treeWidgetKnownSoftware_itemSelectionChanged()
2900 {
2901 	QList<QTreeWidgetItem *> selectedItems = treeWidgetKnownSoftware->selectedItems();
2902 	bool enable = !selectedItems.isEmpty();
2903 	toolButtonPlay->setEnabled(enable);
2904 	toolButtonPlayEmbedded->setEnabled(enable);
2905 	toolButtonAddToFavorites->setEnabled(enable);
2906 	if ( enable && qmc2SoftwareSnap ) {
2907 		SoftwareImageWidget::updateArtwork();
2908 		SoftwareItem *item = (SoftwareItem *)selectedItems.first();
2909 		qmc2SoftwareSnap->snapForcedResetTimer.stop();
2910 		snapForced = true;
2911 		qmc2SoftwareSnap->myItem = item;
2912 		snapTimer.start(QMC2_SWSNAP_DELAY);
2913 		if ( toolButtonToggleSoftwareInfo->isChecked() ) {
2914 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_SOFTWARE_PAGE);
2915 			if ( qmc2MainWindow->floatToggleButtonSoftwareDetail->isChecked() )
2916 				qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizesSoftwareDetail);
2917 		} else {
2918 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2919 			qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2920 			qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2921 		}
2922 		currentItem = item;
2923 		while ( currentItem->parent() )
2924 			currentItem = currentItem->parent();
2925 		if ( qmc2MainWindow->stackedWidgetSpecial->currentIndex() == QMC2_SPECIAL_SOFTWARE_PAGE )
2926 			detailUpdateTimer.start(qmc2UpdateDelay);
2927 		QTimer::singleShot(0, this, SLOT(checkSoftwareManualAvailability()));
2928 	} else {
2929 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2930 		qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2931 		qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2932 		cancelSoftwareSnap();
2933 		currentItem = 0;
2934 	}
2935 }
2936 
on_treeWidgetKnownSoftwareTree_itemSelectionChanged()2937 void SoftwareList::on_treeWidgetKnownSoftwareTree_itemSelectionChanged()
2938 {
2939 	QList<QTreeWidgetItem *> selectedItems = treeWidgetKnownSoftwareTree->selectedItems();
2940 	bool enable = !selectedItems.isEmpty();
2941 	toolButtonPlay->setEnabled(enable);
2942 	toolButtonPlayEmbedded->setEnabled(enable);
2943 	toolButtonAddToFavorites->setEnabled(enable);
2944 	if ( enable && qmc2SoftwareSnap ) {
2945 		SoftwareImageWidget::updateArtwork();
2946 		SoftwareItem *item = (SoftwareItem *)selectedItems.first();
2947 		qmc2SoftwareSnap->snapForcedResetTimer.stop();
2948 		snapForced = true;
2949 		qmc2SoftwareSnap->myItem = item;
2950 		snapTimer.start(QMC2_SWSNAP_DELAY);
2951 		if ( toolButtonToggleSoftwareInfo->isChecked() ) {
2952 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_SOFTWARE_PAGE);
2953 			if ( qmc2MainWindow->floatToggleButtonSoftwareDetail->isChecked() )
2954 				qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizesSoftwareDetail);
2955 		} else {
2956 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2957 			qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2958 			qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2959 		}
2960 		currentItem = item;
2961 		while ( currentItem->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && currentItem->parent() )
2962 			currentItem = currentItem->parent();
2963 		if ( qmc2MainWindow->stackedWidgetSpecial->currentIndex() == QMC2_SPECIAL_SOFTWARE_PAGE )
2964 			detailUpdateTimer.start(qmc2UpdateDelay);
2965 		QTimer::singleShot(0, this, SLOT(checkSoftwareManualAvailability()));
2966 	} else {
2967 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2968 		qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2969 		qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2970 		cancelSoftwareSnap();
2971 		currentItem = 0;
2972 	}
2973 }
2974 
on_treeWidgetFavoriteSoftware_itemSelectionChanged()2975 void SoftwareList::on_treeWidgetFavoriteSoftware_itemSelectionChanged()
2976 {
2977 	QList<QTreeWidgetItem *> selectedItems = treeWidgetFavoriteSoftware->selectedItems();
2978 	bool enable = !selectedItems.isEmpty();
2979 	toolButtonPlay->setEnabled(enable);
2980 	toolButtonPlayEmbedded->setEnabled(enable);
2981 	toolButtonRemoveFromFavorites->setEnabled(enable);
2982 	toolButtonAddToFavorites->setEnabled(enable);
2983 	if ( enable && qmc2SoftwareSnap ) {
2984 		SoftwareImageWidget::updateArtwork();
2985 		SoftwareItem *item = (SoftwareItem *)selectedItems.first();
2986 		qmc2SoftwareSnap->snapForcedResetTimer.stop();
2987 		snapForced = true;
2988 		qmc2SoftwareSnap->myItem = item;
2989 		snapTimer.start(QMC2_SWSNAP_DELAY);
2990 		if ( toolButtonToggleSoftwareInfo->isChecked() ) {
2991 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_SOFTWARE_PAGE);
2992 			if ( qmc2MainWindow->floatToggleButtonSoftwareDetail->isChecked() )
2993 				qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizesSoftwareDetail);
2994 		} else {
2995 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
2996 			qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
2997 			qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
2998 		}
2999 		currentItem = item;
3000 		while ( currentItem->parent() )
3001 			currentItem = currentItem->parent();
3002 		if ( qmc2MainWindow->stackedWidgetSpecial->currentIndex() == QMC2_SPECIAL_SOFTWARE_PAGE )
3003 			detailUpdateTimer.start(qmc2UpdateDelay);
3004 		QTimer::singleShot(0, this, SLOT(checkSoftwareManualAvailability()));
3005 	} else {
3006 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
3007 		qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
3008 		qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
3009 		cancelSoftwareSnap();
3010 		currentItem = 0;
3011 	}
3012 	if ( enable ) {
3013 		QTreeWidgetItem *item = selectedItems.first();
3014 		while ( item->parent() )
3015 			item = item->parent();
3016 		QString s(item->text(QMC2_SWLIST_COLUMN_DEVICECFG));
3017 		if ( !s.isEmpty() ) {
3018 			int index = comboBoxDeviceConfiguration->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive);
3019 			if ( index > 0 )
3020 				comboBoxDeviceConfiguration->setCurrentIndex(index);
3021 			else
3022 				comboBoxDeviceConfiguration->setCurrentIndex(0);
3023 		} else
3024 			comboBoxDeviceConfiguration->setCurrentIndex(0);
3025 	}
3026 }
3027 
on_treeWidgetSearchResults_itemSelectionChanged()3028 void SoftwareList::on_treeWidgetSearchResults_itemSelectionChanged()
3029 {
3030 	QList<QTreeWidgetItem *> selectedItems = treeWidgetSearchResults->selectedItems();
3031 	bool enable = !selectedItems.isEmpty();
3032 	toolButtonPlay->setEnabled(enable);
3033 	toolButtonPlayEmbedded->setEnabled(enable);
3034 	toolButtonAddToFavorites->setEnabled(enable);
3035 	toolButtonRemoveFromFavorites->setEnabled(false);
3036 	if ( enable && qmc2SoftwareSnap ) {
3037 		SoftwareImageWidget::updateArtwork();
3038 		SoftwareItem *item = (SoftwareItem *)selectedItems.first();
3039 		qmc2SoftwareSnap->snapForcedResetTimer.stop();
3040 		snapForced = true;
3041 		qmc2SoftwareSnap->myItem = item;
3042 		snapTimer.start(QMC2_SWSNAP_DELAY);
3043 		if ( toolButtonToggleSoftwareInfo->isChecked() ) {
3044 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_SOFTWARE_PAGE);
3045 			if ( qmc2MainWindow->floatToggleButtonSoftwareDetail->isChecked() )
3046 				qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizesSoftwareDetail);
3047 		} else {
3048 			qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
3049 			qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
3050 			qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
3051 		}
3052 		currentItem = item;
3053 		while ( currentItem->parent() )
3054 			currentItem = currentItem->parent();
3055 		if ( qmc2MainWindow->stackedWidgetSpecial->currentIndex() == QMC2_SPECIAL_SOFTWARE_PAGE )
3056 			detailUpdateTimer.start(qmc2UpdateDelay);
3057 		QTimer::singleShot(0, this, SLOT(checkSoftwareManualAvailability()));
3058 	} else {
3059 		qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
3060 		qmc2MainWindow->on_tabWidgetLogsAndEmulators_currentChanged(qmc2MainWindow->tabWidgetLogsAndEmulators->currentIndex());
3061 		qmc2MainWindow->vSplitter->setSizes(qmc2MainWindow->vSplitterSizes);
3062 		cancelSoftwareSnap();
3063 		currentItem = 0;
3064 	}
3065 }
3066 
on_treeWidgetKnownSoftware_customContextMenuRequested(const QPoint & p)3067 void SoftwareList::on_treeWidgetKnownSoftware_customContextMenuRequested(const QPoint &p)
3068 {
3069 	cancelSoftwareSnap();
3070 	QTreeWidgetItem *item = treeWidgetKnownSoftware->itemAt(p);
3071 	if ( !item )
3072 		return;
3073 	treeWidgetKnownSoftware->setItemSelected(item, true);
3074 	actionAddToFavorites->setVisible(true);
3075 	actionRemoveFromFavorites->setVisible(false);
3076 	updateRebuildSoftwareMenuVisibility();
3077 	softwareListMenu->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetKnownSoftware->viewport()->mapToGlobal(p), softwareListMenu));
3078 	softwareListMenu->show();
3079 }
3080 
on_treeWidgetKnownSoftwareTree_customContextMenuRequested(const QPoint & p)3081 void SoftwareList::on_treeWidgetKnownSoftwareTree_customContextMenuRequested(const QPoint &p)
3082 {
3083 	cancelSoftwareSnap();
3084 	QTreeWidgetItem *item = treeWidgetKnownSoftwareTree->itemAt(p);
3085 	if ( !item )
3086 		return;
3087 	treeWidgetKnownSoftwareTree->setItemSelected(item, true);
3088 	actionAddToFavorites->setVisible(true);
3089 	actionRemoveFromFavorites->setVisible(false);
3090 	updateRebuildSoftwareMenuVisibility();
3091 	softwareListMenu->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(p), softwareListMenu));
3092 	softwareListMenu->show();
3093 }
3094 
on_treeWidgetFavoriteSoftware_customContextMenuRequested(const QPoint & p)3095 void SoftwareList::on_treeWidgetFavoriteSoftware_customContextMenuRequested(const QPoint &p)
3096 {
3097 	cancelSoftwareSnap();
3098 	QTreeWidgetItem *item = treeWidgetFavoriteSoftware->itemAt(p);
3099 	if ( !item )
3100 		return;
3101 	treeWidgetFavoriteSoftware->setItemSelected(item, true);
3102 	actionAddToFavorites->setVisible(false);
3103 	actionRemoveFromFavorites->setVisible(true);
3104 	updateRebuildSoftwareMenuVisibility();
3105 	softwareListMenu->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetFavoriteSoftware->viewport()->mapToGlobal(p), softwareListMenu));
3106 	softwareListMenu->show();
3107 }
3108 
on_treeWidgetSearchResults_customContextMenuRequested(const QPoint & p)3109 void SoftwareList::on_treeWidgetSearchResults_customContextMenuRequested(const QPoint &p)
3110 {
3111 	cancelSoftwareSnap();
3112 	QTreeWidgetItem *item = treeWidgetSearchResults->itemAt(p);
3113 	if ( !item )
3114 		return;
3115 	treeWidgetSearchResults->setItemSelected(item, true);
3116 	actionAddToFavorites->setVisible(true);
3117 	actionRemoveFromFavorites->setVisible(false);
3118 	updateRebuildSoftwareMenuVisibility();
3119 	softwareListMenu->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetSearchResults->viewport()->mapToGlobal(p), softwareListMenu));
3120 	softwareListMenu->show();
3121 }
3122 
cancelSoftwareSnap()3123 void SoftwareList::cancelSoftwareSnap()
3124 {
3125 	snapForced = false;
3126 	snapTimer.stop();
3127 	if ( qmc2SoftwareSnap ) {
3128 		qmc2SoftwareSnap->myItem = 0;
3129 		qmc2SoftwareSnap->hide();
3130 	}
3131 }
3132 
on_treeWidgetKnownSoftware_itemEntered(QTreeWidgetItem * item,int)3133 void SoftwareList::on_treeWidgetKnownSoftware_itemEntered(QTreeWidgetItem *item, int)
3134 {
3135 	if ( item == enteredItem )
3136 		return;
3137 	enteredItem = item;
3138 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapOnMouseHover", false).toBool() )
3139 		return;
3140 	if ( !snapForced ) {
3141 		if ( qmc2SoftwareSnap ) {
3142 			if ( qmc2SoftwareSnap->myItem != item ) {
3143 				qmc2SoftwareSnap->myItem = (SoftwareItem *)item;
3144 				snapTimer.start(QMC2_SWSNAP_DELAY);
3145 			}
3146 		}
3147 	}
3148 }
3149 
on_treeWidgetKnownSoftwareTree_itemEntered(QTreeWidgetItem * item,int)3150 void SoftwareList::on_treeWidgetKnownSoftwareTree_itemEntered(QTreeWidgetItem *item, int)
3151 {
3152 	if ( item == enteredItem )
3153 		return;
3154 	enteredItem = item;
3155 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapOnMouseHover", false).toBool() )
3156 		return;
3157 	if ( !snapForced ) {
3158 		if ( qmc2SoftwareSnap ) {
3159 			if ( qmc2SoftwareSnap->myItem != item ) {
3160 				qmc2SoftwareSnap->myItem = (SoftwareItem *)item;
3161 				snapTimer.start(QMC2_SWSNAP_DELAY);
3162 			}
3163 		}
3164 	}
3165 }
3166 
on_treeWidgetFavoriteSoftware_itemEntered(QTreeWidgetItem * item,int)3167 void SoftwareList::on_treeWidgetFavoriteSoftware_itemEntered(QTreeWidgetItem *item, int)
3168 {
3169 	if ( item == enteredItem )
3170 		return;
3171 	enteredItem = item;
3172 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapOnMouseHover", false).toBool() )
3173 		return;
3174 	if ( !snapForced ) {
3175 		if ( qmc2SoftwareSnap ) {
3176 			if ( qmc2SoftwareSnap->myItem != item ) {
3177 				qmc2SoftwareSnap->myItem = (SoftwareItem *)item;
3178 				snapTimer.start(QMC2_SWSNAP_DELAY);
3179 			}
3180 		}
3181 	}
3182 }
3183 
on_treeWidgetSearchResults_itemEntered(QTreeWidgetItem * item,int)3184 void SoftwareList::on_treeWidgetSearchResults_itemEntered(QTreeWidgetItem *item, int)
3185 {
3186 	if ( item == enteredItem )
3187 		return;
3188 	enteredItem = item;
3189 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapOnMouseHover", false).toBool() )
3190 		return;
3191 	if ( !snapForced ) {
3192 		if ( qmc2SoftwareSnap ) {
3193 			if ( qmc2SoftwareSnap->myItem != item ) {
3194 				qmc2SoftwareSnap->myItem = (SoftwareItem *)item;
3195 				snapTimer.start(QMC2_SWSNAP_DELAY);
3196 			}
3197 		}
3198 	}
3199 }
3200 
on_treeWidgetKnownSoftware_itemActivated(QTreeWidgetItem * item,int)3201 void SoftwareList::on_treeWidgetKnownSoftware_itemActivated(QTreeWidgetItem *item, int)
3202 {
3203 	if ( !qmc2IgnoreItemActivation ) {
3204 		cancelSoftwareSnap();
3205 		switch ( qmc2DefaultLaunchMode ) {
3206 #if (defined(QMC2_OS_UNIX) && QT_VERSION < 0x050000) || defined(QMC2_OS_WIN)
3207 			case QMC2_LAUNCH_MODE_EMBEDDED:
3208 				QTimer::singleShot(0, this, SLOT(playEmbeddedActivated()));
3209 				break;
3210 #endif
3211 			case QMC2_LAUNCH_MODE_INDEPENDENT:
3212 			default:
3213 				QTimer::singleShot(0, this, SLOT(playActivated()));
3214 				break;
3215 		}
3216 	}
3217 	qmc2IgnoreItemActivation = false;
3218 }
3219 
on_treeWidgetKnownSoftware_itemDoubleClicked(QTreeWidgetItem * item,int)3220 void SoftwareList::on_treeWidgetKnownSoftware_itemDoubleClicked(QTreeWidgetItem *item, int)
3221 {
3222 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/DoubleClickActivation").toBool() ) {
3223 		qmc2IgnoreItemActivation = true;
3224 		item->setExpanded(!item->isExpanded());
3225 	}
3226 }
3227 
on_treeWidgetKnownSoftwareTree_itemActivated(QTreeWidgetItem *,int)3228 void SoftwareList::on_treeWidgetKnownSoftwareTree_itemActivated(QTreeWidgetItem *, int)
3229 {
3230 	if ( !qmc2IgnoreItemActivation ) {
3231 		cancelSoftwareSnap();
3232 		switch ( qmc2DefaultLaunchMode ) {
3233 #if (defined(QMC2_OS_UNIX) && QT_VERSION < 0x050000) || defined(QMC2_OS_WIN)
3234 			case QMC2_LAUNCH_MODE_EMBEDDED:
3235 				QTimer::singleShot(0, this, SLOT(playEmbeddedActivated()));
3236 				break;
3237 #endif
3238 			case QMC2_LAUNCH_MODE_INDEPENDENT:
3239 			default:
3240 				QTimer::singleShot(0, this, SLOT(playActivated()));
3241 				break;
3242 		}
3243 	}
3244 	qmc2IgnoreItemActivation = false;
3245 }
3246 
on_treeWidgetKnownSoftwareTree_itemDoubleClicked(QTreeWidgetItem * item,int)3247 void SoftwareList::on_treeWidgetKnownSoftwareTree_itemDoubleClicked(QTreeWidgetItem *item, int)
3248 {
3249 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/DoubleClickActivation").toBool() ) {
3250 		qmc2IgnoreItemActivation = true;
3251 		item->setExpanded(!item->isExpanded());
3252 	}
3253 }
3254 
on_treeWidgetFavoriteSoftware_itemActivated(QTreeWidgetItem * item,int)3255 void SoftwareList::on_treeWidgetFavoriteSoftware_itemActivated(QTreeWidgetItem *item, int)
3256 {
3257 	if ( !qmc2IgnoreItemActivation ) {
3258 		cancelSoftwareSnap();
3259 		switch ( qmc2DefaultLaunchMode ) {
3260 #if (defined(QMC2_OS_UNIX) && QT_VERSION < 0x050000) || defined(QMC2_OS_WIN)
3261 			case QMC2_LAUNCH_MODE_EMBEDDED:
3262 				QTimer::singleShot(0, this, SLOT(playEmbeddedActivated()));
3263 				break;
3264 #endif
3265 			case QMC2_LAUNCH_MODE_INDEPENDENT:
3266 			default:
3267 				QTimer::singleShot(0, this, SLOT(playActivated()));
3268 				break;
3269 		}
3270 	}
3271 	qmc2IgnoreItemActivation = false;
3272 }
3273 
on_treeWidgetFavoriteSoftware_itemDoubleClicked(QTreeWidgetItem * item,int)3274 void SoftwareList::on_treeWidgetFavoriteSoftware_itemDoubleClicked(QTreeWidgetItem *item, int)
3275 {
3276 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/DoubleClickActivation").toBool() ) {
3277 		qmc2IgnoreItemActivation = true;
3278 		item->setExpanded(!item->isExpanded());
3279 	}
3280 }
3281 
on_treeWidgetSearchResults_itemActivated(QTreeWidgetItem * item,int)3282 void SoftwareList::on_treeWidgetSearchResults_itemActivated(QTreeWidgetItem *item, int)
3283 {
3284 	if ( !qmc2IgnoreItemActivation ) {
3285 		cancelSoftwareSnap();
3286 		switch ( qmc2DefaultLaunchMode ) {
3287 #if (defined(QMC2_OS_UNIX) && QT_VERSION < 0x050000) || defined(QMC2_OS_WIN)
3288 			case QMC2_LAUNCH_MODE_EMBEDDED:
3289 				QTimer::singleShot(0, this, SLOT(playEmbeddedActivated()));
3290 				break;
3291 #endif
3292 			case QMC2_LAUNCH_MODE_INDEPENDENT:
3293 			default:
3294 				QTimer::singleShot(0, this, SLOT(playActivated()));
3295 				break;
3296 		}
3297 	}
3298 	qmc2IgnoreItemActivation = false;
3299 }
3300 
on_treeWidgetSearchResults_itemDoubleClicked(QTreeWidgetItem * item,int)3301 void SoftwareList::on_treeWidgetSearchResults_itemDoubleClicked(QTreeWidgetItem *item, int)
3302 {
3303 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/DoubleClickActivation").toBool() ) {
3304 		qmc2IgnoreItemActivation = true;
3305 		item->setExpanded(!item->isExpanded());
3306 	}
3307 }
3308 
on_comboBoxSearch_editTextChanged(const QString &)3309 void SoftwareList::on_comboBoxSearch_editTextChanged(const QString &)
3310 {
3311 	if ( searchActive )
3312 		stopSearch = true;
3313 	searchTimer.start(QMC2_SEARCH_DELAY);
3314 }
3315 
comboBoxSearch_editTextChanged_delayed()3316 void SoftwareList::comboBoxSearch_editTextChanged_delayed()
3317 {
3318 	static QString lastSearchText;
3319 	static bool lastNegatedMatch = false;
3320 
3321 	searchTimer.stop();
3322 	if ( isLoading || qmc2CleaningUp )
3323 		return;
3324 	if ( searchActive ) {
3325 		stopSearch = true;
3326 		searchTimer.start(QMC2_SEARCH_DELAY/10);
3327 		return;
3328 	} else
3329 		stopSearch = false;
3330 	QString pattern(comboBoxSearch->currentText());
3331 	if ( pattern.isEmpty() ) {
3332 		treeWidgetSearchResults->clear();
3333 		lastSearchText.clear();
3334 		lastNegatedMatch = negatedMatch;
3335 		numSoftwareMatches = 0;
3336 		updateStats();
3337 		return;
3338 	} else if ( treeWidgetSearchResults->topLevelItemCount() == 0 )
3339 		lastSearchText.clear();
3340 	if ( pattern == lastSearchText && lastNegatedMatch == negatedMatch )
3341 		return;
3342 	QString patternCopy(pattern);
3343 	// easy pattern match
3344 	int pos = 0;
3345 	QRegExp rxAsterisk("(\\*)");
3346 	while ( (pos = rxAsterisk.indexIn(pattern, pos)) != -1 ) {
3347 		int matchedLength = rxAsterisk.matchedLength();
3348 		if ( pos > 0 ) {
3349 			if ( pattern[pos - 1] != '\\' ) {
3350 				pattern.replace(pos, 1, ".*");
3351 				matchedLength = 2;
3352 			}
3353 		} else {
3354 			pattern.replace(pos, 1, ".*");
3355 			matchedLength = 2;
3356 		}
3357 		pos += matchedLength;
3358 	}
3359 	pos = 0;
3360 	QRegExp rxQuestionMark("(\\?)");
3361 	while ( (pos = rxQuestionMark.indexIn(pattern, pos)) != -1 ) {
3362 		if ( pos > 0 ) {
3363 			if ( pattern[pos - 1] != '\\' )
3364 				pattern.replace(pos, 1, ".");
3365 		} else
3366 			pattern.replace(pos, 1, ".");
3367 		pos += rxQuestionMark.matchedLength();
3368 	}
3369 	pattern.replace(' ', ".* .*").replace(".*^", "").replace("$.*", "");
3370 	treeWidgetSearchResults->clear();
3371 	QRegExp patternRx(QRegExp(pattern, Qt::CaseInsensitive, QRegExp::RegExp2));
3372 	if ( !patternRx.isValid() ) {
3373 		lastSearchText.clear();
3374 		lastNegatedMatch = negatedMatch;
3375 		return;
3376 	}
3377 	searchActive = true;
3378 	lastSearchText = patternCopy;
3379 	progressBarSearch->setVisible(true);
3380 	progressBarSearch->setRange(0, treeWidgetKnownSoftware->topLevelItemCount());
3381 	progressBarSearch->setValue(0);
3382         QList<SoftwareItem *> itemList;
3383 	QList<SoftwareItem *> hideList;
3384 	QStringList compatFilters(systemSoftwareFilterHash.value(systemName));
3385 	QStringList hiddenLists(userDataDb->hiddenLists(systemName));
3386 	numSoftwareMatches = 0;
3387 	for (int i = 0; i < treeWidgetKnownSoftware->topLevelItemCount() && !stopSearch && !qmc2CleaningUp; i++) {
3388 		QTreeWidgetItem *item = treeWidgetKnownSoftware->topLevelItem(i);
3389 		QString itemText(item->text(QMC2_SWLIST_COLUMN_TITLE));
3390 		QString itemName(item->text(QMC2_SWLIST_COLUMN_NAME));
3391 		bool matched = itemText.indexOf(patternRx) > -1 || itemName.indexOf(patternRx) > -1;
3392 		if ( negatedMatch )
3393 			matched = !matched;
3394 		if ( matched ) {
3395 			SoftwareItem *newItem = new SoftwareItem((QTreeWidget *)0);
3396 			SoftwareItem *subItem = new SoftwareItem(newItem);
3397 			subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
3398 			newItem->setText(QMC2_SWLIST_COLUMN_TITLE, item->text(QMC2_SWLIST_COLUMN_TITLE));
3399 			newItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, item->icon(QMC2_SWLIST_COLUMN_TITLE));
3400 			newItem->setWhatsThis(QMC2_SWLIST_COLUMN_TITLE, item->whatsThis(QMC2_SWLIST_COLUMN_TITLE));
3401 			newItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, item->whatsThis(QMC2_SWLIST_COLUMN_NAME));
3402 			newItem->setWhatsThis(QMC2_SWLIST_COLUMN_PART, item->whatsThis(QMC2_SWLIST_COLUMN_PART));
3403 			newItem->setText(QMC2_SWLIST_COLUMN_NAME, item->text(QMC2_SWLIST_COLUMN_NAME));
3404 			newItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, item->text(QMC2_SWLIST_COLUMN_PUBLISHER));
3405 			newItem->setText(QMC2_SWLIST_COLUMN_YEAR, item->text(QMC2_SWLIST_COLUMN_YEAR));
3406 			newItem->setText(QMC2_SWLIST_COLUMN_PART, item->text(QMC2_SWLIST_COLUMN_PART));
3407 			newItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, item->text(QMC2_SWLIST_COLUMN_INTERFACE));
3408 			newItem->setText(QMC2_SWLIST_COLUMN_LIST, item->text(QMC2_SWLIST_COLUMN_LIST));
3409 			newItem->setText(QMC2_SWLIST_COLUMN_SUPPORTED, item->text(QMC2_SWLIST_COLUMN_SUPPORTED));
3410 			itemList << newItem;
3411 			bool softwareListHidden = hiddenLists.contains(item->text(QMC2_SWLIST_COLUMN_LIST));
3412 			bool showItem = true;
3413 			if ( softwareListHidden )
3414 				showItem = false;
3415 			else if ( qmc2SoftwareList->toolButtonCompatFilterToggle->isChecked() ) {
3416 				QStringList compatList(newItem->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(",", QString::SkipEmptyParts));
3417 				showItem = compatList.isEmpty() || compatFilters.isEmpty();
3418 				for (int j = 0; j < compatList.count() && !showItem; j++)
3419 					for (int k = 0; k < compatFilters.count() && !showItem; k++)
3420 						showItem = (compatList.at(j) == compatFilters.at(k));
3421 			}
3422 			if ( !showItem )
3423 				hideList << newItem;
3424 			else
3425 				numSoftwareMatches++;
3426 		}
3427 		progressBarSearch->setValue(progressBarSearch->value() + 1);
3428 		if ( i % QMC2_SEARCH_RESULT_UPDATE == 0 ) {
3429 			treeWidgetSearchResults->setUpdatesEnabled(false);
3430 			foreach (SoftwareItem *item, itemList)
3431 				treeWidgetSearchResults->addTopLevelItem(item);
3432 			foreach (SoftwareItem *item, hideList)
3433 				item->setHidden(true);
3434 			itemList.clear();
3435 			hideList.clear();
3436 			treeWidgetSearchResults->setUpdatesEnabled(true);
3437 			updateStats();
3438 			qApp->processEvents();
3439 		}
3440 	}
3441 	if ( !stopSearch && !qmc2CleaningUp ) {
3442 		treeWidgetSearchResults->setUpdatesEnabled(false);
3443 		foreach (SoftwareItem *item, itemList)
3444 			treeWidgetSearchResults->addTopLevelItem(item);
3445 		foreach (SoftwareItem *item, hideList)
3446 			item->setHidden(true);
3447 		itemList.clear();
3448 		hideList.clear();
3449 		treeWidgetSearchResults->setUpdatesEnabled(true);
3450 		qApp->processEvents();
3451 	} else
3452 		lastSearchText.clear();
3453 	updateStats();
3454 	lastNegatedMatch = negatedMatch;
3455 	progressBarSearch->setVisible(false);
3456 	progressBarSearch->reset();
3457 	if ( autoSelectSearchItem ) {
3458   		treeWidgetSearchResults->setFocus();
3459 		if ( treeWidgetSearchResults->currentItem() )
3460 			treeWidgetSearchResults->currentItem()->setSelected(true);
3461 	}
3462 	autoSelectSearchItem = false;
3463 	searchActive = false;
3464 }
3465 
on_comboBoxSearch_activated(const QString & pattern)3466 void SoftwareList::on_comboBoxSearch_activated(const QString &pattern)
3467 {
3468 	autoSelectSearchItem = true;
3469 	comboBoxSearch_editTextChanged_delayed();
3470 }
3471 
arguments(QStringList * softwareLists,QStringList * softwareNames)3472 QStringList &SoftwareList::arguments(QStringList *softwareLists, QStringList *softwareNames)
3473 {
3474 	static QStringList swlArgs;
3475 
3476 	swlArgs.clear();
3477 	// arguments to start a software list entry
3478 	QTreeWidget *treeWidget;
3479 	switch ( toolBoxSoftwareList->currentIndex() ) {
3480 		case QMC2_SWLIST_FAVORITES_PAGE:
3481 			treeWidget = treeWidgetFavoriteSoftware;
3482 			break;
3483 		case QMC2_SWLIST_SEARCH_PAGE:
3484 			treeWidget = treeWidgetSearchResults;
3485 			break;
3486 		case QMC2_SWLIST_KNOWN_SW_PAGE:
3487 		default:
3488 			if ( viewTree() )
3489 				treeWidget = treeWidgetKnownSoftwareTree;
3490 			else
3491 				treeWidget = treeWidgetKnownSoftware;
3492 			break;
3493 	}
3494 	// optionally add arguments for the selected device configuration
3495 	QString devConfigName(comboBoxDeviceConfiguration->currentText());
3496 	if ( devConfigName != tr("Default configuration") ) {
3497 		qmc2Config->beginGroup(QMC2_EMULATOR_PREFIX + QString("Configuration/Devices/%1/%2").arg(systemName).arg(devConfigName));
3498 		QStringList instances(qmc2Config->value("Instances").toStringList());
3499 		QStringList files(qmc2Config->value("Files").toStringList());
3500 		QStringList slotNames(qmc2Config->value("Slots").toStringList());
3501 		QStringList slotOptions(qmc2Config->value("SlotOptions").toStringList());
3502 		QStringList slotBIOSs(qmc2Config->value("SlotBIOSs").toStringList());
3503 		qmc2Config->endGroup();
3504 		for (int i = 0; i < slotNames.count(); i++) {
3505 			if ( !slotOptions.at(i).isEmpty() ) {
3506 				QString slotOpt(slotOptions.at(i));
3507 				if ( !slotBIOSs.at(i).isEmpty() )
3508 					slotOpt += ",bios=" + slotBIOSs.at(i);
3509 				swlArgs << QString("-%1").arg(slotNames.at(i)) << slotOpt;
3510 			}
3511 		}
3512 		for (int i = 0; i < instances.count(); i++) {
3513 #if defined(QMC2_OS_WIN)
3514 			swlArgs << QString("-%1").arg(instances.at(i)) << files[i].replace('/', '\\');
3515 #else
3516 			swlArgs << QString("-%1").arg(instances.at(i)) << files[i].replace("~", "$HOME");
3517 #endif
3518 		}
3519 	}
3520 	QList<QTreeWidgetItem *> selectedItems = treeWidget->selectedItems();
3521 	QString snapnameList, snapnameSoftware;
3522 	if ( selectedItems.count() > 0 ) {
3523 		QTreeWidgetItemIterator it(treeWidget);
3524 		QStringList manualMounts;
3525 		if ( !autoMounted ) {
3526 			// manually mounted
3527 			while ( *it ) {
3528 				QComboBox *comboBox = (QComboBox *)treeWidget->itemWidget(*it, QMC2_SWLIST_COLUMN_PUBLISHER);
3529 				if ( comboBox ) {
3530 					if ( comboBox->currentIndex() > QMC2_SWLIST_MSEL_DONT_MOUNT ) {
3531 						if ( snapnameList.isEmpty() ) {
3532 							QTreeWidgetItem *item = *it;
3533 							while ( item->parent() )
3534 								item = item->parent();
3535 							snapnameList = item->text(QMC2_SWLIST_COLUMN_LIST);
3536 							snapnameSoftware = item->text(QMC2_SWLIST_COLUMN_NAME);
3537 							if ( comboBoxSnapnameDevice->isVisible() ) {
3538 								if ( snapnameList + ":" + snapnameSoftware != comboBoxSnapnameDevice->currentText() ) {
3539 									snapnameList.clear();
3540 									snapnameSoftware.clear();
3541 								}
3542 							}
3543 						}
3544 						swlArgs << QString("-%1").arg(comboBox->currentText());
3545 						QTreeWidgetItem *partItem = *it;
3546 						QTreeWidgetItem *item = partItem;
3547 						if ( viewTree() ) {
3548 							while ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && item->parent() )
3549 								item = item->parent();
3550 						} else {
3551 							while ( item->parent() )
3552 								item = item->parent();
3553 						}
3554 						swlArgs << QString("%1:%2:%3").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)).arg(partItem->text(QMC2_SWLIST_COLUMN_PART));
3555 						if ( softwareLists )
3556 							*softwareLists << item->text(QMC2_SWLIST_COLUMN_LIST);
3557 						if ( softwareNames )
3558 							*softwareNames << item->text(QMC2_SWLIST_COLUMN_NAME);
3559 					}
3560 				}
3561 				it++;
3562 			}
3563 		} else {
3564 			// automatically mounted
3565 			QTreeWidgetItem *item = selectedItems.first();
3566 			if ( viewTree() ) {
3567 				while ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && item->parent() )
3568 					item = item->parent();
3569 			} else {
3570 				while ( item->parent() )
3571 					item = item->parent();
3572 			}
3573 			snapnameList = item->text(QMC2_SWLIST_COLUMN_LIST);
3574 			snapnameSoftware = item->text(QMC2_SWLIST_COLUMN_NAME);
3575 			QStringList interfaces(item->text(QMC2_SWLIST_COLUMN_INTERFACE).split(',', QString::SkipEmptyParts));
3576 			QStringList parts(item->text(QMC2_SWLIST_COLUMN_PART).split(',', QString::SkipEmptyParts));
3577 			successfulLookups.clear();
3578 			for (int i = 0; i < parts.count(); i++) {
3579 				QString mountDev(lookupMountDevice(parts.at(i), interfaces.at(i)));
3580 				if ( !mountDev.isEmpty() ) {
3581 					swlArgs << QString("-%1").arg(mountDev);
3582 					swlArgs << QString("%1:%2:%3").arg(item->text(QMC2_SWLIST_COLUMN_LIST)).arg(item->text(QMC2_SWLIST_COLUMN_NAME)).arg(parts.at(i));
3583 				}
3584 			}
3585 			if ( softwareLists )
3586 				*softwareLists << item->text(QMC2_SWLIST_COLUMN_LIST);
3587 			if ( softwareNames )
3588 				*softwareNames << item->text(QMC2_SWLIST_COLUMN_NAME);
3589 		}
3590 	}
3591 	if ( toolButtonToggleSnapnameAdjustment->isChecked() && !snapnameList.isEmpty() ) {
3592 		QString snapnamePattern(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SnapnamePattern", "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$").toString());
3593 		snapnamePattern.replace("$SOFTWARE_LIST$", snapnameList).replace("$SOFTWARE_NAME$", snapnameSoftware);
3594 		swlArgs.prepend(snapnamePattern);
3595 		swlArgs.prepend("-snapname");
3596 	}
3597 	if ( toolButtonToggleStatenameAdjustment->isChecked() && !snapnameList.isEmpty() ) {
3598 		QString statenamePattern(qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/StatenamePattern", "soft-lists/$SOFTWARE_LIST$/$SOFTWARE_NAME$").toString());
3599 		statenamePattern.replace("$SOFTWARE_LIST$", snapnameList).replace("$SOFTWARE_NAME$", snapnameSoftware);
3600 		swlArgs.prepend(statenamePattern);
3601 		swlArgs.prepend("-statename");
3602 	}
3603 	return swlArgs;
3604 }
3605 
treeWidgetKnownSoftwareHeader_customContextMenuRequested(const QPoint & p)3606 void SoftwareList::treeWidgetKnownSoftwareHeader_customContextMenuRequested(const QPoint &p)
3607 {
3608 	menuKnownSoftwareHeader->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetKnownSoftware->header()->viewport()->mapToGlobal(p), menuKnownSoftwareHeader));
3609 	menuKnownSoftwareHeader->show();
3610 }
3611 
treeWidgetKnownSoftwareTreeHeader_customContextMenuRequested(const QPoint & p)3612 void SoftwareList::treeWidgetKnownSoftwareTreeHeader_customContextMenuRequested(const QPoint &p)
3613 {
3614 	menuKnownSoftwareHeader->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetKnownSoftwareTree->header()->viewport()->mapToGlobal(p), menuKnownSoftwareHeader));
3615 	menuKnownSoftwareHeader->show();
3616 }
3617 
actionKnownSoftwareHeader_triggered()3618 void SoftwareList::actionKnownSoftwareHeader_triggered()
3619 {
3620 	QAction *action = (QAction *)sender();
3621 	int visibleColumns = 0;
3622 	for (int i = 0; i < treeWidgetKnownSoftware->columnCount(); i++)
3623 		if ( !treeWidgetKnownSoftware->isColumnHidden(i) )
3624 			visibleColumns++;
3625 	if ( action ) {
3626 		if ( action->data().toInt() == QMC2_SWLIST_RESET ) {
3627 			for (int i = 0; i < treeWidgetKnownSoftware->columnCount(); i++) {
3628 				treeWidgetKnownSoftware->setColumnHidden(i, false);
3629 				treeWidgetKnownSoftwareTree->setColumnHidden(i, false);
3630 			}
3631 			treeWidgetKnownSoftware->header()->resizeSections(QHeaderView::Stretch);
3632 			treeWidgetKnownSoftwareTree->header()->resizeSections(QHeaderView::Stretch);
3633 			foreach (QAction *a, menuKnownSoftwareHeader->actions())
3634 				if ( a->isCheckable() ) {
3635 					a->blockSignals(true);
3636 					a->setChecked(true);
3637 					a->blockSignals(false);
3638 				}
3639 			return;
3640 		}
3641 		bool visibility = true;
3642 		if ( action->isChecked() ) {
3643 			treeWidgetKnownSoftware->setColumnHidden(action->data().toInt(), false);
3644 			treeWidgetKnownSoftwareTree->setColumnHidden(action->data().toInt(), false);
3645 		}
3646 		else if ( visibleColumns > 1 ) {
3647 			treeWidgetKnownSoftware->setColumnHidden(action->data().toInt(), true);
3648 			treeWidgetKnownSoftwareTree->setColumnHidden(action->data().toInt(), true);
3649 			visibility = false;
3650 		}
3651 		action->blockSignals(true);
3652 		action->setChecked(visibility);
3653 		action->blockSignals(false);
3654 	}
3655 }
3656 
treeWidgetFavoriteSoftwareHeader_customContextMenuRequested(const QPoint & p)3657 void SoftwareList::treeWidgetFavoriteSoftwareHeader_customContextMenuRequested(const QPoint &p)
3658 {
3659 	menuFavoriteSoftwareHeader->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetFavoriteSoftware->header()->viewport()->mapToGlobal(p), menuFavoriteSoftwareHeader));
3660 	menuFavoriteSoftwareHeader->show();
3661 }
3662 
actionFavoriteSoftwareHeader_triggered()3663 void SoftwareList::actionFavoriteSoftwareHeader_triggered()
3664 {
3665 	QAction *action = (QAction *)sender();
3666 	int visibleColumns = 0;
3667 	for (int i = 0; i < treeWidgetFavoriteSoftware->columnCount(); i++) if ( !treeWidgetFavoriteSoftware->isColumnHidden(i) ) visibleColumns++;
3668 	if ( action ) {
3669 		if ( action->data().toInt() == QMC2_SWLIST_RESET ) {
3670 			for (int i = 0; i < treeWidgetFavoriteSoftware->columnCount(); i++) treeWidgetFavoriteSoftware->setColumnHidden(i, false);
3671 			treeWidgetFavoriteSoftware->header()->resizeSections(QHeaderView::Stretch);
3672 			foreach (QAction *a, menuFavoriteSoftwareHeader->actions())
3673 				if ( a->isCheckable() ) {
3674 					a->blockSignals(true);
3675 					a->setChecked(true);
3676 					a->blockSignals(false);
3677 				}
3678 			return;
3679 		}
3680 		bool visibility = true;
3681 		if ( action->isChecked() )
3682 			treeWidgetFavoriteSoftware->setColumnHidden(action->data().toInt(), false);
3683 		else if ( visibleColumns > 1 ) {
3684 			treeWidgetFavoriteSoftware->setColumnHidden(action->data().toInt(), true);
3685 			visibility = false;
3686 		}
3687 		action->blockSignals(true);
3688 		action->setChecked(visibility);
3689 		action->blockSignals(false);
3690 	}
3691 }
3692 
treeWidgetSearchResultsHeader_customContextMenuRequested(const QPoint & p)3693 void SoftwareList::treeWidgetSearchResultsHeader_customContextMenuRequested(const QPoint &p)
3694 {
3695 	menuSearchResultsHeader->move(qmc2MainWindow->adjustedWidgetPosition(treeWidgetSearchResults->header()->viewport()->mapToGlobal(p), menuSearchResultsHeader));
3696 	menuSearchResultsHeader->show();
3697 }
3698 
actionSearchResultsHeader_triggered()3699 void SoftwareList::actionSearchResultsHeader_triggered()
3700 {
3701 	QAction *action = (QAction *)sender();
3702 	int visibleColumns = 0;
3703 	for (int i = 0; i < treeWidgetSearchResults->columnCount(); i++) if ( !treeWidgetSearchResults->isColumnHidden(i) ) visibleColumns++;
3704 	if ( action ) {
3705 		if ( action->data().toInt() == QMC2_SWLIST_RESET ) {
3706 			for (int i = 0; i < treeWidgetSearchResults->columnCount(); i++) treeWidgetSearchResults->setColumnHidden(i, false);
3707 			treeWidgetSearchResults->header()->resizeSections(QHeaderView::Stretch);
3708 			foreach (QAction *a, menuSearchResultsHeader->actions())
3709 				if ( a->isCheckable() ) {
3710 					a->blockSignals(true);
3711 					a->setChecked(true);
3712 					a->blockSignals(false);
3713 				}
3714 			return;
3715 		}
3716 		bool visibility = true;
3717 		if ( action->isChecked() )
3718 			treeWidgetSearchResults->setColumnHidden(action->data().toInt(), false);
3719 		else if ( visibleColumns > 1 ) {
3720 			treeWidgetSearchResults->setColumnHidden(action->data().toInt(), true);
3721 			visibility = false;
3722 		}
3723 		action->blockSignals(true);
3724 		action->setChecked(visibility);
3725 		action->blockSignals(false);
3726 	}
3727 }
3728 
checkMountDeviceSelection()3729 void SoftwareList::checkMountDeviceSelection()
3730 {
3731 	QComboBox *comboBoxSender = (QComboBox *)sender();
3732 	QTreeWidget *treeWidget;
3733 	switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
3734 		case QMC2_SWLIST_KNOWN_SW_PAGE:
3735 			if ( viewTree() )
3736 				treeWidget = treeWidgetKnownSoftwareTree;
3737 			else
3738 				treeWidget = treeWidgetKnownSoftware;
3739 			break;
3740 		case QMC2_SWLIST_FAVORITES_PAGE:
3741 			treeWidget = treeWidgetFavoriteSoftware;
3742 			break;
3743 		case QMC2_SWLIST_SEARCH_PAGE:
3744 			treeWidget = treeWidgetSearchResults;
3745 			break;
3746 	}
3747 	QString mountDevice(comboBoxSender->currentText());
3748 	QTreeWidgetItemIterator it(treeWidget);
3749 	if ( mountDevice == QObject::tr("Auto mount") ) {
3750 		while ( *it ) {
3751 			if ( viewTree() ) {
3752 				if ( !(*it)->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() )
3753 					successfulLookups.clear();
3754 			} else {
3755 				if ( !(*it)->parent() )
3756 					successfulLookups.clear();
3757 			}
3758 			QComboBox *comboBox = (QComboBox *)treeWidget->itemWidget(*it, QMC2_SWLIST_COLUMN_PUBLISHER);
3759 			if ( comboBox ) {
3760 				comboBox->blockSignals(true);
3761 				comboBox->setCurrentIndex(QMC2_SWLIST_MSEL_AUTO_MOUNT); // => auto mount
3762 				comboBox->blockSignals(false);
3763 				QString itemMountDev(lookupMountDevice((*it)->text(QMC2_SWLIST_COLUMN_PART), (*it)->text(QMC2_SWLIST_COLUMN_INTERFACE)));
3764 				if ( itemMountDev.isEmpty() )
3765 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
3766 				else
3767 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Mounted on:") + " " + itemMountDev);
3768 			}
3769 			it++;
3770 		}
3771 		autoMounted = true;
3772 		mountedSoftware.clear();
3773 	} else if ( mountDevice == QObject::tr("Don't mount") ) {
3774 		while ( *it ) {
3775 			QComboBox *comboBox = (QComboBox *)treeWidget->itemWidget(*it, QMC2_SWLIST_COLUMN_PUBLISHER);
3776 			if ( comboBox ) {
3777 				QTreeWidgetItem *pItem = *it;
3778 				if ( viewTree() ) {
3779 					while ( pItem->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && pItem->parent() )
3780 						pItem = pItem->parent();
3781 				} else {
3782 					while ( pItem->parent() )
3783 						pItem = pItem->parent();
3784 				}
3785 				if ( comboBox == comboBoxSender ) {
3786 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
3787 					mountedSoftware.removeAll(pItem->text(QMC2_SWLIST_COLUMN_LIST) + ":" + pItem->text(QMC2_SWLIST_COLUMN_NAME));
3788 				} else if ( comboBox->currentIndex() == QMC2_SWLIST_MSEL_AUTO_MOUNT ) {
3789 					comboBox->blockSignals(true);
3790 					comboBox->setCurrentIndex(QMC2_SWLIST_MSEL_DONT_MOUNT); // => don't mount
3791 					comboBox->blockSignals(false);
3792 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
3793 					mountedSoftware.removeAll(pItem->text(QMC2_SWLIST_COLUMN_LIST) + ":" + pItem->text(QMC2_SWLIST_COLUMN_NAME));
3794 				}
3795 			}
3796 			it++;
3797 		}
3798 		autoMounted = false;
3799 	} else {
3800 		while ( *it ) {
3801 			QComboBox *comboBox = (QComboBox *)treeWidget->itemWidget(*it, QMC2_SWLIST_COLUMN_PUBLISHER);
3802 			if ( comboBox ) {
3803 				QTreeWidgetItem *pItem = *it;
3804 				if ( viewTree() ) {
3805 					while ( pItem->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && pItem->parent() )
3806 						pItem = pItem->parent();
3807 				} else {
3808 					while ( pItem->parent() )
3809 						pItem = pItem->parent();
3810 				}
3811 				if ( comboBox != comboBoxSender ) {
3812 					if ( comboBox->currentText() == mountDevice || comboBox->currentText() == QObject::tr("Auto mount") ) {
3813 						comboBox->blockSignals(true);
3814 						comboBox->setCurrentIndex(QMC2_SWLIST_MSEL_DONT_MOUNT); // => don't mount
3815 						comboBox->blockSignals(false);
3816 						(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
3817 						mountedSoftware.removeAll(pItem->text(QMC2_SWLIST_COLUMN_LIST) + ":" + pItem->text(QMC2_SWLIST_COLUMN_NAME));
3818 					}
3819 				} else {
3820 					(*it)->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Mounted on:") + " " + mountDevice);
3821 					mountedSoftware << pItem->text(QMC2_SWLIST_COLUMN_LIST) + ":" + pItem->text(QMC2_SWLIST_COLUMN_NAME);
3822 				}
3823 			}
3824 			it++;
3825 		}
3826 		autoMounted = false;
3827 	}
3828 	if ( toolButtonToggleSnapnameAdjustment->isChecked() || toolButtonToggleStatenameAdjustment->isChecked() ) {
3829 		if ( !autoMounted && mountedSoftware.count() > 1 ) {
3830 			comboBoxSnapnameDevice->setUpdatesEnabled(false);
3831 			comboBoxSnapnameDevice->clear();
3832 			std::sort(mountedSoftware.begin(), mountedSoftware.end());
3833 			comboBoxSnapnameDevice->addItems(mountedSoftware);
3834 			comboBoxSnapnameDevice->setUpdatesEnabled(true);
3835 			comboBoxSnapnameDevice->show();
3836 		} else
3837 			comboBoxSnapnameDevice->hide();
3838 	} else
3839 		comboBoxSnapnameDevice->hide();
3840 }
3841 
loadFavoritesFromFile()3842 void SoftwareList::loadFavoritesFromFile()
3843 {
3844 	QString proposedName(systemName + ".fav");
3845 	if ( qmc2Config->contains(QMC2_FRONTEND_PREFIX + "SoftwareList/LastFavoritesStoragePath") )
3846 		proposedName.prepend(qmc2Config->value(QMC2_FRONTEND_PREFIX + "SoftwareList/LastFavoritesStoragePath").toString());
3847 	QString filePath(QFileDialog::getOpenFileName(this, tr("Choose file to merge favorites from"), proposedName, tr("All files (*)"), 0, qmc2Options->useNativeFileDialogs() ? (QFileDialog::Options)0 : QFileDialog::DontUseNativeDialog));
3848 	if ( !filePath.isEmpty() ) {
3849 		QFileInfo fiFilePath(filePath);
3850 		QString storagePath(fiFilePath.absolutePath());
3851 		if ( !storagePath.endsWith("/") )
3852 			storagePath.append("/");
3853 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "SoftwareList/LastFavoritesStoragePath", storagePath);
3854 		// import software-list favorites
3855 		QFile favoritesFile(filePath);
3856 		QStringList compatFilters(systemSoftwareFilterHash.value(systemName));
3857 		QStringList hiddenLists(userDataDb->hiddenLists(systemName));
3858 		if ( favoritesFile.open(QIODevice::ReadOnly | QIODevice::Text) ) {
3859 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading software-favorites for '%1' from '%2'").arg(systemName).arg(filePath));
3860 			QTextStream ts(&favoritesFile);
3861 			int lineCounter = 0;
3862 			while ( !ts.atEnd() ){
3863 				QString line(ts.readLine().trimmed());
3864 				lineCounter++;
3865 				if ( !line.startsWith('#') && !line.isEmpty()) {
3866 					QStringList words(line.split('\t'));
3867 					if ( words.count() > 1 ) {
3868 						QString listName(words.at(0));
3869 						QString entryName(words.at(1));
3870 						QList<QTreeWidgetItem *> matchedItems = treeWidgetFavoriteSoftware->findItems(entryName, Qt::MatchExactly | Qt::MatchCaseSensitive, QMC2_SWLIST_COLUMN_NAME);
3871 						if ( matchedItems.count() <= 0 ) {
3872 							matchedItems = treeWidgetKnownSoftware->findItems(entryName, Qt::MatchExactly | Qt::MatchCaseSensitive, QMC2_SWLIST_COLUMN_NAME);
3873 							if ( matchedItems.count() > 0 ) {
3874 								SoftwareItem *knowSoftwareItem = (SoftwareItem *)matchedItems.at(0);
3875 								SoftwareItem *item = new SoftwareItem(treeWidgetFavoriteSoftware);
3876 								SoftwareItem *subItem = new SoftwareItem(item);
3877 								subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
3878 								item->setText(QMC2_SWLIST_COLUMN_TITLE, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_TITLE));
3879 								item->setWhatsThis(QMC2_SWLIST_COLUMN_TITLE, knowSoftwareItem->whatsThis(QMC2_SWLIST_COLUMN_TITLE));
3880 								item->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, knowSoftwareItem->whatsThis(QMC2_SWLIST_COLUMN_NAME));
3881 								item->setWhatsThis(QMC2_SWLIST_COLUMN_PART, knowSoftwareItem->whatsThis(QMC2_SWLIST_COLUMN_PART));
3882 								bool softwareListHidden = hiddenLists.contains(item->text(QMC2_SWLIST_COLUMN_LIST));
3883 								bool showItem = true;
3884 								if ( softwareListHidden )
3885 									showItem = false;
3886 								else if ( toolButtonCompatFilterToggle->isChecked() ) {
3887 									QStringList compatList = item->whatsThis(QMC2_SWLIST_COLUMN_TITLE).split(",", QString::SkipEmptyParts);
3888 									showItem = compatList.isEmpty() || compatFilters.isEmpty();
3889 									for (int i = 0; i < compatList.count() && !showItem; i++)
3890 										for (int j = 0; j < compatFilters.count() && !showItem; j++)
3891 											showItem = (compatList[i] == compatFilters[j]);
3892 								}
3893 								item->setHidden(!showItem);
3894 								item->setText(QMC2_SWLIST_COLUMN_NAME, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_NAME));
3895 								item->setText(QMC2_SWLIST_COLUMN_PUBLISHER, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_PUBLISHER));
3896 								item->setText(QMC2_SWLIST_COLUMN_YEAR, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_YEAR));
3897 								item->setText(QMC2_SWLIST_COLUMN_PART, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_PART));
3898 								item->setText(QMC2_SWLIST_COLUMN_INTERFACE, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_INTERFACE));
3899 								item->setText(QMC2_SWLIST_COLUMN_LIST, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_LIST));
3900 								item->setText(QMC2_SWLIST_COLUMN_SUPPORTED, knowSoftwareItem->text(QMC2_SWLIST_COLUMN_SUPPORTED));
3901 								if ( words.count() > 2 )
3902 									item->setText(QMC2_SWLIST_COLUMN_DEVICECFG, words[2]);
3903 								qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("entry '%1:%2' successfully imported").arg(listName).arg(entryName));
3904 							} else
3905 								qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: entry '%1:%2' cannot be associated with any known software for this system (line %3 ignored)").arg(listName).arg(entryName).arg(lineCounter));
3906 						} else
3907 							qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: a favorite entry for '%1:%2' already exists (line %3 ignored)").arg(listName).arg(entryName).arg(lineCounter));
3908 					} else
3909 						qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: syntax error on line %1 (ignored)").arg(lineCounter));
3910 				}
3911 			}
3912 			favoritesFile.close();
3913 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading software-favorites for '%1' from '%2')").arg(systemName).arg(filePath));
3914 		} else
3915 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open '%1' for reading, please check permissions").arg(filePath));
3916 	}
3917 }
3918 
saveFavoritesToFile()3919 void SoftwareList::saveFavoritesToFile()
3920 {
3921 	QString proposedName(systemName + ".fav");
3922 	if ( qmc2Config->contains(QMC2_FRONTEND_PREFIX + "SoftwareList/LastFavoritesStoragePath") )
3923 		proposedName.prepend(qmc2Config->value(QMC2_FRONTEND_PREFIX + "SoftwareList/LastFavoritesStoragePath").toString());
3924 	QString filePath(QFileDialog::getSaveFileName(this, tr("Choose file to store favorites to"), proposedName, tr("All files (*)"), 0, qmc2Options->useNativeFileDialogs() ? (QFileDialog::Options)0 : QFileDialog::DontUseNativeDialog));
3925 	if ( !filePath.isEmpty() ) {
3926 		QFileInfo fiFilePath(filePath);
3927 		QString storagePath(fiFilePath.absolutePath());
3928 		if ( !storagePath.endsWith("/") ) storagePath.append("/");
3929 		qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "SoftwareList/LastFavoritesStoragePath", storagePath);
3930 		// export software-list favorites
3931 		QFile favoritesFile(filePath);
3932 		if ( favoritesFile.open(QIODevice::WriteOnly | QIODevice::Text) ) {
3933 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("saving software-favorites for '%1' to '%2'").arg(systemName).arg(filePath));
3934 			QTextStream ts(&favoritesFile);
3935 			ts << QString("# MAME software-list favorites export for driver '%1'\n").arg(systemName);
3936 			ts << QString("# Format: <list-name><TAB><entry-name>\n");
3937 			for (int i = 0; i < treeWidgetFavoriteSoftware->topLevelItemCount(); i++) {
3938 				QTreeWidgetItem *item = treeWidgetFavoriteSoftware->topLevelItem(i);
3939 				if ( item ) {
3940 					ts << item->text(QMC2_SWLIST_COLUMN_LIST) << "\t" << item->text(QMC2_SWLIST_COLUMN_NAME);
3941 					if ( !item->text(QMC2_SWLIST_COLUMN_DEVICECFG).isEmpty() )
3942 						ts << "\t" << item->text(QMC2_SWLIST_COLUMN_DEVICECFG);
3943 					ts << "\n";
3944 				}
3945 
3946 			}
3947 			favoritesFile.close();
3948 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (saving software-favorites for '%1' to '%2')").arg(systemName).arg(filePath));
3949 		} else
3950 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open '%1' for writing, please check permissions").arg(filePath));
3951 	}
3952 }
3953 
softwareStatus(QString listName,QString softwareName,bool translated)3954 QString SoftwareList::softwareStatus(QString listName, QString softwareName, bool translated)
3955 {
3956 	if ( softwareListStateHash.contains(listName) ) {
3957 		if ( softwareListStateHash.value(listName).contains(softwareName) ) {
3958 			switch ( softwareListStateHash.value(listName).value(softwareName) ) {
3959 				case 'C':
3960 					if ( translated )
3961 						return tr("correct");
3962 					else
3963 						return "correct";
3964 					break;
3965 				case 'M':
3966 					if ( translated )
3967 						return tr("mostly correct");
3968 					else
3969 						return "mostly correct";
3970 					break;
3971 				case 'I':
3972 					if ( translated )
3973 						return tr("incorrect");
3974 					else
3975 						return "incorrect";
3976 					break;
3977 				case 'N':
3978 					if ( translated )
3979 						return tr("not found");
3980 					else
3981 						return "not found";
3982 					break;
3983 				case 'U':
3984 				default:
3985 					if ( translated )
3986 						return tr("unknown");
3987 					else
3988 						return "unknown";
3989 					break;
3990 			}
3991 		} else {
3992 			if ( translated )
3993 				return tr("unknown");
3994 			else
3995 				return "unknown";
3996 		}
3997 	} else {
3998 		if ( translated )
3999 			return tr("unknown");
4000 		else
4001 			return "unknown";
4002 	}
4003 }
4004 
status(SoftwareListXmlHandler * handler)4005 QString &SoftwareList::status(SoftwareListXmlHandler *handler)
4006 {
4007 	static QLocale locale;
4008 	m_statusString = "<b>";
4009 	if ( handler ) {
4010 		m_statusString += "<font color=black>" + m_trL + locale.toString(numSoftwareTotal + handler->numTotal) + "</font>&nbsp;";
4011 		if ( toolButtonSoftwareStates->isChecked() ) {
4012 			m_statusString += "<font color=\"#00cc00\">" + m_trC + locale.toString(numSoftwareCorrect + handler->numCorrect) + "</font>&nbsp;"
4013 					  "<font color=\"#799632\">" + m_trM + locale.toString(numSoftwareMostlyCorrect + handler->numMostlyCorrect) + "</font>&nbsp;"
4014 					  "<font color=\"#f90000\">" + m_trI + locale.toString(numSoftwareIncorrect + handler->numIncorrect) + "</font>&nbsp;"
4015 					  "<font color=\"#7f7f7f\">" + m_trN + locale.toString(numSoftwareNotFound + handler->numNotFound) + "</font>&nbsp;"
4016 					  "<font color=\"#0000f9\">" + m_trU + locale.toString(numSoftwareUnknown + handler->numUnknown) + "</font>&nbsp;"
4017 					  "<font color=\"chocolate\">" + m_trS + locale.toString(numSoftwareMatches) + "</font>";
4018 		} else
4019 			m_statusString += "<font color=\"chocolate\">" + m_trS + locale.toString(numSoftwareMatches) + "</font>";
4020 	} else {
4021 		m_statusString += "<font color=black>" + m_trL + locale.toString(numSoftwareTotal) + "</font>&nbsp;";
4022 		if ( toolButtonSoftwareStates->isChecked() ) {
4023 			m_statusString += "<font color=\"#00cc00\">" + m_trC + locale.toString(numSoftwareCorrect) + "</font>&nbsp;"
4024 					  "<font color=\"#799632\">" + m_trM + locale.toString(numSoftwareMostlyCorrect) + "</font>&nbsp;"
4025 					  "<font color=\"#f90000\">" + m_trI + locale.toString(numSoftwareIncorrect) + "</font>&nbsp;"
4026 					  "<font color=\"#7f7f7f\">" + m_trN + locale.toString(numSoftwareNotFound) + "</font>&nbsp;"
4027 					  "<font color=\"#0000f9\">" + m_trU + locale.toString(numSoftwareUnknown) + "</font>&nbsp;"
4028 					  "<font color=\"chocolate\">" + m_trS + locale.toString(numSoftwareMatches) + "</font>";
4029 		} else
4030 			m_statusString += "<font color=\"chocolate\">" + m_trS + locale.toString(numSoftwareMatches) + "</font>";
4031 	}
4032 	m_statusString += "</b>";
4033 	return m_statusString;
4034 }
4035 
updateStats(SoftwareListXmlHandler * handler)4036 void SoftwareList::updateStats(SoftwareListXmlHandler *handler)
4037 {
4038 	labelSoftwareListStats->setText(status(handler));
4039 }
4040 
analyzeSoftware()4041 void SoftwareList::analyzeSoftware()
4042 {
4043 	QTreeWidget *treeWidget;
4044 	switch ( toolBoxSoftwareList->currentIndex() ) {
4045 		case QMC2_SWLIST_KNOWN_SW_PAGE:
4046 			if ( viewTree() )
4047 				treeWidget = treeWidgetKnownSoftwareTree;
4048 			else
4049 				treeWidget = treeWidgetKnownSoftware;
4050 			break;
4051 		case QMC2_SWLIST_FAVORITES_PAGE:
4052 			treeWidget = treeWidgetFavoriteSoftware;
4053 			break;
4054 		case QMC2_SWLIST_SEARCH_PAGE:
4055 			treeWidget = treeWidgetSearchResults;
4056 			break;
4057 	}
4058 	QList<QTreeWidgetItem *> selectedItems = treeWidget->selectedItems();
4059 	if ( !selectedItems.isEmpty() ) {
4060 		QTreeWidgetItem *item = selectedItems[0];
4061 		if ( viewTree() ) {
4062 			while ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && item->parent() )
4063 				item = item->parent();
4064 		} else {
4065 			while ( item->parent() )
4066 				item = item->parent();
4067 		}
4068 		if ( !qmc2SoftwareROMAlyzer )
4069 			qmc2SoftwareROMAlyzer = new ROMAlyzer(0, QMC2_ROMALYZER_MODE_SOFTWARE);
4070 		if ( !qmc2SoftwareROMAlyzer->active() ) {
4071 			qmc2SoftwareROMAlyzer->lineEditSoftwareLists->setText(item->text(QMC2_SWLIST_COLUMN_LIST));
4072 			qmc2SoftwareROMAlyzer->lineEditSets->setText(item->text(QMC2_SWLIST_COLUMN_NAME));
4073 		}
4074 		if ( qmc2SoftwareROMAlyzer->isHidden() )
4075 			qmc2SoftwareROMAlyzer->show();
4076 		else if ( qmc2SoftwareROMAlyzer->isMinimized() )
4077 			qmc2SoftwareROMAlyzer->showNormal();
4078 		if ( qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabAnalyzer && qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabLog )
4079 			qmc2SoftwareROMAlyzer->tabWidgetAnalysis->setCurrentWidget(qmc2SoftwareROMAlyzer->tabAnalyzer);
4080 		QTimer::singleShot(0, qmc2SoftwareROMAlyzer, SLOT(raise()));
4081 		QTimer::singleShot(0, qmc2SoftwareROMAlyzer->pushButtonAnalyze, SLOT(animateClick()));
4082 	}
4083 }
4084 
analyzeSoftwareList()4085 void SoftwareList::analyzeSoftwareList()
4086 {
4087 	QTreeWidget *treeWidget;
4088 	switch ( toolBoxSoftwareList->currentIndex() ) {
4089 		case QMC2_SWLIST_KNOWN_SW_PAGE:
4090 			if ( viewTree() )
4091 				treeWidget = treeWidgetKnownSoftwareTree;
4092 			else
4093 				treeWidget = treeWidgetKnownSoftware;
4094 			break;
4095 		case QMC2_SWLIST_FAVORITES_PAGE:
4096 			treeWidget = treeWidgetFavoriteSoftware;
4097 			break;
4098 		case QMC2_SWLIST_SEARCH_PAGE:
4099 			treeWidget = treeWidgetSearchResults;
4100 			break;
4101 	}
4102 	QList<QTreeWidgetItem *> selectedItems = treeWidget->selectedItems();
4103 	if ( !selectedItems.isEmpty() ) {
4104 		QTreeWidgetItem *item = selectedItems[0];
4105 		if ( viewTree() ) {
4106 			while ( item->whatsThis(QMC2_SWLIST_COLUMN_NAME).isEmpty() && item->parent() )
4107 				item = item->parent();
4108 		} else {
4109 			while ( item->parent() )
4110 				item = item->parent();
4111 		}
4112 		if ( !qmc2SoftwareROMAlyzer )
4113 			qmc2SoftwareROMAlyzer = new ROMAlyzer(0, QMC2_ROMALYZER_MODE_SOFTWARE);
4114 		if ( !qmc2SoftwareROMAlyzer->active() ) {
4115 			qmc2SoftwareROMAlyzer->lineEditSoftwareLists->setText(item->text(QMC2_SWLIST_COLUMN_LIST));
4116 			qmc2SoftwareROMAlyzer->lineEditSets->setText("*");
4117 		}
4118 		if ( qmc2SoftwareROMAlyzer->isHidden() )
4119 			qmc2SoftwareROMAlyzer->show();
4120 		else if ( qmc2SoftwareROMAlyzer->isMinimized() )
4121 			qmc2SoftwareROMAlyzer->showNormal();
4122 		if ( qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabAnalyzer && qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabLog )
4123 			qmc2SoftwareROMAlyzer->tabWidgetAnalysis->setCurrentWidget(qmc2SoftwareROMAlyzer->tabAnalyzer);
4124 		QTimer::singleShot(0, qmc2SoftwareROMAlyzer, SLOT(raise()));
4125 		QTimer::singleShot(0, qmc2SoftwareROMAlyzer->pushButtonAnalyze, SLOT(animateClick()));
4126 	}
4127 }
4128 
analyzeSoftwareLists()4129 void SoftwareList::analyzeSoftwareLists()
4130 {
4131 	if ( !qmc2SoftwareROMAlyzer )
4132 		qmc2SoftwareROMAlyzer = new ROMAlyzer(0, QMC2_ROMALYZER_MODE_SOFTWARE);
4133 	if ( !qmc2SoftwareROMAlyzer->active() ) {
4134 		qmc2SoftwareROMAlyzer->lineEditSoftwareLists->setText(systemSoftwareListHash.value(systemName).join(" "));
4135 		qmc2SoftwareROMAlyzer->lineEditSets->setText("*");
4136 	}
4137 	if ( qmc2SoftwareROMAlyzer->isHidden() )
4138 		qmc2SoftwareROMAlyzer->show();
4139 	else if ( qmc2SoftwareROMAlyzer->isMinimized() )
4140 		qmc2SoftwareROMAlyzer->showNormal();
4141 	if ( qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabAnalyzer && qmc2SoftwareROMAlyzer->tabWidgetAnalysis->currentWidget() != qmc2SoftwareROMAlyzer->tabLog )
4142 		qmc2SoftwareROMAlyzer->tabWidgetAnalysis->setCurrentWidget(qmc2SoftwareROMAlyzer->tabAnalyzer);
4143 	QTimer::singleShot(0, qmc2SoftwareROMAlyzer, SLOT(raise()));
4144 	QTimer::singleShot(0, qmc2SoftwareROMAlyzer->pushButtonAnalyze, SLOT(animateClick()));
4145 }
4146 
SoftwareListXmlHandler(QTreeWidget * parent,QStringList * hiddenLists,bool viewTree)4147 SoftwareListXmlHandler::SoftwareListXmlHandler(QTreeWidget *parent, QStringList *hiddenLists, bool viewTree)
4148 {
4149 	parentTreeWidget = parent;
4150 	numTotal = numCorrect = numMostlyCorrect = numIncorrect = numNotFound = numUnknown = elementCounter = 0;
4151 	newSoftwareStates = softwareListHidden = false;
4152 	setViewTree(viewTree);
4153 	setHiddenLists(hiddenLists);
4154 }
4155 
~SoftwareListXmlHandler()4156 SoftwareListXmlHandler::~SoftwareListXmlHandler()
4157 {
4158 	if ( !itemList().isEmpty() )
4159 		foreach (QTreeWidgetItem *item, itemList())
4160 			delete item;
4161 }
4162 
loadSoftwareStates(QString listName)4163 void SoftwareListXmlHandler::loadSoftwareStates(QString listName)
4164 {
4165 	QString softwareStateCachePath = QDir::toNativeSeparators(QDir::cleanPath(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareStateCache").toString() + "/" + listName + ".ssc"));
4166 	QFile stateCacheFile(softwareStateCachePath);
4167 	if ( stateCacheFile.open(QIODevice::ReadOnly | QIODevice::Text) ) {
4168 		numTotal = numCorrect = numMostlyCorrect = numIncorrect = numNotFound = numUnknown = 0;
4169 		QTextStream ts(&stateCacheFile);
4170 		ts.readLine(); // comment line
4171 		QChar splitChar(' ');
4172 		while ( !ts.atEnd() && !qmc2SoftwareList->interruptLoad ) {
4173 			QStringList words = ts.readLine().trimmed().split(splitChar, QString::SkipEmptyParts);
4174 			if ( words.count() > 1 ) {
4175 				switch ( words[1][0].toLatin1() ) {
4176 					case 'C':
4177 						softwareListStateHash[listName][words[0]] = 'C';
4178 						break;
4179 					case 'M':
4180 						softwareListStateHash[listName][words[0]] = 'M';
4181 						break;
4182 					case 'I':
4183 						softwareListStateHash[listName][words[0]] = 'I';
4184 						break;
4185 					case 'N':
4186 						softwareListStateHash[listName][words[0]] = 'N';
4187 						break;
4188 					case 'U':
4189 					default:
4190 						softwareListStateHash[listName][words[0]] = 'U';
4191 						break;
4192 				}
4193 			}
4194 		}
4195 		stateCacheFile.close();
4196 		if ( !qmc2SoftwareList->interruptLoad )
4197 			newSoftwareStates = true;
4198 		else
4199 			softwareListStateHash[listName].clear();
4200 	}
4201 }
4202 
startElement(const QString &,const QString &,const QString & qName,const QXmlAttributes & attributes)4203 bool SoftwareListXmlHandler::startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &qName, const QXmlAttributes &attributes)
4204 {
4205 	if ( qmc2SoftwareList->interruptLoad )
4206 		return false;
4207 
4208 	if ( ++elementCounter % QMC2_SWLIST_LOAD_RESPONSE_LONG == 0 ) {
4209 		qmc2SoftwareList->updateStats(this);
4210 		qApp->processEvents();
4211 	}
4212 
4213 	if ( qName == "softwarelist" ) {
4214 		softwareListName = attributes.value("name");
4215 		softwareListHidden = hiddenLists()->contains(softwareListName);
4216 		compatFilters = systemSoftwareFilterHash.value(qmc2SoftwareList->systemName);
4217 		if ( qmc2SoftwareList->toolButtonSoftwareStates->isChecked() )
4218 			if ( !softwareListStateHash.contains(softwareListName) )
4219 				loadSoftwareStates(softwareListName);
4220 		return true;
4221 	}
4222 
4223 	if ( qName == "software" ) {
4224 		softwareName = attributes.value("name");
4225 		softwareSupported = attributes.value("supported");
4226 		softwareParentName = attributes.value("cloneof");
4227 		QString setKey(softwareListName + ':' + softwareName);
4228 		if ( !softwareParentName.isEmpty() )
4229 			softwareParentHash.insert(setKey, softwareListName + ':' + softwareParentName);
4230 		else
4231 			softwareParentHash.insert(setKey, "<np>");
4232 		if ( softwareSupported.isEmpty() || softwareSupported == "yes" )
4233 			softwareSupported = QObject::tr("yes");
4234 		else if ( softwareSupported == "no" )
4235 			softwareSupported = QObject::tr("no");
4236 		else
4237 			softwareSupported = QObject::tr("partially");
4238 		softwareItem = new SoftwareItem((QTreeWidget *)0);
4239 		itemList() << softwareItem;
4240 		softwareItemHash.insert(setKey, softwareItem);
4241 		SoftwareItem *subItem = new SoftwareItem(softwareItem);
4242 		subItem->setText(QMC2_SWLIST_COLUMN_TITLE, MachineList::trWaitingForData);
4243 		softwareItem->setText(QMC2_SWLIST_COLUMN_NAME, softwareName);
4244 		softwareItem->setText(QMC2_SWLIST_COLUMN_LIST, softwareListName);
4245 		softwareItem->setText(QMC2_SWLIST_COLUMN_SUPPORTED, softwareSupported);
4246 		numTotal++;
4247 		if ( qmc2SoftwareList->toolButtonSoftwareStates->isChecked() ) {
4248 			if ( softwareListStateHash.value(softwareListName).contains(softwareName) ) {
4249 				switch ( softwareListStateHash.value(softwareListName).value(softwareName) ) {
4250 					case 'C':
4251 						numCorrect++;
4252 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_correct.png")));
4253 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "C");
4254 						if ( qmc2SoftwareList->stateFilter->checkBoxStateFilter->isChecked() ) {
4255 							if ( !qmc2SoftwareList->stateFilter->toolButtonCorrect->isChecked() || softwareListHidden )
4256 								hideList() << softwareItem;
4257 						} else if ( softwareListHidden )
4258 							hideList() << softwareItem;
4259 						break;
4260 					case 'M':
4261 						numMostlyCorrect++;
4262 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_mostlycorrect.png")));
4263 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "M");
4264 						if ( qmc2SoftwareList->stateFilter->checkBoxStateFilter->isChecked() ) {
4265 							if ( !qmc2SoftwareList->stateFilter->toolButtonMostlyCorrect->isChecked() || softwareListHidden )
4266 								hideList() << softwareItem;
4267 						} else if ( softwareListHidden )
4268 							hideList() << softwareItem;
4269 						break;
4270 					case 'I':
4271 						numIncorrect++;
4272 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_incorrect.png")));
4273 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "I");
4274 						if ( qmc2SoftwareList->stateFilter->checkBoxStateFilter->isChecked() ) {
4275 							if ( !qmc2SoftwareList->stateFilter->toolButtonIncorrect->isChecked() || softwareListHidden )
4276 								hideList() << softwareItem;
4277 						} else if ( softwareListHidden )
4278 							hideList() << softwareItem;
4279 						break;
4280 					case 'N':
4281 						numNotFound++;
4282 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_notfound.png")));
4283 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "N");
4284 						if ( qmc2SoftwareList->stateFilter->checkBoxStateFilter->isChecked() ) {
4285 							if ( !qmc2SoftwareList->stateFilter->toolButtonNotFound->isChecked() || softwareListHidden )
4286 								hideList() << softwareItem;
4287 						} else if ( softwareListHidden )
4288 							hideList() << softwareItem;
4289 						break;
4290 					case 'U':
4291 					default:
4292 						numUnknown++;
4293 						softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
4294 						softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "U");
4295 						if ( qmc2SoftwareList->stateFilter->checkBoxStateFilter->isChecked() ) {
4296 							if ( !qmc2SoftwareList->stateFilter->toolButtonUnknown->isChecked() || softwareListHidden )
4297 								hideList() << softwareItem;
4298 						} else if ( softwareListHidden )
4299 							hideList() << softwareItem;
4300 						break;
4301 				}
4302 			} else {
4303 				numUnknown++;
4304 				softwareItem->setIcon(QMC2_SWLIST_COLUMN_TITLE, QIcon(QString::fromUtf8(":/data/img/software_unknown.png")));
4305 				softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_NAME, "U");
4306 				if ( qmc2SoftwareList->stateFilter->checkBoxStateFilter->isChecked() ) {
4307 					if ( !qmc2SoftwareList->stateFilter->toolButtonUnknown->isChecked() || softwareListHidden )
4308 						hideList() << softwareItem;
4309 				} else if ( softwareListHidden )
4310 					hideList() << softwareItem;
4311 			}
4312 		} else if ( softwareListHidden )
4313 			hideList() << softwareItem;
4314 		return true;
4315 	}
4316 
4317 	if ( qName == "part" ) {
4318 		softwarePart = attributes.value("name");
4319 		QString parts = softwareItem->text(QMC2_SWLIST_COLUMN_PART);
4320 		softwareInterface = attributes.value("interface");
4321 		QString interfaces = softwareItem->text(QMC2_SWLIST_COLUMN_INTERFACE);
4322 		if ( parts.isEmpty() )
4323 			softwareItem->setText(QMC2_SWLIST_COLUMN_PART, softwarePart);
4324 		else
4325 			softwareItem->setText(QMC2_SWLIST_COLUMN_PART, parts + "," + softwarePart);
4326 		if ( interfaces.isEmpty() )
4327 			softwareItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, softwareInterface);
4328 		else
4329 			softwareItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, interfaces + "," + softwareInterface);
4330 		return true;
4331 	}
4332 
4333 	if ( qName == "feature" ) {
4334 		if ( attributes.value("name") == "compatibility" ) {
4335 			// we use the invisible whatsThis data of the title column to store the software-compatibility list
4336 			QString partCompat = attributes.value("value");
4337 			if ( !partCompat.isEmpty() ) {
4338 				softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_TITLE, partCompat);
4339 				if ( qmc2SoftwareList->toolButtonCompatFilterToggle->isChecked() ) {
4340 					QStringList compatList = partCompat.split(",", QString::SkipEmptyParts);
4341 					bool showItem = compatList.isEmpty() || compatFilters.isEmpty();
4342 					if ( softwareListHidden )
4343 						showItem = false;
4344 					else for (int i = 0; i < compatList.count() && !showItem; i++)
4345 						for (int j = 0; j < compatFilters.count() && !showItem; j++)
4346 							showItem = (compatList[i] == compatFilters[j]);
4347 					if ( !softwareItem->isHidden() )
4348 						softwareItem->setHidden(!showItem);
4349 				}
4350 			}
4351 			return true;
4352 		}
4353 		/*
4354 		if ( attributes.value("name") == "requirement" ) {
4355 			// we use the invisible whatsThis data of the part column to store the requirements
4356 			QString requirement = attributes.value("value");
4357 			if ( !requirement.isEmpty() ) {
4358 				QStringList currentRequirements = softwareItem->whatsThis(QMC2_SWLIST_COLUMN_PART).split("\t", QString::SkipEmptyParts);
4359 				currentRequirements << requirement;
4360 				currentRequirements.removeDuplicates();
4361 				softwareItem->setWhatsThis(QMC2_SWLIST_COLUMN_PART, currentRequirements.join("\t"));
4362 			}
4363 			return true;
4364 		}
4365 		*/
4366 	}
4367 
4368 	if ( qName == "description" || qName == "year" || qName == "publisher" )
4369 		currentText.clear();
4370 
4371 	return true;
4372 }
4373 
endElement(const QString &,const QString &,const QString & qName)4374 bool SoftwareListXmlHandler::endElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &qName)
4375 {
4376 	if ( qmc2SoftwareList->interruptLoad )
4377 		return false;
4378 
4379 	if ( qName == "description" ) {
4380 		softwareTitle = currentText;
4381 		softwareItem->setText(QMC2_SWLIST_COLUMN_TITLE, softwareTitle);
4382 		return true;
4383 	}
4384 
4385 	if ( qName == "year" ) {
4386 		softwareYear = currentText;
4387 		softwareItem->setText(QMC2_SWLIST_COLUMN_YEAR, softwareYear);
4388 		return true;
4389 	}
4390 
4391 	if ( qName == "publisher" ) {
4392 		softwarePublisher = currentText;
4393 		softwareItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, softwarePublisher);
4394 		return true;
4395 	}
4396 
4397 	if ( qName == "softwarelist" ) {
4398 		parentTreeWidget->insertTopLevelItems(0, itemList());
4399 		itemList().clear();
4400 		foreach (QTreeWidgetItem *item, hideList())
4401 			item->setHidden(true);
4402 		hideList().clear();
4403 	}
4404 
4405 	return true;
4406 }
4407 
characters(const QString & str)4408 bool SoftwareListXmlHandler::characters(const QString &str)
4409 {
4410 	currentText += QString::fromUtf8(str.toUtf8());
4411 	return true;
4412 }
4413 
SoftwareSnap(QWidget * parent)4414 SoftwareSnap::SoftwareSnap(QWidget *parent) :
4415 	QWidget(parent, Qt::ToolTip)
4416 {
4417 	setAttribute(Qt::WA_TranslucentBackground);
4418 	setFocusPolicy(Qt::NoFocus);
4419 	snapForcedResetTimer.setSingleShot(true);
4420 	connect(&snapForcedResetTimer, SIGNAL(timeout()), this, SLOT(resetSnapForced()));
4421 
4422 	ctxMenuRequested = false;
4423 
4424 	contextMenu = new QMenu(this);
4425 	contextMenu->hide();
4426 
4427 	QString s;
4428 	QAction *action;
4429 
4430 	s = tr("Copy image to clipboard");
4431 	action = contextMenu->addAction(s);
4432 	action->setToolTip(s); action->setStatusTip(s);
4433 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/editcopy.png")));
4434 	connect(action, SIGNAL(triggered()), this, SLOT(copyToClipboard()));
4435 
4436 	s = tr("Copy file path to clipboard");
4437 	action = contextMenu->addAction(s);
4438 	action->setToolTip(s); action->setStatusTip(s);
4439 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/editcopy.png")));
4440 	connect(action, SIGNAL(triggered()), this, SLOT(copyPathToClipboard()));
4441 	actionCopyPathToClipboard = action;
4442 
4443 	contextMenu->addSeparator();
4444 
4445 	s = tr("Zoom in (+10%)");
4446 	action = contextMenu->addAction(s);
4447 	action->setToolTip(s); action->setStatusTip(s);
4448 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/zoom-in.png")));
4449 	connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
4450 
4451 	s = tr("Zoom out (-10%)");
4452 	action = contextMenu->addAction(s);
4453 	action->setToolTip(s); action->setStatusTip(s);
4454 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/zoom-out.png")));
4455 	connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
4456 
4457 	s = tr("Reset zoom (100%)");
4458 	action = contextMenu->addAction(s);
4459 	action->setToolTip(s); action->setStatusTip(s);
4460 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/zoom-none.png")));
4461 	connect(action, SIGNAL(triggered()), this, SLOT(resetZoom()));
4462 
4463 	contextMenu->addSeparator();
4464 
4465 	s = tr("Refresh cache slot");
4466 	action = contextMenu->addAction(s);
4467 	action->setToolTip(s); action->setStatusTip(s);
4468 	action->setIcon(QIcon(QString::fromUtf8(":/data/img/reload.png")));
4469 	connect(action, SIGNAL(triggered()), this, SLOT(refresh()));
4470 
4471 	zoom = qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapZoom", 100).toInt();
4472 
4473 	openSource();
4474 }
4475 
~SoftwareSnap()4476 SoftwareSnap::~SoftwareSnap()
4477 {
4478 	closeSource();
4479 }
4480 
openSource()4481 void SoftwareSnap::openSource()
4482 {
4483 	if ( useZip() ) {
4484 		foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFile").toString().split(";", QString::SkipEmptyParts)) {
4485 			snapFileMap[filePath] = unzOpen(filePath.toUtf8().constData());
4486 			if ( snapFileMap[filePath] == 0 )
4487 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open software snap-shot file, please check access permissions for %1").arg(filePath));
4488 		}
4489 	} else if ( useSevenZip() ) {
4490 		foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFile").toString().split(";", QString::SkipEmptyParts)) {
4491 			SevenZipFile *snapFile = new SevenZipFile(filePath);
4492 			if ( !snapFile->open() ) {
4493 				  qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open software snap-shot file %1").arg(filePath) + " - " + tr("7z error") + ": " + snapFile->lastError());
4494 				  delete snapFile;
4495 			} else {
4496 				snapFileMap7z[filePath] = snapFile;
4497 				connect(snapFile, SIGNAL(dataReady()), this, SLOT(sevenZipDataReady()));
4498 			}
4499 		}
4500 	}
4501 #if defined(QMC2_LIBARCHIVE_ENABLED)
4502 	else if ( useArchive() ) {
4503 		foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFile").toString().split(";", QString::SkipEmptyParts)) {
4504 			ArchiveFile *snapFile = new ArchiveFile(filePath);
4505 			if ( !snapFile->open() )
4506 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open software snap-shot file %1").arg(filePath) + " - " + tr("libarchive error") + ": " + snapFile->errorString());
4507 			else
4508 				snapArchiveMap[filePath] = snapFile;
4509 		}
4510 	}
4511 #endif
4512 	reloadActiveFormats();
4513 }
4514 
closeSource()4515 void SoftwareSnap::closeSource()
4516 {
4517 	QMapIterator<QString, unzFile> itZip(snapFileMap);
4518 	while ( itZip.hasNext() ) {
4519 		itZip.next();
4520 		unzClose(itZip.value());
4521 	}
4522 	snapFileMap.clear();
4523 	QMapIterator<QString, SevenZipFile*> it7z(snapFileMap7z);
4524 	while ( it7z.hasNext() ) {
4525 		it7z.next();
4526 		it7z.value()->close();
4527 		delete it7z.value();
4528 	}
4529 	snapFileMap7z.clear();
4530 #if defined(QMC2_LIBARCHIVE_ENABLED)
4531 	QMapIterator<QString, ArchiveFile*> itArchive(snapArchiveMap);
4532 	while ( itArchive.hasNext() ) {
4533 		itArchive.next();
4534 		itArchive.value()->close();
4535 		delete itArchive.value();
4536 	}
4537 	snapArchiveMap.clear();
4538 #endif
4539 }
4540 
primaryPathFor(QString list,QString name)4541 QString SoftwareSnap::primaryPathFor(QString list, QString name)
4542 {
4543 	if ( !qmc2UseSoftwareSnapFile ) {
4544 		QStringList fl = qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapDirectory").toString().split(";", QString::SkipEmptyParts);
4545 		QString baseDirectory;
4546 		if ( fl.count() > 0 )
4547 			baseDirectory = fl[0];
4548 		return QDir::toNativeSeparators(QDir::cleanPath(baseDirectory + "/" + list + "/" + name + ".png"));
4549 	} else // we don't support on-the-fly image replacement for zipped images yet!
4550 		return QString();
4551 }
4552 
replaceImage(QString list,QString name,QPixmap & pixmap)4553 bool SoftwareSnap::replaceImage(QString list, QString name, QPixmap &pixmap)
4554 {
4555 	if ( !qmc2UseSoftwareSnapFile ) {
4556 		QString savePath = primaryPathFor(list, name);
4557 		if ( !savePath.isEmpty() ) {
4558 			bool goOn = true;
4559 			if ( QFile::exists(savePath) ) {
4560 				QString backupPath = savePath + ".bak";
4561 				if ( QFile::exists(backupPath) )
4562 					QFile::remove(backupPath);
4563 				if ( !QFile::copy(savePath, backupPath) ) {
4564 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't create backup of existing image file '%1' as '%2'").arg(savePath).arg(backupPath));
4565 					goOn = false;
4566 				}
4567 			}
4568 			if ( goOn ) {
4569 				QString primaryPath = QFileInfo(savePath).absoluteDir().absolutePath();
4570 				QDir ppDir(primaryPath);
4571 				if ( !ppDir.exists() )
4572 					ppDir.mkpath(primaryPath);
4573 				if ( pixmap.save(savePath, "PNG") ) {
4574 					refresh();
4575 					if ( qmc2SoftwareSnapshot )
4576 						qmc2SoftwareSnapshot->refresh();
4577 					return true;
4578 				} else {
4579 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't create image file '%1'").arg(savePath));
4580 					return false;
4581 				}
4582 			} else
4583 				return false;
4584 		} else {
4585 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't determine primary path for image-type '%1'").arg(tr("software snapshot")));
4586 			return false;
4587 		}
4588 	} else // we don't support on-the-fly image replacement for zipped images yet!
4589 		return false;
4590 }
4591 
zoomIn()4592 void SoftwareSnap::zoomIn()
4593 {
4594 	zoom += 10;
4595 	if ( zoom > 400 )
4596 		zoom = 400;
4597 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapZoom", zoom);
4598 	refresh();
4599 }
4600 
zoomOut()4601 void SoftwareSnap::zoomOut()
4602 {
4603 	zoom -= 10;
4604 	if ( zoom < 10 )
4605 		zoom = 10;
4606 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapZoom", zoom);
4607 	refresh();
4608 }
4609 
resetZoom()4610 void SoftwareSnap::resetZoom()
4611 {
4612 	zoom = 100;
4613 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapZoom", zoom);
4614 	refresh();
4615 }
4616 
mousePressEvent(QMouseEvent * e)4617 void SoftwareSnap::mousePressEvent(QMouseEvent *e)
4618 {
4619 	if ( e->button() != Qt::RightButton)
4620 		hide();
4621 	else
4622 		ctxMenuRequested = true;
4623 }
4624 
enterEvent(QEvent * e)4625 void SoftwareSnap::enterEvent(QEvent *e)
4626 {
4627 	if ( contextMenu->isVisible() )
4628 		QTimer::singleShot(0, contextMenu, SLOT(hide()));
4629 	ctxMenuRequested = false;
4630 
4631 	QWidget::enterEvent(e);
4632 }
4633 
leaveEvent(QEvent * e)4634 void SoftwareSnap::leaveEvent(QEvent *e)
4635 {
4636 	if ( !qmc2SoftwareList->snapForced && !ctxMenuRequested ) {
4637 		myItem = 0;
4638 		hide();
4639 	}  else if ( !qmc2SoftwareList->snapForced )
4640 		QTimer::singleShot(QMC2_SWSNAP_UNFORCE_DELAY, qmc2SoftwareList, SLOT(checkSoftwareSnap()));
4641 
4642 	ctxMenuRequested = contextMenu->isVisible();
4643 
4644 	QWidget::leaveEvent(e);
4645 }
4646 
paintEvent(QPaintEvent *)4647 void SoftwareSnap::paintEvent(QPaintEvent *)
4648 {
4649 	QPainter p(this);
4650 	loadImage();
4651 	p.eraseRect(rect());
4652 	p.end();
4653 }
4654 
loadImage(bool fromParent)4655 void SoftwareSnap::loadImage(bool fromParent)
4656 {
4657 	ctxMenuRequested = false;
4658 
4659 	if ( !qmc2SoftwareList || qmc2SoftwareSnapPosition == QMC2_SWSNAP_POS_DISABLE_SNAPS ) {
4660 		myItem = 0;
4661 		resetSnapForced();
4662 		myCacheKey.clear();
4663 		return;
4664 	}
4665 
4666 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/AutoDisableSoftwareSnap", true).toBool() ) {
4667 		if ( qmc2MainWindow->stackedWidgetSpecial->currentIndex() == QMC2_SPECIAL_SOFTWARE_PAGE || (qmc2MainWindow->tabWidgetSoftwareDetail->parent() == qmc2MainWindow && qmc2MainWindow->tabWidgetSoftwareDetail->isVisible()) ) {
4668 			myItem = 0;
4669 			resetSnapForced();
4670 			myCacheKey.clear();
4671 			return;
4672 		}
4673 	}
4674 
4675 #ifdef QMC2_DEBUG
4676 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, QString("DEBUG: SoftwareSnap::loadImage(): snapForced = '%1'").arg(qmc2SoftwareList->snapForced ? "true" : "false"));
4677 #endif
4678 
4679 	// check if the mouse cursor is still on a software item
4680 	QTreeWidgetItem *item = 0;
4681 	QTreeWidget *treeWidget = 0;
4682 	bool mouseOnView = false;
4683 	QRect rect;
4684 
4685 	switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
4686 		case QMC2_SWLIST_KNOWN_SW_PAGE:
4687 			if ( qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->underMouse() ) {
4688 				mouseOnView = true;
4689 				item = qmc2SoftwareList->treeWidgetKnownSoftware->itemAt(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapFromGlobal(QCursor::pos()));
4690 				if ( item ) {
4691 					rect = qmc2SoftwareList->treeWidgetKnownSoftware->visualItemRect(item);
4692 					rect.setWidth(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->width());
4693 					rect.setX(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->x());
4694 					rect.translate(0, 4);
4695 					position = qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(rect.bottomLeft());
4696 				}
4697 			}
4698 			if ( qmc2SoftwareList->viewTree() )
4699 				treeWidget = qmc2SoftwareList->treeWidgetKnownSoftwareTree;
4700 			else
4701 				treeWidget = qmc2SoftwareList->treeWidgetKnownSoftware;
4702 			break;
4703 		case QMC2_SWLIST_FAVORITES_PAGE:
4704 			if ( qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->underMouse() ) {
4705 				mouseOnView = true;
4706 				item = qmc2SoftwareList->treeWidgetFavoriteSoftware->itemAt(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapFromGlobal(QCursor::pos()));
4707 				if ( item ) {
4708 					rect = qmc2SoftwareList->treeWidgetFavoriteSoftware->visualItemRect(item);
4709 					rect.setWidth(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->width());
4710 					rect.setX(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->x());
4711 					rect.translate(0, 4);
4712 					position = qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(rect.bottomLeft());
4713 				}
4714 			}
4715 			treeWidget = qmc2SoftwareList->treeWidgetFavoriteSoftware;
4716 			break;
4717 		case QMC2_SWLIST_SEARCH_PAGE:
4718 			if ( qmc2SoftwareList->treeWidgetSearchResults->viewport()->underMouse() ) {
4719 				mouseOnView = true;
4720 				item = qmc2SoftwareList->treeWidgetSearchResults->itemAt(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapFromGlobal(QCursor::pos()));
4721 				if ( item ) {
4722 					rect = qmc2SoftwareList->treeWidgetSearchResults->visualItemRect(item);
4723 					rect.setWidth(qmc2SoftwareList->treeWidgetSearchResults->viewport()->width());
4724 					rect.setX(qmc2SoftwareList->treeWidgetSearchResults->viewport()->x());
4725 					rect.translate(0, 4);
4726 					position = qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(rect.bottomLeft());
4727 				}
4728 			}
4729 			treeWidget = qmc2SoftwareList->treeWidgetSearchResults;
4730 			break;
4731 	}
4732 
4733 	// try to fall back to 'selected item' if applicable (no mouse hover)
4734 	if ( (!item || qmc2SoftwareList->snapForced) && mouseOnView ) {
4735 		if ( qmc2SoftwareList->snapForced && myItem != 0 ) {
4736 			item = myItem;
4737 			switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
4738 				case QMC2_SWLIST_KNOWN_SW_PAGE:
4739 					if ( qmc2SoftwareList->viewTree() ) {
4740 						rect = qmc2SoftwareList->treeWidgetKnownSoftwareTree->visualItemRect(item);
4741 						rect.setWidth(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->width());
4742 						rect.setX(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->x());
4743 						rect.translate(0, 4);
4744 						position = qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(rect.bottomLeft());
4745 						treeWidget = qmc2SoftwareList->treeWidgetKnownSoftwareTree;
4746 					} else {
4747 						rect = qmc2SoftwareList->treeWidgetKnownSoftware->visualItemRect(item);
4748 						rect.setWidth(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->width());
4749 						rect.setX(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->x());
4750 						rect.translate(0, 4);
4751 						position = qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(rect.bottomLeft());
4752 						treeWidget = qmc2SoftwareList->treeWidgetKnownSoftware;
4753 					}
4754 					break;
4755 				case QMC2_SWLIST_FAVORITES_PAGE:
4756 					rect = qmc2SoftwareList->treeWidgetFavoriteSoftware->visualItemRect(item);
4757 					rect.setWidth(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->width());
4758 					rect.setX(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->x());
4759 					rect.translate(0, 4);
4760 					position = qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(rect.bottomLeft());
4761 					treeWidget = qmc2SoftwareList->treeWidgetFavoriteSoftware;
4762 					break;
4763 				case QMC2_SWLIST_SEARCH_PAGE:
4764 					rect = qmc2SoftwareList->treeWidgetSearchResults->visualItemRect(item);
4765 					rect.setWidth(qmc2SoftwareList->treeWidgetSearchResults->viewport()->width());
4766 					rect.setX(qmc2SoftwareList->treeWidgetSearchResults->viewport()->x());
4767 					rect.translate(0, 4);
4768 					position = qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(rect.bottomLeft());
4769 					treeWidget = qmc2SoftwareList->treeWidgetSearchResults;
4770 					break;
4771 			}
4772 		}
4773 	}
4774 
4775 	// if we can't figure out which item we're on, let's escape from here...
4776 	if ( !item || !treeWidget || !mouseOnView ) {
4777 		myItem = 0;
4778 		resetSnapForced();
4779 		qmc2SoftwareList->cancelSoftwareSnap();
4780 		myCacheKey.clear();
4781 		return;
4782 	}
4783 
4784 	if ( !qmc2Config->value(QMC2_FRONTEND_PREFIX + "Layout/SoftwareList/SoftwareSnapOnMouseHover", false).toBool() ) {
4785 		// paranoia check :)
4786 		QList<QTreeWidgetItem *> itemList = treeWidget->selectedItems();
4787 		if ( !itemList.isEmpty() )
4788 			if ( itemList[0] != item ) {
4789 				myItem = 0;
4790 				resetSnapForced();
4791 				qmc2SoftwareList->cancelSoftwareSnap();
4792 				myCacheKey.clear();
4793 				return;
4794 			}
4795 	}
4796 
4797 	listName = item->text(QMC2_SWLIST_COLUMN_LIST);
4798 	entryName = item->text(QMC2_SWLIST_COLUMN_NAME);
4799 	myItem = (SoftwareItem *)item;
4800 	myCacheKey = "sws_" + listName + "_" + entryName;
4801 	QString onBehalfOf(myCacheKey);
4802 
4803 	if ( fromParent ) {
4804 		QString parentKey(softwareParentHash.value(listName + ':' + entryName));
4805 		if ( !parentKey.isEmpty() && parentKey != "<np>" ) {
4806 			QString parentName(parentKey.split(':', QString::SkipEmptyParts).at(1));
4807 			myCacheKey = "sws_" + listName + "_" + parentName;
4808 		}
4809 	}
4810 
4811 	ImagePixmap pm;
4812 	bool pmLoaded = false;
4813 	bool drawFrame = true;
4814 	ImagePixmap *cpm = qmc2ImagePixmapCache.object(myCacheKey);
4815 	if ( cpm ) {
4816 		pmLoaded = true;
4817 		pm = *cpm;
4818 	}
4819 
4820 	if ( !pmLoaded ) {
4821 		if ( qmc2UseSoftwareSnapFile ) {
4822 			if ( useZip() ) {
4823 				// try loading image from (semicolon-separated) ZIP archive(s)
4824 				if ( snapFileMap.isEmpty() ) {
4825 					foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFile").toString().split(";", QString::SkipEmptyParts)) {
4826 						snapFileMap[filePath] = unzOpen(filePath.toUtf8().constData());
4827 						if ( snapFileMap[filePath] == 0 )
4828 							qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open software snap-shot file, please check access permissions for %1").arg(filePath));
4829 					}
4830 				}
4831 				foreach (unzFile snapFile, snapFileMap) {
4832 					if ( snapFile ) {
4833 						bool fileOk = true;
4834 						QByteArray imageData;
4835 						foreach (int format, activeFormats) {
4836 							QString formatName = ImageWidget::formatNames[format];
4837 							foreach (QString extension, ImageWidget::formatExtensions[format].split(", ", QString::SkipEmptyParts)) {
4838 								QString pathInZip = listName + "/" + entryName + "." + extension;
4839 								if ( unzLocateFile(snapFile, pathInZip.toUtf8().constData(), 0) == UNZ_OK ) {
4840 									if ( unzOpenCurrentFile(snapFile) == UNZ_OK ) {
4841 										char imageBuffer[QMC2_ZIP_BUFFER_SIZE];
4842 										int len;
4843 										while ( (len = unzReadCurrentFile(snapFile, &imageBuffer, QMC2_ZIP_BUFFER_SIZE)) > 0 )
4844 											imageData.append(imageBuffer, len);
4845 										unzCloseCurrentFile(snapFile);
4846 										fileOk = true;
4847 									} else
4848 										fileOk = false;
4849 								} else
4850 									fileOk = false;
4851 
4852 								if ( fileOk ) {
4853 									if ( pm.loadFromData(imageData, formatName.toUtf8().constData()) ) {
4854 										pmLoaded = true;
4855 										qmc2ImagePixmapCache.insert(onBehalfOf, new ImagePixmap(pm), pm.toImage().byteCount());
4856 										break;
4857 									}
4858 								}
4859 							}
4860 
4861 							if ( pmLoaded )
4862 								break;
4863 						}
4864 					}
4865 
4866 					if ( pmLoaded )
4867 						break;
4868 				}
4869 			} else if ( useSevenZip() ) {
4870 				// try loading image from (semicolon-separated) 7z archive(s)
4871 				if ( snapFileMap7z.isEmpty() ) {
4872 					foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFile").toString().split(";", QString::SkipEmptyParts)) {
4873 						SevenZipFile *snapFile = new SevenZipFile(filePath);
4874 						if ( !snapFile->open() ) {
4875 							  qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open software snap-shot file %1").arg(filePath) + " - " + tr("7z error") + ": " + snapFile->lastError());
4876 							  delete snapFile;
4877 						} else {
4878 							snapFileMap7z[filePath] = snapFile;
4879 							connect(snapFile, SIGNAL(dataReady()), this, SLOT(sevenZipDataReady()));
4880 						}
4881 					}
4882 				}
4883 				foreach (SevenZipFile *snapFile, snapFileMap7z) {
4884 					if ( snapFile ) {
4885 						bool fileOk = true;
4886 						QByteArray imageData;
4887 						foreach (int format, activeFormats) {
4888 							QString formatName = ImageWidget::formatNames[format];
4889 							foreach (QString extension, ImageWidget::formatExtensions[format].split(", ", QString::SkipEmptyParts)) {
4890 								bool isFillingDictionary = false;
4891 								QString pathIn7z = listName + "/" + entryName + "." + extension;
4892 								int index = snapFile->indexOfName(pathIn7z);
4893 								if ( index >= 0 ) {
4894 									m_async = true;
4895 									quint64 readLength = snapFile->read(index, &imageData, &m_async);
4896 									if ( readLength == 0 && m_async ) {
4897 										qmc2ImagePixmapCache.remove(onBehalfOf);
4898 										isFillingDictionary = true;
4899 										fileOk = true;
4900 									} else
4901 										fileOk = !snapFile->hasError();
4902 								} else
4903 									fileOk = false;
4904 
4905 								if ( fileOk ) {
4906 									if ( isFillingDictionary ) {
4907 										pm = qmc2MainWindow->qmc2GhostImagePixmap.scaledToHeight(qmc2MainWindow->qmc2GhostImagePixmap.height()/2, Qt::SmoothTransformation);
4908 										pm.isGhost = false;
4909 										QPainter p;
4910 										QString message = tr("Decompressing archive, please wait...");
4911 										p.begin(&pm);
4912 										p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform);
4913 										QFont f(qApp->font());
4914 										f.setWeight(QFont::Bold);
4915 										f.setPointSize(f.pointSize() * 2);
4916 										QFontMetrics fm(f);
4917 										int adjustment = fm.height() / 2;
4918 										p.setFont(f);
4919 										QRect outerRect = p.boundingRect(pm.rect(), Qt::AlignCenter | Qt::TextWordWrap, message).adjusted(-adjustment, -adjustment, adjustment, adjustment);
4920 										QPainterPath pp;
4921 										pp.addRoundedRect(outerRect, 5, 5);
4922 										p.fillPath(pp, QBrush(QColor(0, 0, 0, 128), Qt::SolidPattern));
4923 										p.setPen(QColor(255, 255, 0, 255));
4924 										p.drawText(pm.rect(), Qt::AlignCenter | Qt::TextWordWrap, message);
4925 										p.end();
4926 										pmLoaded = true;
4927 										drawFrame = false;
4928 										enableWidgets(false);
4929 									} else if ( pm.loadFromData(imageData, formatName.toUtf8().constData()) ) {
4930 										pmLoaded = true;
4931 										qmc2ImagePixmapCache.insert(onBehalfOf, new ImagePixmap(pm), pm.toImage().byteCount());
4932 										break;
4933 									}
4934 								}
4935 							}
4936 
4937 							if ( pmLoaded )
4938 								break;
4939 						}
4940 					}
4941 
4942 					if ( pmLoaded )
4943 						break;
4944 				}
4945 			}
4946 		} else {
4947 			// try loading image from (semicolon-separated) software-snapshot folder(s)
4948 			pmLoaded = false;
4949 			foreach (QString baseDirectory, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapDirectory").toString().split(";", QString::SkipEmptyParts)) {
4950 				QDir snapDir(baseDirectory + "/" + listName);
4951 				foreach (int format, activeFormats) {
4952 					foreach (QString extension, ImageWidget::formatExtensions[format].split(", ", QString::SkipEmptyParts)) {
4953 						QString fullEntryName = entryName + "." + extension;
4954 						if ( snapDir.exists(fullEntryName) ) {
4955 							QString filePath = snapDir.absoluteFilePath(fullEntryName);
4956 							if ( pm.load(filePath) ) {
4957 								pmLoaded = true;
4958 								pm.imagePath = filePath;
4959 								qmc2ImagePixmapCache.insert(onBehalfOf, new ImagePixmap(pm), pm.toImage().byteCount());
4960 							}
4961 						}
4962 						if ( pmLoaded )
4963 							break;
4964 					}
4965 					if ( pmLoaded )
4966 						break;
4967 				}
4968 				if ( pmLoaded )
4969 					break;
4970 			}
4971 		}
4972 	}
4973 
4974 	if ( !pmLoaded && qmc2ParentImageFallback && !fromParent ) {
4975 		loadImage(true);
4976 		return;
4977 	}
4978 
4979 	if ( pmLoaded && !pm.isGhost ) {
4980 		qreal factor = (qreal)zoom / 100.0;
4981 		QSize zoomSize(factor * pm.size().width(), factor * pm.size().height());
4982 		pm = pm.scaled(zoomSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
4983 		resize(pm.size());
4984 		switch ( qmc2SoftwareSnapPosition ) {
4985 			case QMC2_SWSNAP_POS_ABOVE_CENTER:
4986 				rect.translate(0, -4);
4987 				switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
4988 					case QMC2_SWLIST_KNOWN_SW_PAGE:
4989 						if ( qmc2SoftwareList->viewTree() ) {
4990 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->rect().center()).x() - width() / 2);
4991 							position.setY(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
4992 						} else {
4993 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->rect().center()).x() - width() / 2);
4994 							position.setY(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
4995 						}
4996 						break;
4997 					case QMC2_SWLIST_FAVORITES_PAGE:
4998 						position.setX(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->rect().center()).x() - width() / 2);
4999 						position.setY(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5000 						break;
5001 					case QMC2_SWLIST_SEARCH_PAGE:
5002 						position.setX(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetSearchResults->viewport()->rect().center()).x() - width() / 2);
5003 						position.setY(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5004 						break;
5005 				}
5006 				break;
5007 
5008 			case QMC2_SWSNAP_POS_ABOVE_RIGHT:
5009 				rect.translate(0, -4);
5010 				switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
5011 					case QMC2_SWLIST_KNOWN_SW_PAGE:
5012 						if ( qmc2SoftwareList->viewTree() ) {
5013 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->rect().bottomRight()).x() - width());
5014 							position.setY(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5015 						} else {
5016 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->rect().bottomRight()).x() - width());
5017 							position.setY(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5018 						}
5019 						break;
5020 					case QMC2_SWLIST_FAVORITES_PAGE:
5021 						position.setX(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->rect().bottomRight()).x() - width());
5022 						position.setY(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5023 						break;
5024 					case QMC2_SWLIST_SEARCH_PAGE:
5025 						position.setX(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetSearchResults->viewport()->rect().bottomRight()).x() - width());
5026 						position.setY(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5027 						break;
5028 				}
5029 				break;
5030 
5031 			case QMC2_SWSNAP_POS_ABOVE_LEFT:
5032 				rect.translate(0, -4);
5033 				switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
5034 					case QMC2_SWLIST_KNOWN_SW_PAGE:
5035 						if ( qmc2SoftwareList->viewTree() )
5036 							position.setY(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5037 						else
5038 							position.setY(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5039 						break;
5040 					case QMC2_SWLIST_FAVORITES_PAGE:
5041 						position.setY(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5042 						break;
5043 					case QMC2_SWLIST_SEARCH_PAGE:
5044 						position.setY(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(rect.topLeft()).y() - height() - 4);
5045 						break;
5046 				}
5047 				break;
5048 
5049 			case QMC2_SWSNAP_POS_BELOW_RIGHT:
5050 				switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
5051 					case QMC2_SWLIST_KNOWN_SW_PAGE:
5052 						if ( qmc2SoftwareList->viewTree() )
5053 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->rect().bottomRight()).x() - width());
5054 						else
5055 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->rect().bottomRight()).x() - width());
5056 						break;
5057 					case QMC2_SWLIST_FAVORITES_PAGE:
5058 						position.setX(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->rect().bottomRight()).x() - width());
5059 						break;
5060 					case QMC2_SWLIST_SEARCH_PAGE:
5061 						position.setX(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetSearchResults->viewport()->rect().bottomRight()).x() - width());
5062 						break;
5063 				}
5064 				break;
5065 
5066 			case QMC2_SWSNAP_POS_BELOW_CENTER:
5067 				switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
5068 					case QMC2_SWLIST_KNOWN_SW_PAGE:
5069 						if ( qmc2SoftwareList->viewTree() )
5070 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftwareTree->viewport()->rect().center()).x() - width() / 2);
5071 						else
5072 							position.setX(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->rect().center()).x() - width() / 2);
5073 						break;
5074 					case QMC2_SWLIST_FAVORITES_PAGE:
5075 						position.setX(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->rect().center()).x() - width() / 2);
5076 						break;
5077 					case QMC2_SWLIST_SEARCH_PAGE:
5078 						position.setX(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapToGlobal(qmc2SoftwareList->treeWidgetSearchResults->viewport()->rect().center()).x() - width() / 2);
5079 						break;
5080 				}
5081 				break;
5082 
5083 			case QMC2_SWSNAP_POS_BELOW_LEFT:
5084 			default:
5085 				// already prepared above...
5086 				break;
5087 		}
5088 		move(position);
5089 		QPalette pal = palette();
5090 		if ( drawFrame ) {
5091 			QPainter p;
5092 			p.begin(&pm);
5093 			p.setPen(QPen(QColor(0, 0, 0, 64), 1));
5094 			rect = pm.rect();
5095 			rect.setWidth(rect.width() - 1);
5096 			rect.setHeight(rect.height() - 1);
5097 			p.drawRect(rect);
5098 			p.end();
5099 		}
5100 		pal.setBrush(QPalette::Window, pm);
5101 		setPalette(pal);
5102 		showNormal();
5103 		update();
5104 		snapForcedResetTimer.start(QMC2_SWSNAP_UNFORCE_DELAY);
5105 	} else {
5106 		myItem = 0;
5107 		resetSnapForced();
5108 		qmc2SoftwareList->cancelSoftwareSnap();
5109 	}
5110 }
5111 
refresh()5112 void SoftwareSnap::refresh()
5113 {
5114 	if ( !myCacheKey.isEmpty() ) {
5115 		qmc2ImagePixmapCache.remove(myCacheKey);
5116 		update();
5117 	}
5118 }
5119 
sevenZipDataReady()5120 void SoftwareSnap::sevenZipDataReady()
5121 {
5122 	update();
5123 	enableWidgets(true);
5124 }
5125 
enableWidgets(bool enable)5126 void SoftwareSnap::enableWidgets(bool enable)
5127 {
5128 	qmc2Options->radioButtonSoftwareSnapSelect->setEnabled(enable);
5129 	qmc2Options->lineEditSoftwareSnapFile->setEnabled(enable);
5130 	qmc2Options->comboBoxSoftwareSnapFileType->setEnabled(enable);
5131 	qmc2Options->toolButtonBrowseSoftwareSnapFile->setEnabled(enable);
5132 }
5133 
resetSnapForced()5134 void SoftwareSnap::resetSnapForced()
5135 {
5136 	if ( qmc2SoftwareList ) {
5137 		QTreeWidgetItem *item = 0;
5138 		if ( !qmc2SoftwareList->snapForced ) {
5139 			switch ( qmc2SoftwareList->toolBoxSoftwareList->currentIndex() ) {
5140 				case QMC2_SWLIST_KNOWN_SW_PAGE:
5141 					if ( qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->underMouse() ) {
5142 						item = qmc2SoftwareList->treeWidgetKnownSoftware->itemAt(qmc2SoftwareList->treeWidgetKnownSoftware->viewport()->mapFromGlobal(QCursor::pos()));
5143 						if ( item )
5144 							if ( item != myItem || item->text(QMC2_SWLIST_COLUMN_NAME) != entryName  ) {
5145 								qmc2SoftwareList->cancelSoftwareSnap();
5146 								qmc2SoftwareList->snapTimer.start(QMC2_SWSNAP_DELAY);
5147 							}
5148 					}
5149 					break;
5150 				case QMC2_SWLIST_FAVORITES_PAGE:
5151 					if ( qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->underMouse() ) {
5152 						item = qmc2SoftwareList->treeWidgetFavoriteSoftware->itemAt(qmc2SoftwareList->treeWidgetFavoriteSoftware->viewport()->mapFromGlobal(QCursor::pos()));
5153 						if ( item )
5154 							if ( item != myItem || item->text(QMC2_SWLIST_COLUMN_NAME) != entryName  ) {
5155 								qmc2SoftwareList->cancelSoftwareSnap();
5156 								qmc2SoftwareList->snapTimer.start(QMC2_SWSNAP_DELAY);
5157 							}
5158 					}
5159 					break;
5160 				case QMC2_SWLIST_SEARCH_PAGE:
5161 					if ( qmc2SoftwareList->treeWidgetSearchResults->viewport()->underMouse() ) {
5162 						item = qmc2SoftwareList->treeWidgetSearchResults->itemAt(qmc2SoftwareList->treeWidgetSearchResults->viewport()->mapFromGlobal(QCursor::pos()));
5163 						if ( item )
5164 							if ( item != myItem || item->text(QMC2_SWLIST_COLUMN_NAME) != entryName  ) {
5165 								qmc2SoftwareList->cancelSoftwareSnap();
5166 								qmc2SoftwareList->snapTimer.start(QMC2_SWSNAP_DELAY);
5167 							}
5168 					}
5169 					break;
5170 			}
5171 		}
5172 	}
5173 	qmc2SoftwareList->snapForced = false;
5174 }
5175 
contextMenuEvent(QContextMenuEvent * e)5176 void SoftwareSnap::contextMenuEvent(QContextMenuEvent *e)
5177 {
5178 	ctxMenuRequested = true;
5179 	if ( !myCacheKey.isEmpty() ) {
5180 		ImagePixmap *cpm = qmc2ImagePixmapCache.object(myCacheKey);
5181 		if ( cpm )
5182 			actionCopyPathToClipboard->setVisible(!cpm->imagePath.isEmpty());
5183 		else
5184 			actionCopyPathToClipboard->setVisible(false);
5185 	} else
5186 		actionCopyPathToClipboard->setVisible(false);
5187 	contextMenu->move(qmc2MainWindow->adjustedWidgetPosition(mapToGlobal(e->pos()), contextMenu));
5188 	contextMenu->show();
5189 }
5190 
copyToClipboard()5191 void SoftwareSnap::copyToClipboard()
5192 {
5193 	QPixmap pm(size());
5194 	render(&pm);
5195 	qApp->clipboard()->setPixmap(pm);
5196 }
5197 
copyPathToClipboard()5198 void SoftwareSnap::copyPathToClipboard()
5199 {
5200 	if ( !myCacheKey.isEmpty() ) {
5201 		ImagePixmap *cpm = qmc2ImagePixmapCache.object(myCacheKey);
5202 		if ( cpm )
5203 			qApp->clipboard()->setText(cpm->imagePath);
5204 	}
5205 }
5206 
reloadActiveFormats()5207 void SoftwareSnap::reloadActiveFormats()
5208 {
5209 	activeFormats.clear();
5210 	QStringList imgFmts = qmc2Config->value(QMC2_FRONTEND_PREFIX + "ActiveImageFormats/sws", QStringList()).toStringList();
5211 	if ( imgFmts.isEmpty() )
5212 		activeFormats << QMC2_IMAGE_FORMAT_INDEX_PNG;
5213 	else for (int i = 0; i < imgFmts.count(); i++)
5214 		activeFormats << imgFmts[i].toInt();
5215 }
5216 
useZip()5217 bool SoftwareSnap::useZip()
5218 {
5219 	return qmc2UseSoftwareSnapFile && qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFileType").toInt() == QMC2_IMG_FILETYPE_ZIP;
5220 }
5221 
useSevenZip()5222 bool SoftwareSnap::useSevenZip()
5223 {
5224 	return qmc2UseSoftwareSnapFile && qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFileType").toInt() == QMC2_IMG_FILETYPE_7Z;
5225 }
5226 
useArchive()5227 bool SoftwareSnap::useArchive()
5228 {
5229 	return qmc2UseSoftwareSnapFile && qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareSnapFileType").toInt() == QMC2_IMG_FILETYPE_ARCHIVE;
5230 }
5231 
SoftwareEntryXmlHandler(QTreeWidgetItem * item,bool viewTree)5232 SoftwareEntryXmlHandler::SoftwareEntryXmlHandler(QTreeWidgetItem *item, bool viewTree)
5233 {
5234 	parentTreeWidgetItem = (SoftwareItem *)item;
5235 	softwareName = parentTreeWidgetItem->text(QMC2_SWLIST_COLUMN_NAME);
5236 	softwareValid = success = false;
5237 	partItem = dataareaItem = romItem = infoItem = requirementItem = compatItem = 0;
5238 	elementCounter = animSequenceCounter = 0;
5239 	setViewTree(viewTree);
5240 }
5241 
~SoftwareEntryXmlHandler()5242 SoftwareEntryXmlHandler::~SoftwareEntryXmlHandler()
5243 {
5244 	// NOP
5245 }
5246 
startElement(const QString &,const QString &,const QString & qName,const QXmlAttributes & attributes)5247 bool SoftwareEntryXmlHandler::startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &qName, const QXmlAttributes &attributes)
5248 {
5249 	if ( ++elementCounter % QMC2_SWLIST_LOAD_RESPONSE_LONG == 0 ) {
5250 		QTreeWidgetItem *item = parentTreeWidgetItem->child(0);
5251 		if ( elementCounter % QMC2_SWLIST_LOAD_ANIM_DELAY == 0 ) {
5252 			if ( item->text(QMC2_SWLIST_COLUMN_TITLE).startsWith(QObject::tr("Searching")) ) {
5253 				QString dot(".");
5254 				item->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Searching") + dot.repeated(++animSequenceCounter));
5255 			}
5256 		}
5257 		parentTreeWidgetItem->treeWidget()->viewport()->update();
5258 		qApp->processEvents();
5259 	}
5260 
5261 	if ( !softwareValid ) {
5262 		if ( qName == "software" ) {
5263 			softwareValid = ( attributes.value("name") == softwareName );
5264 			if ( softwareValid ) {
5265 				qmc2SoftwareList->successfulLookups.clear();
5266 				parentTreeWidgetItem->child(0)->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Updating"));
5267 				parentTreeWidgetItem->treeWidget()->viewport()->update();
5268 				qApp->processEvents();
5269 			}
5270 		}
5271 
5272 		return true;
5273 	}
5274 
5275 	if ( qName == "part" ) {
5276 		if ( partItem == 0 ) {
5277 			partItem = new SoftwareItem((QTreeWidget *)0);
5278 			partItem->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Part:") + " " + attributes.value("name"));
5279 			partItem->setText(QMC2_SWLIST_COLUMN_PART, attributes.value("name"));
5280 			partItem->setText(QMC2_SWLIST_COLUMN_INTERFACE, attributes.value("interface"));
5281 			partItem->setText(QMC2_SWLIST_COLUMN_LIST, parentTreeWidgetItem->text(QMC2_SWLIST_COLUMN_LIST));
5282 			QStringList mountList;
5283 			QString mountDev(qmc2SoftwareList->lookupMountDevice(partItem->text(QMC2_SWLIST_COLUMN_PART), partItem->text(QMC2_SWLIST_COLUMN_INTERFACE), &mountList));
5284 			QComboBox *comboBoxMountDevices = 0;
5285 			if ( mountList.count() > 0 ) {
5286 				comboBoxMountDevices = new QComboBox;
5287 				comboBoxMountDevices->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
5288 				mountList.prepend(QObject::tr("Don't mount"));
5289 				mountList.prepend(QObject::tr("Auto mount"));
5290 				comboBoxMountDevices->insertItems(0, mountList);
5291 				comboBoxMountDevices->insertSeparator(QMC2_SWLIST_MSEL_SEPARATOR);
5292 				if ( !qmc2SoftwareList->autoMounted ) {
5293 					comboBoxMountDevices->setCurrentIndex(QMC2_SWLIST_MSEL_DONT_MOUNT); // ==> don't mount
5294 					partItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
5295 				} else {
5296 					comboBoxMountDevices->setCurrentIndex(QMC2_SWLIST_MSEL_AUTO_MOUNT); // ==> auto mount
5297 					if ( mountDev.isEmpty() )
5298 						partItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Not mounted"));
5299 					else
5300 						partItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Mounted on:") + " " + mountDev);
5301 				}
5302 				parentTreeWidgetItem->treeWidget()->setItemWidget(partItem, QMC2_SWLIST_COLUMN_PUBLISHER, comboBoxMountDevices);
5303 				QObject::connect(comboBoxMountDevices, SIGNAL(currentIndexChanged(int)), qmc2SoftwareList, SLOT(checkMountDeviceSelection()));
5304 			} else {
5305 				partItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("No mount device"));
5306 				partItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, QObject::tr("Unmanaged"));
5307 			}
5308 			partItems << partItem;
5309 			comboBoxes[partItem] = comboBoxMountDevices;
5310 		}
5311 
5312 		return true;
5313 	}
5314 
5315 	if ( qName == "feature" ) {
5316 		if ( partItem != 0 ) {
5317 			QString featureName = attributes.value("name");
5318 			if ( featureName == "part id" || featureName == "part_id" ) {
5319 				QString partTitle = attributes.value("value");
5320 				if ( !partTitle.isEmpty() )
5321 					partItem->setText(QMC2_SWLIST_COLUMN_TITLE, partItem->text(QMC2_SWLIST_COLUMN_TITLE) + " (" + partTitle + ")");
5322 				return true;
5323 			}
5324 			if ( featureName == "requirement" ) {
5325 				requirementItem = new SoftwareItem((QTreeWidget *)0);
5326 				requirementItem->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Requirement:") + " " + attributes.value("value"));
5327 				requirementItems << requirementItem;
5328 				return true;
5329 			}
5330 			if ( !compatItem && featureName == "compatibility" ) {
5331 				compatItem = new SoftwareItem((QTreeWidget *)0);
5332 				compatItem->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Compatibility:") + " " + attributes.value("value"));
5333 			}
5334 		}
5335 
5336 		return true;
5337 	}
5338 
5339 	if ( qName == "dataarea" ) {
5340 		if ( partItem != 0 ) {
5341 			dataareaItem = new SoftwareItem(partItem);
5342 			dataareaItem->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Data area:") + " " + attributes.value("name"));
5343 			QString s = attributes.value("size");
5344 			if ( !s.isEmpty() )
5345 				dataareaItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Size:") + " " + s);
5346 		}
5347 
5348 		return true;
5349 	}
5350 
5351 	if ( qName == "diskarea" ) {
5352 		if ( partItem != 0 ) {
5353 			dataareaItem = new SoftwareItem(partItem);
5354 			dataareaItem->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Disk area:") + " " + attributes.value("name"));
5355 			QString s = attributes.value("size");
5356 			if ( !s.isEmpty() )
5357 				dataareaItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Size:") + " " + s);
5358 		}
5359 
5360 		return true;
5361 	}
5362 
5363 	if ( qName == "rom" ) {
5364 		if ( dataareaItem != 0 ) {
5365 			QString romName = attributes.value("name");
5366 			if ( !romName.isEmpty() ) {
5367 				romItem = new SoftwareItem(dataareaItem);
5368 				romItem->setText(QMC2_SWLIST_COLUMN_TITLE, romName);
5369 				QString s = attributes.value("size");
5370 				if ( !s.isEmpty() )
5371 					romItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("Size:") + " " + s);
5372 				s = attributes.value("crc");
5373 				if ( !s.isEmpty() )
5374 					romItem->setText(QMC2_SWLIST_COLUMN_PUBLISHER, QObject::tr("CRC:") + " " + s);
5375 			}
5376 		}
5377 
5378 		return true;
5379 	}
5380 
5381 	if ( qName == "disk" ) {
5382 		if ( dataareaItem != 0 ) {
5383 			QString diskName = attributes.value("name");
5384 			if ( !diskName.isEmpty() ) {
5385 				romItem = new SoftwareItem(dataareaItem);
5386 				romItem->setText(QMC2_SWLIST_COLUMN_TITLE, diskName);
5387 				QString s = attributes.value("sha1");
5388 				if ( !s.isEmpty() )
5389 					romItem->setText(QMC2_SWLIST_COLUMN_NAME, QObject::tr("SHA1:") + " " + s);
5390 			}
5391 		}
5392 
5393 		return true;
5394 	}
5395 
5396 	if ( qName == "info" ) {
5397 		infoItem = new SoftwareItem((QTreeWidget *)0);
5398 		infoItem->setText(QMC2_SWLIST_COLUMN_TITLE, QObject::tr("Info:") + " " + attributes.value("name"));
5399 #if defined(QMC2_OS_WIN)
5400 		infoItem->setText(QMC2_SWLIST_COLUMN_NAME, QString::fromUtf8(attributes.value("value").toUtf8()));
5401 #else
5402 		infoItem->setText(QMC2_SWLIST_COLUMN_NAME, attributes.value("value"));
5403 #endif
5404 		infoItems << infoItem;
5405 	}
5406 
5407 	return true;
5408 }
5409 
endElement(const QString &,const QString &,const QString & qName)5410 bool SoftwareEntryXmlHandler::endElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &qName)
5411 {
5412 	if ( !softwareValid )
5413 		return true;
5414 
5415 	if ( qName == "software" ) {
5416 		// stop here...
5417 		parentTreeWidgetItem->treeWidget()->setUpdatesEnabled(false);
5418 		QTreeWidgetItem *childItem = parentTreeWidgetItem->takeChild(0);
5419 		delete childItem;
5420 		parentTreeWidgetItem->insertChildren(0, partItems);
5421 		for (int i = 0; i < partItems.count(); i++) {
5422 			QTreeWidgetItem *item = partItems[i];
5423 			QComboBox *cb = comboBoxes[item];
5424 			if ( cb )
5425 				parentTreeWidgetItem->treeWidget()->setItemWidget(item, QMC2_SWLIST_COLUMN_PUBLISHER, cb);
5426 		}
5427 		if ( !requirementItems.isEmpty() )
5428 			parentTreeWidgetItem->insertChildren(0, requirementItems);
5429 		if ( compatItem )
5430 			parentTreeWidgetItem->insertChild(0, compatItem);
5431 		if ( !infoItems.isEmpty() )
5432 			parentTreeWidgetItem->insertChildren(0, infoItems);
5433 		parentTreeWidgetItem->treeWidget()->setUpdatesEnabled(true);
5434 		parentTreeWidgetItem->treeWidget()->viewport()->update();
5435 		qApp->processEvents();
5436 		success = true;
5437 		return false;
5438 	}
5439 
5440 	if ( qName == "part" ) {
5441 		partItem = 0;
5442 		return true;
5443 	}
5444 
5445 	if ( qName == "dataarea" || qName == "diskarea" ) {
5446 		dataareaItem = 0;
5447 		return true;
5448 	}
5449 
5450 	if ( qName == "rom" ) {
5451 		romItem = 0;
5452 		return true;
5453 	}
5454 
5455 	return true;
5456 }
5457