1 #include "mainwindow.h"
2 #include "ui_mainwindow.h"
3 #include "sendinghandler.h"
4 #include "dockwidget.h"
5 #include "utils.h"
6
7 #include "../../core/schedule.h"
8 #include "../../core/cpu.h"
9 #include "../../core/emu.h"
10 #include "../../core/link.h"
11 #include "../../core/debug/debug.h"
12
13 #include <cmath>
14 #include <QtCore/QFileInfo>
15 #include <QtCore/QRegularExpression>
16 #include <QtNetwork/QNetworkAccessManager>
17 #include <QtWidgets/QDesktopWidget>
18 #include <QtWidgets/QApplication>
19 #include <QtWidgets/QMessageBox>
20 #include <QtWidgets/QInputDialog>
21 #include <QtWidgets/QScrollBar>
22 #include <QtNetwork/QNetworkReply>
23
24 #ifdef _MSC_VER
25 #include <direct.h>
26 #define chdir _chdir
27 #else
28 #include <unistd.h>
29 #endif
30
31 const QString MainWindow::SETTING_DEBUGGER_TEXT_SIZE = QStringLiteral("Debugger/text_size");
32 const QString MainWindow::SETTING_DEBUGGER_RESTORE_ON_OPEN = QStringLiteral("Debugger/restore_on_open");
33 const QString MainWindow::SETTING_DEBUGGER_SAVE_ON_CLOSE = QStringLiteral("Debugger/save_on_close");
34 const QString MainWindow::SETTING_DEBUGGER_RESET_OPENS = QStringLiteral("Debugger/open_on_reset");
35 const QString MainWindow::SETTING_DEBUGGER_ENABLE_SOFT = QStringLiteral("Debugger/enable_soft_commands");
36 const QString MainWindow::SETTING_DEBUGGER_BOLD_SYMBOLS = QStringLiteral("Debugger/disasm_bold_symbols");
37 const QString MainWindow::SETTING_DEBUGGER_DISASM_SPACE = QStringLiteral("Debugger/disasm_add_space");
38 const QString MainWindow::SETTING_DEBUGGER_ADDR_COL = QStringLiteral("Debugger/disasm_addr_column");
39 const QString MainWindow::SETTING_DEBUGGER_DATA_COL = QStringLiteral("Debugger/disasm_data_column");
40 const QString MainWindow::SETTING_DEBUGGER_IMPLICT = QStringLiteral("Debugger/disasm_implict");
41 const QString MainWindow::SETTING_DEBUGGER_UPPERCASE = QStringLiteral("Debugger/disasm_uppercase");
42 const QString MainWindow::SETTING_DEBUGGER_IMAGE_PATH = QStringLiteral("Debugger/image_path");
43 const QString MainWindow::SETTING_DEBUGGER_FLASH_BYTES = QStringLiteral("Debugger/flash_bytes_per_line");
44 const QString MainWindow::SETTING_DEBUGGER_RAM_BYTES = QStringLiteral("Debugger/ram_bytes_per_line");
45 const QString MainWindow::SETTING_DEBUGGER_FLASH_ASCII = QStringLiteral("Debugger/flash_ascii");
46 const QString MainWindow::SETTING_DEBUGGER_RAM_ASCII = QStringLiteral("Debugger/ram_ascii");
47 const QString MainWindow::SETTING_DEBUGGER_BREAK_IGNORE = QStringLiteral("Debugger/ignore_breakpoints");
48 const QString MainWindow::SETTING_DEBUGGER_AUTO_EQUATES = QStringLiteral("Debugger/auto_equates");
49 const QString MainWindow::SETTING_DEBUGGER_IGNORE_DMA = QStringLiteral("Debugger/ignore_dma");
50 const QString MainWindow::SETTING_DEBUGGER_NORM_OS = QStringLiteral("Debugger/norm_os");
51 const QString MainWindow::SETTING_DEBUGGER_PRE_I = QStringLiteral("Debugger/pre_i");
52 const QString MainWindow::SETTING_SCREEN_FRAMESKIP = QStringLiteral("Screen/frameskip");
53 const QString MainWindow::SETTING_SCREEN_SCALE = QStringLiteral("Screen/scale");
54 const QString MainWindow::SETTING_SCREEN_SKIN = QStringLiteral("Screen/skin");
55 const QString MainWindow::SETTING_SCREEN_SPI = QStringLiteral("Screen/spi");
56 const QString MainWindow::SETTING_KEYPAD_KEYMAP = QStringLiteral("Keypad/map");
57 const QString MainWindow::SETTING_KEYPAD_COLOR = QStringLiteral("Keypad/color");
58 const QString MainWindow::SETTING_KEYPAD_CUSTOM_PATH = QStringLiteral("Keypad/custom_path");
59 const QString MainWindow::SETTING_WINDOW_GROUP_DRAG = QStringLiteral("Window/group_dock_drag");
60 const QString MainWindow::SETTING_WINDOW_FULLSCREEN = QStringLiteral("Window/fullscreen");
61 const QString MainWindow::SETTING_WINDOW_STATE = QStringLiteral("Window/state");
62 const QString MainWindow::SETTING_WINDOW_GEOMETRY = QStringLiteral("Window/geometry");
63 const QString MainWindow::SETTING_WINDOW_SEPARATOR = QStringLiteral("Window/boundaries");
64 const QString MainWindow::SETTING_WINDOW_MENUBAR = QStringLiteral("Window/menubar");
65 const QString MainWindow::SETTING_WINDOW_STATUSBAR = QStringLiteral("Window/statusbar");
66 const QString MainWindow::SETTING_WINDOW_POSITION = QStringLiteral("Window/position");
67 const QString MainWindow::SETTING_WINDOW_MEMORY_DOCKS = QStringLiteral("Window/memory_docks");
68 const QString MainWindow::SETTING_WINDOW_MEMORY_DOCK_BYTES = QStringLiteral("Window/memory_docks_bytes");
69 const QString MainWindow::SETTING_WINDOW_MEMORY_DOCK_ASCII = QStringLiteral("Window/memory_docks_ascii");
70 const QString MainWindow::SETTING_WINDOW_VISUALIZER_DOCKS = QStringLiteral("Window/visualizer_docks");
71 const QString MainWindow::SETTING_WINDOW_VISUALIZER_CONFIG = QStringLiteral("Window/visualizer_config");
72 const QString MainWindow::SETTING_WINDOW_KEYHISTORY_DOCKS = QStringLiteral("Window/keyhistory_docks");
73 const QString MainWindow::SETTING_WINDOW_KEYHISTORY_CONFIG = QStringLiteral("Window/keyhistory_config");
74 const QString MainWindow::SETTING_CAPTURE_FRAMESKIP = QStringLiteral("Capture/frameskip");
75 const QString MainWindow::SETTING_CAPTURE_OPTIMIZE = QStringLiteral("Capture/optimize");
76 const QString MainWindow::SETTING_SLOT_NAMES = QStringLiteral("Slot/names");
77 const QString MainWindow::SETTING_SLOT_PATHS = QStringLiteral("Slot/paths");
78 const QString MainWindow::SETTING_IMAGE_PATH = QStringLiteral("image_path");
79 const QString MainWindow::SETTING_ROM_PATH = QStringLiteral("rom_path");
80 const QString MainWindow::SETTING_STATUS_INTERVAL = QStringLiteral("status_interval");
81 const QString MainWindow::SETTING_FIRST_RUN = QStringLiteral("first_run");
82 const QString MainWindow::SETTING_UI_EDIT_MODE = QStringLiteral("ui_edit_mode");
83 const QString MainWindow::SETTING_PAUSE_FOCUS = QStringLiteral("pause_on_focus_change");
84 const QString MainWindow::SETTING_SAVE_ON_CLOSE = QStringLiteral("save_on_close");
85 const QString MainWindow::SETTING_RESTORE_ON_OPEN = QStringLiteral("restore_on_open");
86 const QString MainWindow::SETTING_EMUSPEED = QStringLiteral("emulated_speed");
87 const QString MainWindow::SETTING_AUTOUPDATE = QStringLiteral("check_for_updates");
88 const QString MainWindow::SETTING_ALWAYS_ON_TOP = QStringLiteral("always_on_top");
89 const QString MainWindow::SETTING_NATIVE_CONSOLE = QStringLiteral("native_console");
90 const QString MainWindow::SETTING_CURRENT_DIR = QStringLiteral("current_directory");
91 const QString MainWindow::SETTING_ENABLE_WIN_CONSOLE = QStringLiteral("enable_windows_console");
92 const QString MainWindow::SETTING_RECENT_SAVE = QStringLiteral("Recent/save_paths");
93 const QString MainWindow::SETTING_RECENT_PATHS = QStringLiteral("Recent/paths");
94 const QString MainWindow::SETTING_RECENT_SELECT = QStringLiteral("Recent/selected");
95
96 const QString MainWindow::SETTING_KEYPAD_NATURAL = QStringLiteral("natural");
97 const QString MainWindow::SETTING_KEYPAD_CEMU = QStringLiteral("cemu");
98 const QString MainWindow::SETTING_KEYPAD_TILEM = QStringLiteral("tilem");
99 const QString MainWindow::SETTING_KEYPAD_WABBITEMU = QStringLiteral("wabbitemu");
100 const QString MainWindow::SETTING_KEYPAD_JSTIFIED = QStringLiteral("jsTIfied");
101 const QString MainWindow::SETTING_KEYPAD_CUSTOM = QStringLiteral("custom");
102
103 const QString MainWindow::SETTING_PREFERRED_LANG = QStringLiteral("preferred_lang");
104 const QString MainWindow::SETTING_VERSION = QStringLiteral("version");
105
106 const QString MainWindow::SETTING_DEFAULT_CONFIG_FILE = QStringLiteral("/cemu_config.ini");
107 const QString MainWindow::SETTING_DEFAULT_ROM_FILE = QStringLiteral("/cemu_rom.rom");
108 const QString MainWindow::SETTING_DEFAULT_DEBUG_FILE = QStringLiteral("/cemu_debug.ini");
109 const QString MainWindow::SETTING_DEFAULT_IMAGE_FILE = QStringLiteral("/cemu_image.ce");
110 const QString MainWindow::TXT_YES = QStringLiteral("y");
111 const QString MainWindow::TXT_NO = QStringLiteral("n");
112 const QString MainWindow::TXT_NAN = QStringLiteral("NaN");
113
114 // In all cases, all paths in memory should be absolute paths.
115 // But in the UI or config file, they can be relative (to appDir()) if portable mode is enabled, or absolute otherwise.
setPortable(bool state)116 void MainWindow::setPortable(bool state) {
117 ui->checkPortable->blockSignals(true);
118 ui->checkPortable->setChecked(state);
119 ui->checkPortable->blockSignals(false);
120 m_portable = state;
121
122 delete m_config;
123 m_config = Q_NULLPTR;
124
125 const QDir dir = appDir();
126
127 // Get all new paths, in absolute
128 const QString newConfigPath = QFileInfo((state ? dir.path() : configPath) + SETTING_DEFAULT_CONFIG_FILE).absoluteFilePath();
129 const QString newConfigDirPath = QDir::cleanPath(QFileInfo(newConfigPath).absolutePath());
130 QString newDebugPath = newConfigDirPath + SETTING_DEFAULT_DEBUG_FILE;
131 QString newImagePath = newConfigDirPath + SETTING_DEFAULT_IMAGE_FILE;
132 QString newRomPath = m_pathRom; // No change here
133
134 // Update the FS (still using absolute paths)
135 QFile(m_pathConfig).copy(newConfigPath);
136 QFile(m_pathImage).copy(newImagePath);
137
138 // Remove old settings if previously portable
139 if (!state) {
140 QFile(m_pathConfig).remove();
141 QFile(m_pathImage).remove();
142 }
143
144 // Update paths in memory (still using absolute paths)
145 m_pathConfig = newConfigPath;
146 m_pathImage = newImagePath;
147
148 // Now changing the UI and qsettings content, with, if portable, paths relative to appDir()
149 if (state) {
150 newDebugPath = dir.relativeFilePath(newDebugPath);
151 newImagePath = dir.relativeFilePath(newImagePath);
152 newRomPath = dir.relativeFilePath(newRomPath);
153 }
154
155 // Update new QSettings (memory + FS)
156 m_config = new QSettings(newConfigPath, QSettings::IniFormat); // Path is absolute
157 m_config->setValue(SETTING_DEBUGGER_IMAGE_PATH, newDebugPath);
158 m_config->setValue(SETTING_IMAGE_PATH, newImagePath);
159 m_config->setValue(SETTING_ROM_PATH, newRomPath);
160 m_config->sync();
161
162 ui->pathDebug->setText(newDebugPath);
163 ui->pathImage->setText(newImagePath);
164 ui->pathRom->setText(newRomPath);
165 ui->pathConfig->setText(newConfigPath);
166
167 ui->buttonChangeSavedDebugPath->setEnabled(!state);
168 ui->buttonChangeSavedImagePath->setEnabled(!state);
169 }
170
setFrameskip(int value)171 void MainWindow::setFrameskip(int value) {
172 m_config->setValue(SETTING_CAPTURE_FRAMESKIP, value);
173 ui->apngSkip->setValue(value);
174 ui->apngSkipDisplay->setText(QString::number((ui->guiSkip->value() + 1) * (ui->apngSkip->value() + 1) - 1));
175 }
176
setOptimizeRecord(bool state)177 void MainWindow::setOptimizeRecord(bool state) {
178 ui->checkOptimizeRecording->setChecked(state);
179 m_config->setValue(SETTING_CAPTURE_OPTIMIZE, state);
180 m_optimizeRecording = state;
181 }
182
iconsLoad()183 void MainWindow::iconsLoad() {
184 QString iconPath = QStringLiteral(":/icons/resources/icons/");
185 m_iconStop.addPixmap(QPixmap(iconPath + QStringLiteral("stop.png")));
186 m_iconRun.addPixmap(QPixmap(iconPath + QStringLiteral("run.png")));
187 m_iconSave.addPixmap(QPixmap(iconPath + QStringLiteral("import.png")));
188 m_iconLoad.addPixmap(QPixmap(iconPath + QStringLiteral("export.png")));
189 m_iconEdit.addPixmap(QPixmap(iconPath + QStringLiteral("wizard.png")));
190 m_iconRemove.addPixmap(QPixmap(iconPath + QStringLiteral("exit.png")));
191 m_iconSearch.addPixmap(QPixmap(iconPath + QStringLiteral("search.png")));
192 m_iconGoto.addPixmap(QPixmap(iconPath + QStringLiteral("goto.png")));
193 m_iconSync.addPixmap(QPixmap(iconPath + QStringLiteral("refresh.png")));
194 m_iconAddMem.addPixmap(QPixmap(iconPath + QStringLiteral("add_mem.png")));
195 m_iconLcd.addPixmap(QPixmap(iconPath + QStringLiteral("lcd.png")));
196 m_iconUiEdit.addPixmap(QPixmap(iconPath + QStringLiteral("ui_edit.png")));
197 m_iconAscii.addPixmap(QPixmap(iconPath + QStringLiteral("characters.png")));
198 m_iconCheck.addPixmap(QPixmap(iconPath + QStringLiteral("check.png")));
199 m_iconCheckGray.addPixmap(QPixmap(iconPath + QStringLiteral("checkgray.png")));
200 m_actionAddMemory->setIcon(m_iconAddMem);
201 m_actionAddVisualizer->setIcon(m_iconLcd);
202 m_actionToggleUI->setIcon(m_iconUiEdit);
203 }
204
bootImageCheck()205 bool MainWindow::bootImageCheck() {
206 QDir dir = appDir();
207 QDirIterator dirIt(dir, QDirIterator::NoIteratorFlags);
208 while (dirIt.hasNext()) {
209 dirIt.next();
210 QString dirItFile = dirIt.filePath();
211 if (QFileInfo(dirItFile).isFile()) {
212 if (QFileInfo(dirItFile).suffix() == QStringLiteral("cemu")) {
213 if (!m_loadedBootImage) {
214 m_loadedBootImage = bootImageImport(dirItFile);
215 }
216 QFile(dirItFile).remove();
217 }
218 }
219 }
220 return m_loadedBootImage;
221 }
222
bootImageImport(const QString & bootImagePath)223 bool MainWindow::bootImageImport(const QString &bootImagePath) {
224 QString newConfigPath = configPath + SETTING_DEFAULT_CONFIG_FILE;
225 QString romPath = configPath + SETTING_DEFAULT_ROM_FILE;
226 QFile bootFile(bootImagePath);
227 QFile romFile(romPath);
228 romFile.remove();
229 if (!romFile.open(QIODevice::WriteOnly)) { return false; }
230 QFile settingsFile(newConfigPath);
231 settingsFile.remove();
232 if (!settingsFile.open(QIODevice::WriteOnly)) { return false; }
233 bootFile.open(QIODevice::ReadOnly);
234 QByteArray romData = bootFile.read(0x400000);
235 QByteArray settingsData = bootFile.readAll();
236 romFile.write(romData);
237 settingsFile.write(settingsData);
238 romFile.close();
239 settingsFile.close();
240 bootFile.close();
241 m_pathConfig = newConfigPath;
242 return true;
243 }
244
bootImageExport()245 void MainWindow::bootImageExport() {
246 QMessageBox::information(this, MSG_INFORMATION, tr("A bootable image can be used to start CEmu with predefined configurations, without the need for any extra setup."
247 "\n\nThe bootable image should be placed in the same directory as the CEmu executable. When CEmu is then started, "
248 "the boot image will be loaded automatically and then removed for convience."));
249
250 QString path = QFileDialog::getSaveFileName(this, tr("Save bootable CEmu image"),
251 m_dir.absolutePath(),
252 tr("Bootable CEmu images (*.cemu);"));
253
254 if (!path.isEmpty()) {
255 m_dir = QFileInfo(path).absoluteDir();
256 QFile romFile(m_pathRom);
257 if (!romFile.open(QIODevice::ReadOnly)) {
258 return;
259 }
260 QByteArray romData = romFile.readAll();
261
262 QFile settingsFile(m_pathConfig);
263 if (!settingsFile.open(QIODevice::ReadOnly)) {
264 return;
265 }
266 QByteArray settingsData = settingsFile.readAll();
267
268 QFile writter(path);
269 writter.open(QIODevice::WriteOnly);
270 writter.write(romData);
271 writter.write(settingsData);
272 romFile.close();
273 settingsFile.close();
274 writter.close();
275 }
276 }
277
setDebugSoftCommands(bool state)278 void MainWindow::setDebugSoftCommands(bool state) {
279 ui->checkDisableSoftCommands->blockSignals(true);
280 ui->checkDisableSoftCommands->setChecked(state);
281 ui->checkDisableSoftCommands->blockSignals(false);
282 m_config->setValue(SETTING_DEBUGGER_ENABLE_SOFT, state);
283 debug_flag(DBG_SOFT_COMMANDS, state);
284 }
285
setDebugDisasmBoldSymbols(bool state)286 void MainWindow::setDebugDisasmBoldSymbols(bool state) {
287 ui->checkDisasmBoldSymbols->setChecked(state);
288 m_config->setValue(SETTING_DEBUGGER_BOLD_SYMBOLS, state);
289 disasm.bold_sym = state;
290 if (guiDebug) {
291 disasmUpdate();
292 }
293 }
294
setDockGroupDrag(bool state)295 void MainWindow::setDockGroupDrag(bool state) {
296 ui->checkAllowGroupDrag->setChecked(state);
297 m_config->setValue(SETTING_WINDOW_GROUP_DRAG, state);
298 setUIDockEditMode(m_uiEditMode);
299 }
300
setDebugDisasmDataCol(bool state)301 void MainWindow::setDebugDisasmDataCol(bool state) {
302 ui->checkDisasmDataCol->setChecked(state);
303 m_config->setValue(SETTING_DEBUGGER_DATA_COL, state);
304 disasm.bytes = state;
305 if (guiDebug) {
306 disasmUpdate();
307 }
308 }
309
setDebugDisasmAddrCol(bool state)310 void MainWindow::setDebugDisasmAddrCol(bool state) {
311 ui->checkDisasmAddr->setChecked(state);
312 m_config->setValue(SETTING_DEBUGGER_ADDR_COL, state);
313 disasm.addr = state;
314 if (guiDebug) {
315 if (!disasm.addr) {
316 disasmUpdate();
317 } else {
318 disasmUpdateAddr(cpu.registers.PC, true);
319 }
320 }
321 }
322
setDebugDisasmImplict(bool state)323 void MainWindow::setDebugDisasmImplict(bool state) {
324 ui->checkDisasmImplict->setChecked(state);
325 m_config->setValue(SETTING_DEBUGGER_IMPLICT, state);
326 disasm.implicit = state;
327 if (guiDebug) {
328 disasmUpdate();
329 }
330 }
331
setDebugDisasmUppercase(bool state)332 void MainWindow::setDebugDisasmUppercase(bool state) {
333 ui->checkDisasmUppercase->setChecked(state);
334 m_config->setValue(SETTING_DEBUGGER_UPPERCASE, state);
335 disasm.uppercase = state;
336 if (guiDebug) {
337 disasmUpdate();
338 }
339 }
340
setDebugDisasmSpace(bool state)341 void MainWindow::setDebugDisasmSpace(bool state) {
342 ui->checkAddSpace->setChecked(state);
343 m_config->setValue(SETTING_DEBUGGER_DISASM_SPACE, state);
344 disasm.comma = state ? ", " : ",";
345 if (guiDebug) {
346 disasmUpdate();
347 }
348 }
349
setLcdSpi(bool state)350 void MainWindow::setLcdSpi(bool state) {
351 ui->checkSpi->setChecked(state);
352 m_config->setValue(SETTING_SCREEN_SPI, state);
353 emu_set_lcd_spi(state == false ? 0 : 1);
354 emit setLcdMode(state);
355 }
356
setLcdDma(bool state)357 void MainWindow::setLcdDma(bool state) {
358 ui->checkDma->setChecked(state);
359 m_config->setValue(SETTING_DEBUGGER_IGNORE_DMA, !state);
360 bool ok;
361 int64_t cycleCounter = ui->cycleView->text().toLongLong(&ok);
362 if (!ok)
363 cycleCounter = debug.totalCycles - (!state ? debug.dmaCycles : 0);
364 else if (!state)
365 cycleCounter -= debug.dmaCycles;
366 else
367 cycleCounter += debug.dmaCycles;
368 ui->cycleView->setText(QString::number(cycleCounter));
369 m_ignoreDmaCycles = !state;
370 }
371
setFocusSetting(bool state)372 void MainWindow::setFocusSetting(bool state) {
373 ui->checkFocus->setChecked(state);
374 m_config->setValue(SETTING_PAUSE_FOCUS, state);
375 m_pauseOnFocus = state;
376 }
377
memLoadState()378 void MainWindow::memLoadState() {
379 ui->ramBytes->setValue(m_config->value(SETTING_DEBUGGER_RAM_BYTES, 8).toInt());
380 ui->flashBytes->setValue(m_config->value(SETTING_DEBUGGER_FLASH_BYTES, 8).toInt());
381 ui->ramAscii->setChecked(m_config->value(SETTING_DEBUGGER_RAM_ASCII, true).toBool());
382 ui->flashAscii->setChecked(m_config->value(SETTING_DEBUGGER_FLASH_ASCII, true).toBool());
383 ui->ramEdit->setAsciiArea(ui->ramAscii->isChecked());
384 ui->flashEdit->setAsciiArea(ui->flashAscii->isChecked());
385 }
386
setMenuBarState(bool state)387 void MainWindow::setMenuBarState(bool state) {
388 ui->menubar->setHidden(state);
389 ui->actionHideMenuBar->setChecked(state);
390 m_config->setValue(SETTING_WINDOW_MENUBAR, state);
391 }
392
setStatusBarState(bool state)393 void MainWindow::setStatusBarState(bool state) {
394 ui->statusBar->setHidden(state);
395 ui->actionHideStatusBar->setChecked(state);
396 m_config->setValue(SETTING_WINDOW_STATUSBAR, state);
397 }
398
setDebugIgnoreBreakpoints(bool state)399 void MainWindow::setDebugIgnoreBreakpoints(bool state) {
400 ui->buttonToggleBreakpoints->setChecked(state);
401 m_config->setValue(SETTING_DEBUGGER_BREAK_IGNORE, state);
402 debug_flag(DBG_IGNORE, state);
403 }
404
setDebugResetTrigger(bool state)405 void MainWindow::setDebugResetTrigger(bool state) {
406 ui->checkDebugResetTrigger->setChecked(state);
407 m_config->setValue(SETTING_DEBUGGER_RESET_OPENS, state);
408 debug_flag(DBG_OPEN_ON_RESET, state);
409 }
410
setAutoSave(bool state)411 void MainWindow::setAutoSave(bool state) {
412 ui->checkSaveRestore->setChecked(state);
413 m_config->setValue(SETTING_RESTORE_ON_OPEN, state);
414 m_config->setValue(SETTING_SAVE_ON_CLOSE, state);
415 }
416
setDebugAutoSave(bool state)417 void MainWindow::setDebugAutoSave(bool state) {
418 ui->checkSaveLoadDebug->setChecked(state);
419 m_config->setValue(SETTING_DEBUGGER_SAVE_ON_CLOSE, state);
420 m_config->setValue(SETTING_DEBUGGER_RESTORE_ON_OPEN, state);
421 }
422
setFont(int fontSize)423 void MainWindow::setFont(int fontSize) {
424 ui->textSize->setValue(fontSize);
425 m_config->setValue(SETTING_DEBUGGER_TEXT_SIZE, ui->textSize->value());
426
427 QFont monospace = QFontDatabase::systemFont(QFontDatabase::FixedFont);
428
429 monospace.setPointSize(fontSize);
430 ui->console->setFont(monospace);
431 m_disasm->setFont(monospace);
432
433 ui->stackView->setFont(monospace);
434
435 ui->afregView->setFont(monospace);
436 ui->hlregView->setFont(monospace);
437 ui->deregView->setFont(monospace);
438 ui->bcregView->setFont(monospace);
439 ui->ixregView->setFont(monospace);
440 ui->iyregView->setFont(monospace);
441 ui->af_regView->setFont(monospace);
442 ui->hl_regView->setFont(monospace);
443 ui->de_regView->setFont(monospace);
444 ui->bc_regView->setFont(monospace);
445 ui->splregView->setFont(monospace);
446 ui->spsregView->setFont(monospace);
447 ui->mbregView->setFont(monospace);
448 ui->iregView->setFont(monospace);
449 ui->rregView->setFont(monospace);
450 ui->imregView->setFont(monospace);
451 ui->freqView->setFont(monospace);
452 ui->pcregView->setFont(monospace);
453 ui->lcdbaseView->setFont(monospace);
454 ui->lcdcurrView->setFont(monospace);
455 ui->cycleView->setFont(monospace);
456 }
457
setKeypadColor(unsigned int color)458 void MainWindow::setKeypadColor(unsigned int color) {
459 ui->keypadWidget->setType(get_device_type(), color);
460 m_config->setValue(SETTING_KEYPAD_COLOR, color);
461 }
462
setCalcSkinTopFromType()463 void MainWindow::setCalcSkinTopFromType() {
464 bool is83 = get_device_type() == TI83PCE;
465 ui->calcSkinTop->setStyleSheet(is83 ? QStringLiteral(".QFrame { border-image: url(:/skin/resources/skin/ti83pce.png) 0 0 0 0 stretch stretch; }")
466 : QStringLiteral(".QFrame { border-image: url(:/skin/resources/skin/ti84pce.png) 0 0 0 0 stretch stretch; }"));
467 }
468
setImagePath()469 void MainWindow::setImagePath() {
470 QString saveImagePath = QFileDialog::getSaveFileName(this, tr("Set saved image to restore from"),
471 m_dir.absolutePath(),
472 tr("CEmu images (*.ce);;All files (*.*)"));
473 if (!saveImagePath.isEmpty()) {
474 m_dir = QFileInfo(saveImagePath).absoluteDir();
475 m_config->setValue(SETTING_IMAGE_PATH, saveImagePath);
476 ui->pathImage->setText(saveImagePath);
477 }
478 }
479
setDebugPath()480 void MainWindow::setDebugPath() {
481 QString savePath = QFileDialog::getSaveFileName(this, tr("Set debugging information path"),
482 m_dir.absolutePath(),
483 tr("Debugging information (*.ini);;All files (*.*)"));
484 if (!savePath.isEmpty()) {
485 m_dir = QFileInfo(savePath).absoluteDir();
486 m_config->setValue(SETTING_DEBUGGER_IMAGE_PATH, savePath);
487 ui->pathDebug->setText(savePath);
488 }
489 }
490
setUIDocks()491 void MainWindow::setUIDocks() {
492
493 // Create "Docks" menu to make closing and opening docks more intuitive
494 m_menuDocks = new QMenu(TITLE_DOCKS, this);
495 ui->menubar->insertMenu(ui->menuExtras->menuAction(), m_menuDocks);
496
497 // Convert any current docks
498 for (const auto &dock : findChildren<DockWidget*>()) {
499 dock->setState(m_uiEditMode);
500 }
501
502 // Add the screen action
503 QAction *action = ui->screenDock->toggleViewAction();
504 action->setIcon(m_iconLcd);
505
506 // Convert the tabs into DockWidgets
507 while (ui->tabWidget->count()) {
508 DockWidget *dw = new DockWidget(ui->tabWidget, this);
509
510 // Fill "Docks" menu
511 QAction *action = dw->toggleViewAction();
512 action->setIcon(dw->windowIcon());
513 m_menuDocks->addAction(action);
514
515 dw->setState(m_uiEditMode);
516 addDockWidget(Qt::RightDockWidgetArea, dw);
517 if (!m_dockPtrs.isEmpty()) {
518 tabifyDockWidget(m_dockPtrs.back(), dw);
519 }
520
521 m_dockPtrs.append(dw);
522 }
523
524 m_menuDocks->addAction(action);
525 m_menuDocks->addSeparator();
526
527 m_menuDebug = new QMenu(TITLE_DEBUG, this);
528 ui->menubar->insertMenu(ui->menuExtras->menuAction(), m_menuDebug);
529
530 // Convert the tabs into QDockWidgets
531 while (ui->tabDebug->count()) {
532 DockWidget *dw = new DockWidget(ui->tabDebug, this);
533
534 // Fill "Docks" menu
535 QAction *action = dw->toggleViewAction();
536 action->setIcon(dw->windowIcon());
537 m_menuDebug->addAction(action);
538
539 dw->setState(m_uiEditMode);
540 addDockWidget(Qt::RightDockWidgetArea, dw);
541 if (isFirstRun() || !opts.useSettings) {
542 dw->hide();
543 dw->close();
544 }
545 }
546
547 // Load removable docks
548 setMemDocks();
549 setVisualizerDocks();
550 setKeyHistoryDocks();
551
552 m_menuDebug->addSeparator();
553 m_menuDebug->addAction(m_actionAddMemory);
554 m_menuDebug->addAction(m_actionAddVisualizer);
555
556 m_menuDocks->addSeparator();
557 m_menuDocks->addAction(m_actionToggleUI);
558
559 ui->tabWidget->close();
560 ui->tabDebug->close();
561
562 m_dockPtrs.append(ui->screenDock);
563
564 // hide all the docks
565 for (const auto &dock : findChildren<DockWidget*>()) {
566 dock->setVisible(false);
567 }
568 }
569
setUIDockEditMode(bool mode)570 void MainWindow::setUIDockEditMode(bool mode) {
571 if (mode) {
572 if (m_config->value(SETTING_WINDOW_GROUP_DRAG).toBool()) {
573 setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks
574 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
575 | QMainWindow::GroupedDragging
576 #endif
577 );
578 } else {
579 setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
580 }
581 } else {
582 setDockOptions(0);
583 }
584 for (const auto &dock : findChildren<DockWidget*>()) {
585 dock->setState(mode);
586 }
587 }
588
setUIEditMode(bool mode)589 void MainWindow::setUIEditMode(bool mode) {
590 m_uiEditMode = mode;
591 m_config->setValue(SETTING_UI_EDIT_MODE, mode);
592 m_actionToggleUI->setChecked(mode);
593 m_actionAddMemory->setEnabled(mode);
594 m_actionAddVisualizer->setEnabled(mode);
595 setUIBoundaries(mode);
596 setUIDockEditMode(mode);
597 }
598
setThrottle(int mode)599 void MainWindow::setThrottle(int mode) {
600 ui->checkThrottle->setChecked(mode == Qt::Checked);
601 emu.setThrottle(mode == Qt::Checked);
602 }
603
setAutoUpdates(int state)604 void MainWindow::setAutoUpdates(int state) {
605 m_config->setValue(SETTING_AUTOUPDATE, state);
606 ui->checkUpdates->setChecked(state);
607
608 if (state == Qt::Checked) {
609 checkUpdate(false);
610 }
611 }
612
checkUpdate(bool forceInfoBox)613 void MainWindow::checkUpdate(bool forceInfoBox) {
614 if (!CEMU_RELEASE) {
615 if (forceInfoBox) {
616 QMessageBox::warning(this, MSG_WARNING, tr("Checking updates is disabled for development builds"));
617 }
618 return;
619 }
620
621 static const QString currentVersionReleaseURL = QStringLiteral("https://github.com/CE-Programming/CEmu/releases/tag/" CEMU_VERSION);
622 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
623 connect(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply* reply) {
624 QString newVersionURL = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
625 if (!newVersionURL.isEmpty()) {
626 if (newVersionURL.compare(currentVersionReleaseURL) == 0) {
627 if (forceInfoBox) {
628 QMessageBox::information(this, tr("No update available"), tr("You already have the latest CEmu version") + QStringLiteral(" (" CEMU_VERSION ")"));
629 }
630 } else {
631 QMessageBox updateInfoBox(this);
632 updateInfoBox.addButton(QMessageBox::Ok);
633 updateInfoBox.setIconPixmap(QPixmap(QStringLiteral(":/icons/resources/icons/icon.png")));
634 updateInfoBox.setWindowTitle(tr("CEmu update"));
635 updateInfoBox.setText(tr("<b>A new version of CEmu is available!</b>"
636 "<br/>"
637 "You can <a href='%1'>download it here</a>.")
638 .arg(newVersionURL));
639 updateInfoBox.setTextFormat(Qt::RichText);
640 updateInfoBox.show();
641 updateInfoBox.exec();
642 }
643 } else { // No internet connection? GitHub doesn't provide this redirection anymore?
644 if (forceInfoBox) {
645 QMessageBox updateInfoBox(this);
646 updateInfoBox.addButton(QMessageBox::Ok);
647 updateInfoBox.setIcon(QMessageBox::Warning);
648 updateInfoBox.setWindowTitle(tr("Update check failed"));
649 updateInfoBox.setText(tr("<b>An error occurred while checking for CEmu updates.</b>"
650 "<br/>"
651 "You can however <a href='https://github.com/CE-Programming/CEmu/releases/latest'>go here</a> to check yourself."));
652 updateInfoBox.setTextFormat(Qt::RichText);
653 updateInfoBox.show();
654 updateInfoBox.exec();
655 }
656 }
657 });
658
659 manager->get(QNetworkRequest(QUrl(QStringLiteral("https://github.com/CE-Programming/CEmu/releases/latest"))));
660 }
661
lcdAdjust()662 void MainWindow::lcdAdjust() {
663 float scale = ui->scaleLCD->value() / 100.0;
664 bool skin = ui->checkSkin->isChecked();
665 ui->calcSkinTop->setVisible(skin);
666 float w, h;
667 w = LCD_WIDTH * scale;
668 h = LCD_HEIGHT * scale;
669 ui->lcd->setFixedSize(w, h);
670 ui->lcd->move(skin ? 60 * scale : 0, skin ? 78 * scale : 0);
671 if (skin) {
672 w = 440 * scale;
673 h = 351 * scale;
674 }
675 ui->calcSkinTop->resize(w, h);
676 ui->screenWidget->setFixedSize(w, h);
677 }
678
setLcdScale(int scale)679 void MainWindow::setLcdScale(int scale) {
680 int roundedScale = round(scale / 10.0) * 10;
681 m_config->setValue(SETTING_SCREEN_SCALE, roundedScale);
682 ui->scaleLCD->setValue(roundedScale);
683 lcdAdjust();
684 }
685
setSkinToggle(bool enable)686 void MainWindow::setSkinToggle(bool enable) {
687 m_config->setValue(SETTING_SCREEN_SKIN, enable);
688 ui->checkSkin->setChecked(enable);
689 lcdAdjust();
690 }
691
setDebugAutoEquates(bool enable)692 void MainWindow::setDebugAutoEquates(bool enable) {
693 m_config->setValue(SETTING_DEBUGGER_AUTO_EQUATES, enable);
694 ui->checkAutoEquates->setChecked(enable);
695 sendingHandler->setLoadEquates(enable);
696 }
697
setGuiSkip(int value)698 void MainWindow::setGuiSkip(int value) {
699 m_config->setValue(SETTING_SCREEN_FRAMESKIP, value);
700 ui->guiSkip->blockSignals(true);
701 ui->guiSkip->setValue(value);
702 ui->guiSkip->blockSignals(false);
703 ui->apngSkipDisplay->setText(QString::number((ui->guiSkip->value() + 1) * (ui->apngSkip->value() + 1) - 1));
704 emit setLcdFrameskip(value);
705 }
706
setStatusInterval(int value)707 void MainWindow::setStatusInterval(int value) {
708 m_config->setValue(SETTING_STATUS_INTERVAL, value);
709 ui->statusInterval->setValue(value);
710 m_timerFps.stop();
711 m_timerEmu.stop();
712 m_timerFpsTriggered = true;
713 m_timerEmuTriggered = true;
714 m_timerEmuTriggerable = true;
715 m_timerFpsTriggerable = true;
716 if (!value) {
717 m_timerEmuTriggerable = false;
718 m_timerFpsTriggerable = false;
719 } else {
720 m_timerFps.start(value * 1000);
721 m_timerEmu.start(value * 1000);
722 }
723 }
724
setEmuSpeed(int value)725 void MainWindow::setEmuSpeed(int value) {
726 m_config->setValue(SETTING_EMUSPEED, value);
727 ui->emulationSpeedSpin->blockSignals(true);
728 ui->emulationSpeedSpin->setValue(value);
729 ui->emulationSpeedSpin->blockSignals(false);
730 ui->emulationSpeed->setValue(value);
731 emu.setSpeed(value);
732 if (value == 0) {
733 m_timerEmuTriggered = true;
734 showEmuSpeed(0);
735 }
736 }
737
keypadChanged()738 void MainWindow::keypadChanged() {
739 QString name = sender()->objectName();
740 unsigned int color = KEYPAD_BLACK;
741
742 if (name == QStringLiteral("buttonWhite")) color = KEYPAD_WHITE;
743 else if (name == QStringLiteral("buttonBlack")) color = KEYPAD_BLACK;
744 else if (name == QStringLiteral("buttonGolden")) color = KEYPAD_GOLDEN;
745 else if (name == QStringLiteral("buttonPlum")) color = KEYPAD_PLUM;
746 else if (name == QStringLiteral("buttonPink")) color = KEYPAD_PINK;
747 else if (name == QStringLiteral("buttonRed")) color = KEYPAD_RED;
748 else if (name == QStringLiteral("buttonLightning")) color = KEYPAD_LIGHTNING;
749 else if (name == QStringLiteral("buttonTrueBlue")) color = KEYPAD_TRUE_BLUE;
750 else if (name == QStringLiteral("buttonDenim")) color = KEYPAD_DENIM;
751 else if (name == QStringLiteral("buttonSilver")) color = KEYPAD_SILVER;
752 else if (name == QStringLiteral("buttonSpaceGrey")) color = KEYPAD_SPACEGREY;
753 else if (name == QStringLiteral("buttonCoral")) color = KEYPAD_CORAL;
754 else if (name == QStringLiteral("buttonMint")) color = KEYPAD_MINT;
755 else if (name == QStringLiteral("buttonRoseGold")) color = KEYPAD_ROSEGOLD;
756 else if (name == QStringLiteral("buttonCrystalClear")) color = KEYPAD_CRYSTALCLEAR;
757
758 setKeypadColor(color);
759 }
760
keymapCustomSelected()761 void MainWindow::keymapCustomSelected() {
762 QFileDialog dialog(this);
763
764 m_config->remove(SETTING_KEYPAD_CUSTOM_PATH);
765
766 dialog.setDirectory(m_dir);
767 dialog.setFileMode(QFileDialog::ExistingFile);
768 dialog.setNameFilter(tr("Keymap Config (*.ini);;All files (*.*)"));
769
770 if (!dialog.exec()) {
771 ui->radioCEmuKeys->setChecked(true);
772 return;
773 }
774
775 m_dir = dialog.directory().absolutePath();
776 const auto& selectedFiles = dialog.selectedFiles();
777 if (selectedFiles.empty()) {
778 ui->radioCEmuKeys->setChecked(true);
779 return;
780 }
781
782 m_config->setValue(SETTING_KEYPAD_CUSTOM_PATH, selectedFiles.first());
783 setKeymap(SETTING_KEYPAD_CUSTOM);
784 }
785
keymapChanged()786 void MainWindow::keymapChanged() {
787 if (ui->radioNaturalKeys->isChecked()) {
788 setKeymap(SETTING_KEYPAD_NATURAL);
789 } else if (ui->radioCEmuKeys->isChecked()) {
790 setKeymap(SETTING_KEYPAD_CEMU);
791 } else if (ui->radioTilEmKeys->isChecked()) {
792 setKeymap(SETTING_KEYPAD_TILEM);
793 } else if (ui->radioWabbitemuKeys->isChecked()) {
794 setKeymap(SETTING_KEYPAD_WABBITEMU);
795 } else if (ui->radiojsTIfiedKeys->isChecked()) {
796 setKeymap(SETTING_KEYPAD_JSTIFIED);
797 } else if (ui->radioCustomKeys->isChecked()) {
798 setKeymap(SETTING_KEYPAD_CUSTOM);
799 }
800 }
801
setKeymap(const QString & value)802 void MainWindow::setKeymap(const QString &value) {
803 QtKeypadBridge::KeymapMode mode;
804 QString map = value;
805 QString customPath = m_config->value(SETTING_KEYPAD_CUSTOM_PATH, QString()).toString();
806 if (!SETTING_KEYPAD_CUSTOM.compare(map, Qt::CaseInsensitive)) {
807 if (customPath.isEmpty() || !fileExists(customPath)) {
808 map = SETTING_KEYPAD_CEMU;
809 } else if (!keypadBridge->keymapImport(customPath)) {
810 QMessageBox::warning(this, MSG_WARNING, tr("Unable to set custom keymap."));
811 map = SETTING_KEYPAD_CEMU;
812 }
813 }
814 if (!SETTING_KEYPAD_NATURAL.compare(map, Qt::CaseInsensitive)) {
815 mode = QtKeypadBridge::KEYMAP_NATURAL;
816 } else if (!SETTING_KEYPAD_CEMU.compare(map, Qt::CaseInsensitive)) {
817 mode = QtKeypadBridge::KEYMAP_CEMU;
818 } else if (!SETTING_KEYPAD_TILEM.compare(map, Qt::CaseInsensitive)) {
819 mode = QtKeypadBridge::KEYMAP_TILEM;
820 } else if (!SETTING_KEYPAD_WABBITEMU.compare(map, Qt::CaseInsensitive)) {
821 mode = QtKeypadBridge::KEYMAP_WABBITEMU;
822 } else if (!SETTING_KEYPAD_JSTIFIED.compare(map, Qt::CaseInsensitive)) {
823 mode = QtKeypadBridge::KEYMAP_JSTIFIED;
824 } else {
825 mode = QtKeypadBridge::KEYMAP_CUSTOM;
826 }
827 m_config->setValue(SETTING_KEYPAD_KEYMAP, map);
828 keypadBridge->setKeymap(mode);
829 if (map == SETTING_KEYPAD_CEMU) {
830 ui->radioCEmuKeys->setChecked(true);
831 }
832 }
833
keymapLoad()834 void MainWindow::keymapLoad() {
835 QString currKeyMap = m_config->value(SETTING_KEYPAD_KEYMAP, SETTING_KEYPAD_CEMU).toString();
836 if (!SETTING_KEYPAD_NATURAL.compare(currKeyMap, Qt::CaseInsensitive)) {
837 ui->radioNaturalKeys->setChecked(true);
838 } else if (!SETTING_KEYPAD_CEMU.compare(currKeyMap, Qt::CaseInsensitive)) {
839 ui->radioCEmuKeys->setChecked(true);
840 } else if (!SETTING_KEYPAD_TILEM.compare(currKeyMap, Qt::CaseInsensitive)) {
841 ui->radioTilEmKeys->setChecked(true);
842 } else if (!SETTING_KEYPAD_WABBITEMU.compare(currKeyMap, Qt::CaseInsensitive)) {
843 ui->radioWabbitemuKeys->setChecked(true);
844 } else if (!SETTING_KEYPAD_JSTIFIED.compare(currKeyMap, Qt::CaseInsensitive)) {
845 ui->radiojsTIfiedKeys->setChecked(true);
846 } else if (!SETTING_KEYPAD_CUSTOM.compare(currKeyMap, Qt::CaseInsensitive)) {
847 ui->radioCustomKeys->setChecked(true);
848 }
849 setKeymap(currKeyMap);
850 }
851
setFullscreen(int value)852 void MainWindow::setFullscreen(int value) {
853 static QWidget *parentPtr = Q_NULLPTR;
854 if (parentPtr == Q_NULLPTR) {
855 parentPtr = ui->lcd->parentWidget();
856 }
857 switch (value) {
858 default:
859 case FULLSCREEN_NONE:
860 showNormal();
861 ui->lcd->setParent(parentPtr);
862 ui->lcd->showNormal();
863 lcdAdjust();
864 m_fullscreen = FULLSCREEN_NONE;
865 break;
866 case FULLSCREEN_ALL:
867 showFullScreen();
868 m_fullscreen = FULLSCREEN_ALL;
869 break;
870 case FULLSCREEN_LCD:
871 ui->lcd->setParent(this, Qt::Tool | Qt::FramelessWindowHint | Qt::CustomizeWindowHint);
872 ui->lcd->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
873 ui->lcd->showFullScreen();
874 ui->lcd->installEventFilter(keypadBridge);
875 ui->lcd->setFocus();
876 m_fullscreen = FULLSCREEN_LCD;
877 break;
878 }
879 }
880
setTop(bool state)881 void MainWindow::setTop(bool state) {
882 if (state) {
883 setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
884 } else {
885 setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
886 }
887 if (m_setup) {
888 show();
889 }
890 m_config->setValue(SETTING_ALWAYS_ON_TOP, state);
891 ui->checkAlwaysOnTop->setCheckState(state ? Qt::Checked : Qt::Unchecked);
892 }
893
stateSaveInfo()894 void MainWindow::stateSaveInfo() {
895 QStringList slotNames;
896 QStringList slotPaths;
897
898 for (int i = 0; i < ui->slotView->rowCount(); i++) {
899 slotNames.append(ui->slotView->item(i, SLOT_NAME_COL)->text());
900 slotPaths.append(ui->slotView->item(i, SLOT_EDIT_COL)->data(Qt::UserRole).toString());
901 }
902
903 m_config->setValue(SETTING_SLOT_NAMES, slotNames);
904 m_config->setValue(SETTING_SLOT_PATHS, slotPaths);
905 }
906
stateLoadInfo()907 void MainWindow::stateLoadInfo() {
908 QStringList slotNames = m_config->value(SETTING_SLOT_NAMES).toStringList();
909 QStringList slotPaths = m_config->value(SETTING_SLOT_PATHS).toStringList();
910
911 for (int i = 0; i < slotNames.size(); i++) {
912 QString name = slotNames.at(i);
913 QString path = slotPaths.at(i);
914 stateAdd(name, path);
915 }
916 }
917
setRecentSave(bool state)918 void MainWindow::setRecentSave(bool state) {
919 ui->checkSaveRecent->setChecked(state);
920 m_config->setValue(SETTING_RECENT_SAVE, state);
921 }
922
setPreRevisionI(bool state)923 void MainWindow::setPreRevisionI(bool state) {
924 ui->checkPreI->setChecked(state);
925 m_config->setValue(SETTING_DEBUGGER_PRE_I, state);
926 cpu.preI = state;
927 }
928
setNormalOs(bool state)929 void MainWindow::setNormalOs(bool state) {
930 ui->checkNormOs->setChecked(state);
931 m_config->setValue(SETTING_DEBUGGER_NORM_OS, state);
932 m_normalOs = state;
933 if (guiDebug) {
934 ui->opView->setEnabled(state);
935 ui->vatView->setEnabled(state);
936 ui->opStack->setEnabled(state);
937 ui->fpStack->setEnabled(state);
938 if (state) {
939 osUpdate();
940 }
941 }
942 }
943
setUIBoundaries(bool state)944 void MainWindow::setUIBoundaries(bool state) {
945 m_config->setValue(SETTING_WINDOW_SEPARATOR, state);
946 if (state) {
947 setStyleSheet(QStringLiteral("QMainWindow::separator{ width: 4px; height: 4px; }"));
948 } else {
949 setStyleSheet(QStringLiteral("QMainWindow::separator{ width: 0px; height: 0px; }"));
950 }
951 }
952
recentSaveInfo()953 void MainWindow::recentSaveInfo() {
954 QStringList paths;
955 QStringList selects;
956
957 if (m_config->value(SETTING_RECENT_SAVE).toBool()) {
958 for (int i = 0; i < ui->varLoadedView->rowCount(); i++) {
959 paths.append(ui->varLoadedView->item(i, RECENT_PATH_COL)->text());
960 selects.append(static_cast<QAbstractButton *>(ui->varLoadedView->cellWidget(i, RECENT_SELECT_COL))->isChecked() ? TXT_YES : TXT_NO);
961 }
962 }
963
964 m_config->setValue(SETTING_RECENT_PATHS, paths);
965 m_config->setValue(SETTING_RECENT_SELECT, selects);
966 }
967
recentLoadInfo()968 void MainWindow::recentLoadInfo() {
969 QStringList paths = m_config->value(SETTING_RECENT_PATHS).toStringList();
970 QStringList selects = m_config->value(SETTING_RECENT_SELECT).toStringList();
971
972 if (m_config->value(SETTING_RECENT_SAVE).toBool()) {
973 for (int i = 0; i < paths.size(); i++) {
974 const QString path = paths.at(i);
975 bool select = selects.at(i) == TXT_YES;
976 sendingHandler->addFile(path, select);
977 }
978 }
979 }
980
setMemDocks()981 void MainWindow::setMemDocks() {
982 QStringList names = m_config->value(SETTING_WINDOW_MEMORY_DOCKS).toStringList();
983 QList<int> bytes = m_config->value(SETTING_WINDOW_MEMORY_DOCK_BYTES).value<QList<int>>();
984 QList<bool> ascii = m_config->value(SETTING_WINDOW_MEMORY_DOCK_ASCII).value<QList<bool>>();
985
986 if (names.length() != bytes.length()) {
987 return;
988 }
989
990 for (int i = 0; i < names.length(); i++) {
991 addMemDock(names.at(i), bytes.at(i), ascii.at(i));
992 }
993 }
994
setVisualizerDocks()995 void MainWindow::setVisualizerDocks() {
996 QStringList names = m_config->value(SETTING_WINDOW_VISUALIZER_DOCKS).toStringList();
997 QStringList configs = m_config->value(SETTING_WINDOW_VISUALIZER_CONFIG).toStringList();
998
999 if (names.length() != configs.length()) {
1000 return;
1001 }
1002
1003 for (int i = 0; i < names.length(); i++) {
1004 addVisualizerDock(names.at(i), configs.at(i));
1005 }
1006 }
1007
setKeyHistoryDocks()1008 void MainWindow::setKeyHistoryDocks() {
1009 QStringList names = m_config->value(SETTING_WINDOW_KEYHISTORY_DOCKS).toStringList();
1010 QList<int> sizes = m_config->value(SETTING_WINDOW_KEYHISTORY_CONFIG).value<QList<int>>();
1011
1012 if (names.length() != sizes.length()) {
1013 return;
1014 }
1015
1016 for (int i = 0; i < names.length(); i++) {
1017 addKeyHistoryDock(names.at(i), sizes.at(i));
1018 }
1019 }
1020
1021
addKeyHistoryDock(const QString & magic,int size)1022 void MainWindow::addKeyHistoryDock(const QString &magic, int size) {
1023 if (m_docksKeyHistory.contains(magic)) {
1024 return;
1025 }
1026
1027 m_docksKeyHistory.append(magic);
1028 m_docksKeyHistorySize.append(size);
1029
1030 DockWidget *dw = new DockWidget(TXT_KEYHISTORY_DOCK, this);
1031 KeyHistoryWidget *widget = new KeyHistoryWidget(this, size);
1032
1033 dw->makeCloseableFloat(true);
1034
1035 if (m_setup) {
1036 dw->setFloating(true);
1037 dw->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, dw->minimumSize(), qApp->desktop()->availableGeometry()));
1038 }
1039
1040 connect(ui->keypadWidget, &KeypadWidget::keyPressed, widget, &KeyHistoryWidget::add);
1041 connect(widget, &KeyHistoryWidget::fontSizeChanged, [this, widget, magic]{
1042 int index;
1043 if ((index = m_docksKeyHistory.indexOf(magic)) != -1) {
1044 m_docksKeyHistorySize[index] = widget->getFontSize();
1045 }
1046 });
1047 connect(dw, &DockWidget::closed, [this, magic]{
1048 int index;
1049 if ((index = m_docksKeyHistory.indexOf(magic)) != -1) {
1050 m_docksKeyHistory.removeAt(index);
1051 m_docksKeyHistorySize.removeAt(index);
1052 }
1053 });
1054
1055 dw->setState(m_uiEditMode);
1056 addDockWidget(Qt::RightDockWidgetArea, dw);
1057 dw->setObjectName(magic);
1058 dw->setWidget(widget);
1059
1060 if (m_setup) {
1061 dw->show();
1062 dw->activateWindow();
1063 dw->raise();
1064 }
1065 }
1066
setVersion()1067 void MainWindow::setVersion() {
1068 m_config->setValue(SETTING_VERSION, QStringLiteral(CEMU_VERSION));
1069 }
1070
checkVersion()1071 void MainWindow::checkVersion() {
1072 bool ask = false;
1073
1074 if (isFirstRun()) {
1075 setVersion();
1076 return;
1077 }
1078
1079 if (m_config->contains(SETTING_VERSION)) {
1080 if (m_config->value(SETTING_VERSION, QStringLiteral(CEMU_VERSION)).toString().compare(QStringLiteral(CEMU_VERSION))) {
1081 ask = true;
1082 }
1083 } else {
1084 ask = true;
1085 }
1086
1087 if (ask) {
1088 if (CEMU_RELEASE) {
1089 setAutoUpdates(true);
1090 }
1091
1092 m_keepSetup = true;
1093 QCheckBox *cb = new QCheckBox(tr("Keep migratable settings"));
1094 cb->setChecked(true);
1095 QMessageBox msgbox;
1096 msgbox.setText(tr("This version of CEmu is not compatible with your settings, probably made by an older version. "
1097 "Would you like to erase them to prevent any unexpected behavior?"));
1098 msgbox.setIcon(QMessageBox::Icon::Question);
1099 msgbox.addButton(QMessageBox::Yes);
1100 msgbox.addButton(QMessageBox::No);
1101 msgbox.setDefaultButton(QMessageBox::Yes);
1102 msgbox.setCheckBox(cb);
1103
1104 connect(cb, &QCheckBox::stateChanged, [this](int state) {
1105 m_keepSetup = static_cast<Qt::CheckState>(state) == Qt::CheckState::Checked;
1106 });
1107
1108 if (msgbox.exec() == QMessageBox::Yes) {
1109 resetCEmu();
1110 }
1111 setVersion();
1112 }
1113 }
1114
saveDebug()1115 void MainWindow::saveDebug() {
1116 if (m_config->value(SETTING_DEBUGGER_SAVE_ON_CLOSE, false).toBool()) {
1117 debugExportFile(m_config->value(SETTING_DEBUGGER_IMAGE_PATH).toString());
1118 }
1119 }
1120
saveSettings()1121 void MainWindow::saveSettings() {
1122 if (opts.useSettings) {
1123 m_config->setValue(SETTING_WINDOW_POSITION, pos());
1124 m_config->setValue(SETTING_WINDOW_GEOMETRY, saveGeometry());
1125 m_config->setValue(SETTING_WINDOW_STATE, saveState());
1126 m_config->setValue(SETTING_CURRENT_DIR, m_dir.absolutePath());
1127 m_config->setValue(SETTING_DEBUGGER_FLASH_BYTES, ui->flashBytes->value());
1128 m_config->setValue(SETTING_DEBUGGER_RAM_BYTES, ui->ramBytes->value());
1129 m_config->setValue(SETTING_DEBUGGER_FLASH_ASCII, ui->flashAscii->isChecked());
1130 m_config->setValue(SETTING_DEBUGGER_RAM_ASCII, ui->ramAscii->isChecked());
1131 m_config->setValue(SETTING_WINDOW_FULLSCREEN, m_fullscreen);
1132 m_config->setValue(SETTING_WINDOW_MEMORY_DOCKS, m_docksMemory);
1133 m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_BYTES, QVariant::fromValue(m_docksMemoryBytes));
1134 m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_ASCII, QVariant::fromValue(m_docksMemoryAscii));
1135 m_config->setValue(SETTING_WINDOW_VISUALIZER_DOCKS, m_docksVisualizer);
1136 m_config->setValue(SETTING_WINDOW_VISUALIZER_CONFIG, m_docksVisualizerConfig);
1137 m_config->setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, m_docksKeyHistory);
1138 m_config->setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, QVariant::fromValue(m_docksKeyHistorySize));
1139
1140 saveDebug();
1141 stateSaveInfo();
1142 recentSaveInfo();
1143
1144 m_config->sync();
1145 }
1146 }
1147
guiExport()1148 void MainWindow::guiExport() {
1149 QString filter = tr("Window Config (*.ini)");
1150 QString path = QFileDialog::getSaveFileName(this, tr("Save window configuration"),
1151 m_dir.absolutePath(), filter, &filter);
1152 if (path.isEmpty()) {
1153 return;
1154 }
1155
1156 QSettings window(path, QSettings::IniFormat);
1157 window.setValue(SETTING_SCREEN_SKIN, m_config->value(SETTING_SCREEN_SKIN));
1158 window.setValue(SETTING_WINDOW_FULLSCREEN, m_fullscreen);
1159 window.setValue(SETTING_WINDOW_MENUBAR, m_config->value(SETTING_WINDOW_MENUBAR));
1160 window.setValue(SETTING_WINDOW_STATUSBAR, m_config->value(SETTING_WINDOW_STATUSBAR));
1161 window.setValue(SETTING_WINDOW_SEPARATOR, m_config->value(SETTING_WINDOW_SEPARATOR));
1162 window.setValue(SETTING_WINDOW_STATE, saveState());
1163 window.setValue(SETTING_WINDOW_GEOMETRY, saveGeometry());
1164 window.setValue(SETTING_WINDOW_POSITION, pos());
1165 window.setValue(SETTING_WINDOW_MEMORY_DOCKS, m_docksMemory);
1166 window.setValue(SETTING_WINDOW_MEMORY_DOCK_BYTES, QVariant::fromValue(m_docksMemoryBytes));
1167 window.setValue(SETTING_WINDOW_MEMORY_DOCK_ASCII, QVariant::fromValue(m_docksMemoryAscii));
1168 window.setValue(SETTING_WINDOW_VISUALIZER_DOCKS, m_docksVisualizer);
1169 window.setValue(SETTING_WINDOW_VISUALIZER_CONFIG, m_docksVisualizerConfig);
1170 window.setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, m_docksKeyHistory);
1171 window.setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, QVariant::fromValue(m_docksKeyHistorySize));
1172 window.setValue(SETTING_UI_EDIT_MODE, m_uiEditMode);
1173 window.setValue(SETTING_STATUS_INTERVAL, ui->statusInterval->value());
1174 window.sync();
1175 }
1176
guiImport()1177 void MainWindow::guiImport() {
1178 QString path = QFileDialog::getOpenFileName(this, tr("Select saved image to restore from"),
1179 m_dir.absolutePath(),
1180 tr("Window Config (*.ini);;All files (*.*)"));
1181 if (path.isEmpty()) {
1182 return;
1183 }
1184
1185 QSettings window(path, QSettings::IniFormat);
1186 ipcCloseConnected();
1187 m_config->setValue(SETTING_SCREEN_SKIN, window.value(SETTING_SCREEN_SKIN));
1188 m_config->setValue(SETTING_WINDOW_FULLSCREEN, window.value(SETTING_WINDOW_FULLSCREEN));
1189 m_config->setValue(SETTING_WINDOW_GEOMETRY, window.value(SETTING_WINDOW_GEOMETRY));
1190 m_config->setValue(SETTING_WINDOW_STATE, window.value(SETTING_WINDOW_STATE));
1191 m_config->setValue(SETTING_WINDOW_POSITION, window.value(SETTING_WINDOW_POSITION));
1192 m_config->setValue(SETTING_WINDOW_MEMORY_DOCKS, window.value(SETTING_WINDOW_MEMORY_DOCKS));
1193 m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_BYTES, window.value(SETTING_WINDOW_MEMORY_DOCK_BYTES));
1194 m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_ASCII, window.value(SETTING_WINDOW_MEMORY_DOCK_ASCII));
1195 m_config->setValue(SETTING_WINDOW_VISUALIZER_DOCKS, window.value(SETTING_WINDOW_VISUALIZER_DOCKS));
1196 m_config->setValue(SETTING_WINDOW_VISUALIZER_CONFIG, window.value(SETTING_WINDOW_VISUALIZER_CONFIG));
1197 m_config->setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, window.value(SETTING_WINDOW_KEYHISTORY_DOCKS));
1198 m_config->setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, window.value(SETTING_WINDOW_KEYHISTORY_CONFIG));
1199 m_config->setValue(SETTING_UI_EDIT_MODE, window.value(SETTING_UI_EDIT_MODE));
1200 m_config->setValue(SETTING_WINDOW_MENUBAR, window.value(SETTING_WINDOW_MENUBAR));
1201 m_config->setValue(SETTING_WINDOW_STATUSBAR, window.value(SETTING_WINDOW_STATUSBAR));
1202 m_config->setValue(SETTING_WINDOW_SEPARATOR, window.value(SETTING_WINDOW_SEPARATOR));
1203 m_config->setValue(SETTING_STATUS_INTERVAL, window.value(SETTING_STATUS_INTERVAL));
1204 m_needReload = true;
1205 close();
1206 }
1207
keymapExport()1208 void MainWindow::keymapExport() {
1209 QString filter = tr("Keymap Config (*.ini)");
1210 QString path = QFileDialog::getSaveFileName(this, tr("Save keymap configuration"),
1211 m_dir.absolutePath(), filter, &filter);
1212 if (path.isEmpty()) {
1213 return;
1214 }
1215
1216
1217 keypadBridge->keymapExport(path);
1218 }
1219
isFirstRun()1220 bool MainWindow::isFirstRun() {
1221 return !m_config->value(SETTING_FIRST_RUN, false).toBool();
1222 }
1223
1224