1 #include "v1541commander.h"
2 #include "aboutbox.h"
3 #include "logwindow.h"
4 #include "mainwindow.h"
5 #include "petsciiwindow.h"
6 #include "petsciiedit.h"
7 #include "settings.h"
8 #include "settingsdialog.h"
9
10 #include <QAction>
11 #include <QCryptographicHash>
12 #include <QFileDialog>
13 #include <QFileInfo>
14 #include <QFont>
15 #include <QFontDatabase>
16 #ifndef _WIN32
17 #include <QIcon>
18 #endif
19 #include <QLocalServer>
20 #include <QLocalSocket>
21 #include <QMessageBox>
22 #include <QPixmap>
23 #include <QSet>
24 #include <QScreen>
25 #include <QTranslator>
26 #include <QWindow>
27 #ifdef _WIN32
28 #include <windows.h>
29 #include <lmcons.h>
30 #else
31 #include <sys/types.h>
32 #include <pwd.h>
33 #include <unistd.h>
34 #endif
35
36 #include <1541img/log.h>
37
38 class V1541Commander::priv
39 {
40 public:
41 priv(V1541Commander *commander);
42 V1541Commander *commander;
43 QFont c64font;
44 QFont menufont;
45 QFont statusfont;
46 QPixmap statusLedRed;
47 QPixmap statusLedYellow;
48 QPixmap statusLedGreen;
49 #ifdef _WIN32
50 HICON appIcon;
51 #else
52 QIcon appIcon;
53 #endif
54 QAction newAction;
55 QAction openAction;
56 QAction saveAction;
57 QAction saveAsAction;
58 QAction exportZipcodeAction;
59 QAction exportZipcodeD64Action;
60 QAction exportLynxAction;
61 QAction closeAction;
62 QAction settingsAction;
63 QAction aboutAction;
64 QAction exitAction;
65 QAction petsciiWindowAction;
66 QAction logWindowAction;
67 QAction fsOptionsAction;
68 QAction rewriteImageAction;
69 QAction autoMapLcAction;
70 QAction mapLcAction;
71 QAction newFileAction;
72 QAction deleteFileAction;
73 QAction lowerCaseAction;
74 QVector<MainWindow *> allWindows;
75 MainWindow *lastActiveWindow;
76 PetsciiWindow petsciiWindow;
77 AboutBox aboutBox;
78 LogWindow logWindow;
79 SettingsDialog settingsDialog;
80 QString instanceServerName;
81 QLocalServer instanceServer;
82 bool isPrimaryInstance;
83 Settings settings;
84 QSet<QLocalSocket *> activeClients;
85
86 MainWindow *addWindow(bool show = true);
87 void removeWindow(MainWindow *w);
88 void updateActions(MainWindow *w);
89 };
90
priv(V1541Commander * commander)91 V1541Commander::priv::priv(V1541Commander *commander) :
92 commander(commander),
93 c64font("C64 Pro Mono", 10),
94 menufont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)),
95 statusfont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)),
96 statusLedRed(":/statusled_red.png"),
97 statusLedYellow(":/statusled_yellow.png"),
98 statusLedGreen(":/statusled_green.png"),
99 appIcon(),
100 newAction(tr("&New")),
101 openAction(tr("&Open")),
102 saveAction(tr("&Save")),
103 saveAsAction(tr("Save &As")),
104 exportZipcodeAction(tr("&Zipcode")),
105 exportZipcodeD64Action(tr("Zipcode (&D64)")),
106 exportLynxAction(tr("&LyNX")),
107 closeAction(tr("&Close")),
108 settingsAction(tr("Se&ttings")),
109 aboutAction(tr("&About")),
110 exitAction(tr("E&xit")),
111 petsciiWindowAction(tr("&PETSCII Input")),
112 logWindowAction(tr("lib1541img &log")),
113 fsOptionsAction(tr("Filesystem &Options")),
114 rewriteImageAction(tr("&Rewrite Image")),
115 autoMapLcAction(tr("&Auto map on input")),
116 mapLcAction(tr("&Map current disk")),
117 newFileAction(tr("&New File")),
118 deleteFileAction(tr("&Delete File")),
119 lowerCaseAction(tr("&Lowercase")),
120 allWindows(),
121 lastActiveWindow(0),
122 petsciiWindow(c64font),
123 aboutBox(c64font),
124 logWindow(),
125 settingsDialog(),
126 instanceServerName(),
127 instanceServer(),
128 isPrimaryInstance(false),
129 settings(),
130 activeClients()
131 {
132 newAction.setShortcuts(QKeySequence::New);
133 newAction.setStatusTip(tr("Create a new disk image"));
134 openAction.setShortcuts(QKeySequence::Open);
135 openAction.setStatusTip(tr("Open a disk image"));
136 saveAction.setShortcuts(QKeySequence::Save);
137 saveAction.setStatusTip(tr("Save disk image"));
138 #ifdef _WIN32
139 saveAsAction.setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S));
140 #else
141 saveAsAction.setShortcuts(QKeySequence::SaveAs);
142 #endif
143 saveAsAction.setStatusTip(tr("Save disk image as new file"));
144 exportZipcodeAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_Z));
145 exportZipcodeAction.setStatusTip(tr("Export as a set of Zipcode files"));
146 exportZipcodeD64Action.setShortcut(
147 QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Z));
148 exportZipcodeD64Action.setStatusTip(
149 tr("Export as Zipcode files on a new D64 image"));
150 exportLynxAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_Y));
151 exportLynxAction.setStatusTip(tr("Export as a LyNX file"));
152 closeAction.setShortcuts(QKeySequence::Close);
153 closeAction.setStatusTip(tr("Close current file"));
154 settingsAction.setStatusTip(tr("Configure V1541Commander settings"));
155 #ifdef _WIN32
156 exitAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_Q));
157 #else
158 exitAction.setShortcuts(QKeySequence::Quit);
159 #endif
160 exitAction.setStatusTip(tr("Exit the application"));
161 petsciiWindowAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_P));
162 petsciiWindowAction.setStatusTip(tr("Show PETSCII input window"));
163 logWindowAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_L));
164 logWindowAction.setStatusTip(tr("Show lib1541img log messages"));
165 fsOptionsAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_F));
166 fsOptionsAction.setStatusTip(tr("Change filesystem options"));
167 rewriteImageAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_R));
168 rewriteImageAction.setStatusTip(tr("Rewrite disk image from scratch"));
169 autoMapLcAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_M));
170 autoMapLcAction.setStatusTip(tr("Toggle automatic mapping of uppercase "
171 "GFX chars to lowercase compatible codes"));
172 autoMapLcAction.setCheckable(true);
173 mapLcAction.setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_M));
174 mapLcAction.setStatusTip(tr("Map uppercase GFX chars to lowercase "
175 "compatible codes in current disk"));
176 newFileAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_Period));
177 newFileAction.setStatusTip(tr("Create new file at selection"));
178 deleteFileAction.setShortcut(QKeySequence::Delete);
179 deleteFileAction.setStatusTip(tr("Delete selected file"));
180 lowerCaseAction.setShortcut(QKeySequence(Qt::CTRL+Qt::Key_Space));
181 lowerCaseAction.setStatusTip(tr("Toggle lowercase / graphics font mode"));
182 lowerCaseAction.setCheckable(true);
183 #ifdef _WIN32
184 HINSTANCE inst = GetModuleHandleW(0);
185 appIcon = LoadIcon(inst, MAKEINTRESOURCE(1000));
186 SetClassLong(HWND(aboutBox.winId()), GCL_HICON, (LONG)appIcon);
187 NONCLIENTMETRICSW ncm;
188 ncm.cbSize = sizeof ncm;
189 if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof ncm, &ncm, 0))
190 {
191 if (ncm.lfMessageFont.lfHeight < 0) ncm.lfMessageFont.lfHeight *= -1;
192 if (ncm.lfMenuFont.lfHeight < 0) ncm.lfMenuFont.lfHeight *= -1;
193 if (ncm.lfStatusFont.lfHeight < 0) ncm.lfStatusFont.lfHeight *= -1;
194 QFont sysfont(QString::fromWCharArray(ncm.lfMessageFont.lfFaceName));
195 sysfont.setPixelSize(ncm.lfMessageFont.lfHeight);
196 commander->setFont(sysfont);
197 QFont mfont(QString::fromWCharArray(ncm.lfMenuFont.lfFaceName));
198 mfont.setPixelSize(ncm.lfMenuFont.lfHeight);
199 menufont = mfont;
200 QFont sfont(QString::fromWCharArray(ncm.lfStatusFont.lfFaceName));
201 sfont.setPixelSize(ncm.lfStatusFont.lfHeight);
202 statusfont = sfont;
203 }
204 #else
205 appIcon.addPixmap(QPixmap(":/icon_256.png"));
206 appIcon.addPixmap(QPixmap(":/icon_48.png"));
207 appIcon.addPixmap(QPixmap(":/icon_32.png"));
208 appIcon.addPixmap(QPixmap(":/icon_16.png"));
209 logWindow.setWindowIcon(appIcon);
210 aboutBox.setWindowIcon(appIcon);
211 petsciiWindow.setWindowIcon(appIcon);
212 settingsDialog.setWindowIcon(appIcon);
213 #endif
214 QCryptographicHash appData(QCryptographicHash::Sha256);
215 appData.addData(QCoreApplication::organizationName().toUtf8());
216 appData.addData(QCoreApplication::applicationName().toUtf8());
217 #ifdef _WIN32
218 wchar_t username[UNLEN + 1];
219 DWORD usernameLen = UNLEN + 1;
220 if (GetUserNameW(username, &usernameLen))
221 {
222 appData.addData(QString::fromWCharArray(username).toUtf8());
223 }
224 else
225 {
226 appData.addData(qgetenv("USERNAME"));
227 }
228 #else
229 QByteArray username;
230 uid_t uid = geteuid();
231 struct passwd *pw = getpwuid(uid);
232 if (pw)
233 {
234 username = pw->pw_name;
235 }
236 if (username.isEmpty())
237 {
238 username = qgetenv("USER");
239 }
240 appData.addData(username);
241 #endif
242 instanceServerName = appData.result().toBase64().replace("/","_");
243 bool listening = instanceServer.listen(instanceServerName);
244 #ifdef _WIN32
245 if (listening)
246 {
247 CreateMutexW(0, true,
248 reinterpret_cast<LPCWSTR>(instanceServerName.utf16()));
249 if (GetLastError() == ERROR_ALREADY_EXISTS)
250 {
251 instanceServer.close();
252 listening = false;
253 }
254 }
255 #else
256 if (!listening)
257 {
258 QLocalSocket sock;
259 sock.connectToServer(instanceServerName, QIODevice::WriteOnly);
260 if (sock.state() != QLocalSocket::ConnectedState &&
261 !sock.waitForConnected(2000))
262 {
263 QLocalServer::removeServer(instanceServerName);
264 listening = instanceServer.listen(instanceServerName);
265 }
266 else
267 {
268 sock.disconnectFromServer();
269 }
270 }
271 #endif
272 isPrimaryInstance = listening;
273 }
274
275 #ifdef _WIN32
stackWinPos(QPoint & pos,const QRect * screenRect)276 static void stackWinPos(QPoint &pos, const QRect *screenRect)
277 {
278 pos.setX(pos.x() + 64);
279 pos.setY(pos.y() + 32);
280 if (screenRect)
281 {
282 if (pos.x() + 480 > screenRect->right()
283 || pos.y() + 120 > screenRect->bottom())
284 {
285 pos.setX(screenRect->left());
286 pos.setY(screenRect->top());
287 }
288 }
289 }
290 #endif
291
addWindow(bool show)292 MainWindow *V1541Commander::priv::addWindow(bool show)
293 {
294 #ifdef _WIN32
295 MainWindow *newWin = new MainWindow();
296 MainWindow *lastWin = lastActiveWindow;
297 if (!lastWin && allWindows.count() > 0) lastWin = allWindows.first();
298 if (lastWin)
299 {
300 const QScreen *screen = 0;
301 QRect screenGeom;
302 const QRect *screenRect = 0;
303 const QWindow *winhndl = lastWin->windowHandle();
304 if (winhndl)
305 {
306 screen = winhndl->screen();
307 }
308 if (screen)
309 {
310 screenGeom = screen->availableGeometry();
311 screenRect = &screenGeom;
312 }
313 QPoint pos = lastWin->pos();
314 stackWinPos(pos, screenRect);
315 bool posOk = false;
316 for (int i = 0; i < 20; ++i)
317 {
318 posOk = true;
319 for (QVector<MainWindow *>::const_iterator w = allWindows.cbegin();
320 w != allWindows.cend(); ++w)
321 {
322 if (pos == (*w)->pos())
323 {
324 posOk = false;
325 break;
326 }
327 }
328 if (posOk) break;
329 stackWinPos(pos, screenRect);
330 }
331 newWin->move(pos);
332 }
333 lastActiveWindow = newWin;
334 #else
335 lastActiveWindow = new MainWindow();
336 lastActiveWindow->setWindowIcon(appIcon);
337 #endif
338 if (show) lastActiveWindow->show();
339 allWindows.append(lastActiveWindow);
340 connect(lastActiveWindow, &MainWindow::activated,
341 commander, &V1541Commander::windowActivated);
342 connect(lastActiveWindow, &MainWindow::closed,
343 commander, &V1541Commander::windowClosed);
344 connect(lastActiveWindow, &MainWindow::contentChanged,
345 commander, &V1541Commander::windowContentChanged);
346 connect(lastActiveWindow, &MainWindow::modifiedChanged,
347 commander, &V1541Commander::windowContentChanged);
348 connect(lastActiveWindow, &MainWindow::selectionChanged,
349 commander, &V1541Commander::windowSelectionChanged);
350 return lastActiveWindow;
351 }
352
removeWindow(MainWindow * w)353 void V1541Commander::priv::removeWindow(MainWindow *w)
354 {
355 if (w == lastActiveWindow) lastActiveWindow = 0;
356 allWindows.removeAll(w);
357 w->close();
358 if (allWindows.count() == 0)
359 {
360 if (settings.rememberWindowPositions())
361 {
362 settings.setMainGeometry(w->saveGeometry());
363 settings.setPetsciiGeometry(petsciiWindow.saveGeometry());
364 settings.setLogGeometry(logWindow.saveGeometry());
365 }
366 closeAllWindows();
367 }
368 w->deleteLater();
369 }
370
updateActions(MainWindow * w)371 void V1541Commander::priv::updateActions(MainWindow *w)
372 {
373 closeAction.setEnabled(w->content() != MainWindow::Content::None);
374 saveAction.setEnabled(w->hasValidContent() &&
375 (w->isWindowModified() || w->filename().isEmpty()));
376 saveAsAction.setEnabled(w->hasValidContent());
377 exportZipcodeAction.setEnabled(w->hasValidContent());
378 exportZipcodeD64Action.setEnabled(w->hasValidContent());
379 exportLynxAction.setEnabled(w->hasValidContent());
380 fsOptionsAction.setEnabled(w->content() == MainWindow::Content::Image
381 && w->hasValidContent());
382 rewriteImageAction.setEnabled(w->content() == MainWindow::Content::Image
383 && w->hasValidContent());
384 newFileAction.setEnabled(w->content() == MainWindow::Content::Image
385 && !w->isReadOnly());
386 mapLcAction.setEnabled(w->content() == MainWindow::Content::Image
387 && !w->isReadOnly());
388 deleteFileAction.setEnabled(w->content() == MainWindow::Content::Image
389 && !w->isReadOnly() && w->hasValidSelection());
390 }
391
V1541Commander(int & argc,char ** argv,QTranslator * translator)392 V1541Commander::V1541Commander(int &argc, char **argv, QTranslator *translator)
393 : QApplication(argc, argv)
394 {
395 #ifdef DEBUG
396 setMaxLogLevel(L_DEBUG);
397 #endif
398 QFontDatabase::addApplicationFont(":/C64_Pro_Mono-STYLE.ttf");
399 installTranslator(translator);
400 d = new priv(this);
401 d->addWindow(false);
402 if (d->settings.rememberWindowPositions())
403 {
404 d->lastActiveWindow->restoreGeometry(d->settings.mainGeometry());
405 d->petsciiWindow.restoreGeometry(d->settings.petsciiGeometry());
406 d->logWindow.restoreGeometry(d->settings.logGeometry());
407 }
408 d->lowerCaseAction.setChecked(d->settings.lowercase());
409 d->petsciiWindow.setLowercase(d->settings.lowercase());
410 d->autoMapLcAction.setChecked(d->settings.automapPetsciiToLc());
411 connect(&d->newAction, &QAction::triggered,
412 this, &V1541Commander::newImage);
413 connect(&d->openAction, SIGNAL(triggered()),
414 this, SLOT(open()));
415 connect(&d->saveAction, &QAction::triggered,
416 this, &V1541Commander::save);
417 connect(&d->saveAsAction, &QAction::triggered,
418 this, &V1541Commander::saveAs);
419 connect(&d->exportZipcodeAction, &QAction::triggered,
420 this, &V1541Commander::exportZipcode);
421 connect(&d->exportZipcodeD64Action, &QAction::triggered,
422 this, &V1541Commander::exportZipcodeD64);
423 connect(&d->exportLynxAction, &QAction::triggered,
424 this, &V1541Commander::exportLynx);
425 connect(&d->closeAction, &QAction::triggered,
426 this, &V1541Commander::close);
427 connect(&d->settingsAction, &QAction::triggered,
428 this, &V1541Commander::showSettings);
429 connect(&d->aboutAction, &QAction::triggered,
430 this, &V1541Commander::about);
431 connect(&d->exitAction, &QAction::triggered,
432 this, &V1541Commander::exit);
433 connect(&d->petsciiWindowAction, &QAction::triggered,
434 this, &V1541Commander::showPetsciiWindow);
435 connect(&d->logWindowAction, &QAction::triggered,
436 this, &V1541Commander::showLogWindow);
437 connect(&d->fsOptionsAction, &QAction::triggered,
438 this, &V1541Commander::fsOptions);
439 connect(&d->rewriteImageAction, &QAction::triggered,
440 this, &V1541Commander::rewriteImage);
441 connect(&d->mapLcAction, &QAction::triggered,
442 this, &V1541Commander::mapToLc);
443 connect(&d->newFileAction, &QAction::triggered,
444 this, &V1541Commander::newFile);
445 connect(&d->deleteFileAction, &QAction::triggered,
446 this, &V1541Commander::deleteFile);
447 connect(&d->petsciiWindow, &PetsciiWindow::petsciiInput,
448 this, &V1541Commander::petsciiInput);
449 connect(&d->lowerCaseAction, &QAction::triggered, this, [this](){
450 d->settings.setLowercase(!d->settings.lowercase());
451 emit lowerCaseChanged(d->settings.lowercase());
452 d->petsciiWindow.setLowercase(d->settings.lowercase());
453 });
454 connect(&d->autoMapLcAction, &QAction::triggered, this, [this](){
455 d->settings.setAutomapPetsciiToLc(
456 !d->settings.automapPetsciiToLc());
457 emit autoMapToLcChanged(d->settings.automapPetsciiToLc());
458 });
459 connect(&d->logWindow, &LogWindow::logLineAppended,
460 this, &V1541Commander::logLineAppended);
461 if (d->isPrimaryInstance)
462 {
463 connect(&d->instanceServer, &QLocalServer::newConnection,
464 this, &V1541Commander::newConnection);
465 }
466 }
467
~V1541Commander()468 V1541Commander::~V1541Commander()
469 {
470 delete d;
471 }
472
sanitizeWindowRect(QRect * winRect,const QScreen * screen)473 static void sanitizeWindowRect(QRect *winRect, const QScreen *screen)
474 {
475 if (!screen) return;
476 QRect screenRect = screen->availableGeometry();
477 if (winRect->right() > screenRect.right())
478 {
479 winRect->moveRight(screenRect.right());
480 }
481 if (winRect->bottom() > screenRect.bottom())
482 {
483 winRect->moveBottom(screenRect.bottom());
484 }
485 if (winRect->top() < screenRect.top())
486 {
487 winRect->moveTop(screenRect.top());
488 }
489 if (winRect->left() < screenRect.left())
490 {
491 winRect->moveLeft(screenRect.left());
492 }
493 }
494
sanitizeWindowPos(QWidget * window)495 static void sanitizeWindowPos(QWidget *window)
496 {
497 const QScreen *screen = 0;
498 const QWindow *currentWin = window->windowHandle();
499 if (currentWin)
500 {
501 screen = currentWin->screen();
502 }
503 if (screen)
504 {
505 QRect winRect = window->frameGeometry();
506 sanitizeWindowRect(&winRect, screen);
507 window->move(winRect.topLeft());
508 }
509 }
510
show()511 void V1541Commander::show()
512 {
513 MainWindow *w = d->lastActiveWindow;
514 if (!w) return;
515
516 w->show();
517 sanitizeWindowPos(w);
518 }
519
newImage()520 void V1541Commander::newImage()
521 {
522 MainWindow *w = d->lastActiveWindow;
523 if (!w) return;
524
525 if (w->content() != MainWindow::Content::None)
526 {
527 w = d->addWindow();
528 }
529 w->newImage();
530 if (w->content() == MainWindow::Content::None)
531 {
532 if (d->allWindows.count() > 1)
533 {
534 d->removeWindow(w);
535 }
536 }
537 }
538
open(const QString & filename)539 void V1541Commander::open(const QString &filename)
540 {
541 MainWindow *w = d->lastActiveWindow;
542 if (!w || w->content() != MainWindow::Content::None)
543 {
544 w = d->addWindow();
545 }
546 w->show();
547 w->raise();
548 w->activateWindow();
549
550 w->openImage(filename);
551 if (w->content() == MainWindow::Content::None)
552 {
553 QMessageBox::critical(w, tr("Error reading file"),
554 tr("<p>The file you selected couldn't be read.</p>"
555 "<p>This means you either haven't permission to read "
556 "it or it doesn't contain a valid 1541 disc "
557 "image.</p>"));
558 if (d->allWindows.count() > 1)
559 {
560 d->removeWindow(w);
561 }
562 }
563 }
564
open()565 void V1541Commander::open()
566 {
567 MainWindow *w = d->lastActiveWindow;
568 if (!w) return;
569
570 QString imgFile = QFileDialog::getOpenFileName(w, tr("Open disk image"),
571 QString(), tr("1541 disk images (*.d64);;"
572 "Zipcode files (*!*.prg);;LyNX files (*.lnx);;all files (*)"));
573 if (!imgFile.isEmpty())
574 {
575 open(imgFile);
576 }
577 }
578
getFilterForWindowContent(MainWindow::Content content)579 static QString getFilterForWindowContent(MainWindow::Content content)
580 {
581 switch (content)
582 {
583 case MainWindow::Content::Image:
584 return QCoreApplication::translate("V1541Commander",
585 "1541 disk images (*.d64);;all files (*)");
586 default:
587 return QCoreApplication::translate("V1541Commander",
588 "all files (*)");
589 }
590 }
591
save()592 void V1541Commander::save()
593 {
594 MainWindow *w = d->lastActiveWindow;
595 if (!w) return;
596
597 if (w->filename().isEmpty()) saveAs();
598 else w->save();
599 }
600
saveAs()601 void V1541Commander::saveAs()
602 {
603 MainWindow *w = d->lastActiveWindow;
604 if (!w) return;
605
606 QString imgFile = QFileDialog::getSaveFileName(w, tr("Save as ..."),
607 QString(), getFilterForWindowContent(w->content()));
608 if (!imgFile.isEmpty())
609 {
610 w->save(imgFile);
611 }
612 }
613
exportZipcode()614 void V1541Commander::exportZipcode()
615 {
616 MainWindow *w = d->lastActiveWindow;
617 if (!w) return;
618
619 QString filename = w->filename();
620 if (!filename.isEmpty())
621 {
622 filename = QFileInfo(filename).completeBaseName();
623 }
624 QString zcFile = QFileDialog::getSaveFileName(w, tr("Export as ..."),
625 filename, tr("Zipcode files (*.prg);;all files (*)"));
626
627 if (!zcFile.isEmpty())
628 {
629 w->exportZipcode(zcFile);
630 }
631 }
632
exportZipcodeD64()633 void V1541Commander::exportZipcodeD64()
634 {
635 MainWindow *w = d->lastActiveWindow;
636 if (!w) return;
637 CbmdosVfs *vfs = w->exportZipcodeVfs();
638 if (vfs)
639 {
640 w = d->addWindow();
641 w->openVfs(vfs);
642 }
643 }
644
exportLynx()645 void V1541Commander::exportLynx()
646 {
647 MainWindow *w = d->lastActiveWindow;
648 if (!w) return;
649
650 QString filename = w->filename();
651 if (!filename.isEmpty())
652 {
653 filename = QFileInfo(filename).completeBaseName();
654 }
655 QString lynxFile = QFileDialog::getSaveFileName(w, tr("Export as ..."),
656 filename, tr("LyNX files (*.lnx);;all files (*)"));
657
658 if (!lynxFile.isEmpty())
659 {
660 w->exportLynx(lynxFile);
661 }
662 }
663
close()664 void V1541Commander::close()
665 {
666 MainWindow *w = d->lastActiveWindow;
667 if (!w) return;
668
669 w->closeDocument();
670 if (d->allWindows.count() > 1)
671 {
672 d->removeWindow(w);
673 }
674 }
675
showSettings()676 void V1541Commander::showSettings()
677 {
678 MainWindow *w = d->lastActiveWindow;
679 if (!w) return;
680
681 d->settingsDialog.show();
682 QRect settingsDialogRect = d->settingsDialog.frameGeometry();
683 QRect mainWinRect = w->frameGeometry();
684 settingsDialogRect.moveCenter(mainWinRect.center());
685
686 const QScreen *screen = 0;
687 const QWindow *currentWin = w->windowHandle();
688 if (currentWin)
689 {
690 screen = currentWin->screen();
691 sanitizeWindowRect(&settingsDialogRect, screen);
692 }
693
694 d->settingsDialog.move(settingsDialogRect.topLeft());
695 d->settingsDialog.activateWindow();
696 d->settingsDialog.raise();
697 }
698
about()699 void V1541Commander::about()
700 {
701 MainWindow *w = d->lastActiveWindow;
702 if (!w) return;
703
704 d->aboutBox.show();
705 QRect aboutBoxRect = d->aboutBox.frameGeometry();
706 QRect mainWinRect = w->frameGeometry();
707 aboutBoxRect.moveCenter(mainWinRect.center());
708
709 const QScreen *screen = 0;
710 const QWindow *currentWin = w->windowHandle();
711 if (currentWin)
712 {
713 screen = currentWin->screen();
714 sanitizeWindowRect(&aboutBoxRect, screen);
715 }
716
717 d->aboutBox.move(aboutBoxRect.topLeft());
718 d->aboutBox.activateWindow();
719 d->aboutBox.raise();
720 }
721
exit()722 void V1541Commander::exit()
723 {
724 closeAllWindows();
725 }
726
windowActivated()727 void V1541Commander::windowActivated()
728 {
729 MainWindow *w = static_cast<MainWindow *>(sender());
730 d->lastActiveWindow = w;
731 d->updateActions(w);
732 }
733
windowClosed()734 void V1541Commander::windowClosed()
735 {
736 d->removeWindow(static_cast<MainWindow*>(sender()));
737 }
738
windowContentChanged()739 void V1541Commander::windowContentChanged()
740 {
741 MainWindow *w = static_cast<MainWindow*>(sender());
742 if (w == d->lastActiveWindow) d->updateActions(w);
743 }
744
windowSelectionChanged()745 void V1541Commander::windowSelectionChanged()
746 {
747 MainWindow *w = static_cast<MainWindow*>(sender());
748 if (w == d->lastActiveWindow) d->updateActions(w);
749 }
750
showPetsciiWindow()751 void V1541Commander::showPetsciiWindow()
752 {
753 QRect geom = d->petsciiWindow.frameGeometry();
754 d->petsciiWindow.show();
755 d->petsciiWindow.move(geom.topLeft());
756 QCoreApplication::processEvents();
757 sanitizeWindowPos(&d->petsciiWindow);
758 d->petsciiWindow.raise();
759 }
760
showLogWindow()761 void V1541Commander::showLogWindow()
762 {
763 QRect geom = d->logWindow.frameGeometry();
764 d->logWindow.show();
765 d->logWindow.move(geom.topLeft());
766 QCoreApplication::processEvents();
767 sanitizeWindowPos(&d->logWindow);
768 d->logWindow.raise();
769 }
770
fsOptions()771 void V1541Commander::fsOptions()
772 {
773 MainWindow *w = d->lastActiveWindow;
774 if (!w) return;
775
776 w->fsOptions();
777 }
778
rewriteImage()779 void V1541Commander::rewriteImage()
780 {
781 MainWindow *w = d->lastActiveWindow;
782 if (!w) return;
783
784 w->rewriteImage();
785 }
786
mapToLc()787 void V1541Commander::mapToLc()
788 {
789 MainWindow *w = d->lastActiveWindow;
790 if (!w) return;
791
792 w->mapToLc();
793 }
794
newFile()795 void V1541Commander::newFile()
796 {
797 MainWindow *w = d->lastActiveWindow;
798 if (!w) return;
799
800 w->newFile();
801 }
802
deleteFile()803 void V1541Commander::deleteFile()
804 {
805 MainWindow *w = d->lastActiveWindow;
806 if (!w) return;
807
808 w->deleteFile();
809 }
810
logLineAppended(const QString & line)811 void V1541Commander::logLineAppended(const QString &line)
812 {
813 MainWindow *w = d->lastActiveWindow;
814 if (!w) return;
815
816 w->showStatusLine(line);
817 }
818
newConnection()819 void V1541Commander::newConnection()
820 {
821 QLocalSocket *conn = d->instanceServer.nextPendingConnection();
822 connect(conn, &QIODevice::readyRead, this, &V1541Commander::readyRead);
823 connect(conn, &QLocalSocket::disconnected,
824 this, &V1541Commander::disconnected);
825 QDataStream sendStream(conn);
826 sendStream << applicationPid();
827 conn->flush();
828 }
829
disconnected()830 void V1541Commander::disconnected()
831 {
832 QLocalSocket *sock = qobject_cast<QLocalSocket *>(sender());
833 if (sock)
834 {
835 if (!d->activeClients.contains(sock))
836 {
837 MainWindow *w = d->lastActiveWindow;
838 if (!w) w = d->allWindows.first();
839 if (w)
840 {
841 w->show();
842 w->raise();
843 w->activateWindow();
844 }
845 sock->deleteLater();
846 }
847 }
848 }
849
readyRead()850 void V1541Commander::readyRead()
851 {
852 QLocalSocket *sock = qobject_cast<QLocalSocket *>(sender());
853 if (sock)
854 {
855 d->activeClients.insert(sock);
856 QDataStream recvStream(sock);
857 QString filename;
858 for (;;)
859 {
860 recvStream.startTransaction();
861 recvStream >> filename;
862 if (!recvStream.commitTransaction()) break;
863 open(filename);
864 }
865 d->activeClients.remove(sock);
866 if (sock->state() != QLocalSocket::ConnectedState)
867 {
868 sock->deleteLater();
869 }
870 }
871 }
872
petsciiInput(ushort val)873 void V1541Commander::petsciiInput(ushort val)
874 {
875 PetsciiEdit *pe = qobject_cast<PetsciiEdit *>(focusWidget());
876 if (pe) pe->petsciiInput(val);
877 }
878
c64font() const879 const QFont &V1541Commander::c64font() const
880 {
881 return d->c64font;
882 }
883
menufont() const884 const QFont &V1541Commander::menufont() const
885 {
886 return d->menufont;
887 }
888
statusfont() const889 const QFont &V1541Commander::statusfont() const
890 {
891 return d->statusfont;
892 }
893
statusLedRed() const894 const QPixmap &V1541Commander::statusLedRed() const
895 {
896 return d->statusLedRed;
897 }
898
statusLedYellow() const899 const QPixmap &V1541Commander::statusLedYellow() const
900 {
901 return d->statusLedYellow;
902 }
903
statusLedGreen() const904 const QPixmap &V1541Commander::statusLedGreen() const
905 {
906 return d->statusLedGreen;
907 }
908
newAction()909 QAction &V1541Commander::newAction()
910 {
911 return d->newAction;
912 }
913
openAction()914 QAction &V1541Commander::openAction()
915 {
916 return d->openAction;
917 }
918
saveAction()919 QAction &V1541Commander::saveAction()
920 {
921 return d->saveAction;
922 }
923
saveAsAction()924 QAction &V1541Commander::saveAsAction()
925 {
926 return d->saveAsAction;
927 }
928
exportZipcodeAction()929 QAction &V1541Commander::exportZipcodeAction()
930 {
931 return d->exportZipcodeAction;
932 }
933
exportZipcodeD64Action()934 QAction &V1541Commander::exportZipcodeD64Action()
935 {
936 return d->exportZipcodeD64Action;
937 }
938
exportLynxAction()939 QAction &V1541Commander::exportLynxAction()
940 {
941 return d->exportLynxAction;
942 }
943
closeAction()944 QAction &V1541Commander::closeAction()
945 {
946 return d->closeAction;
947 }
948
settingsAction()949 QAction &V1541Commander::settingsAction()
950 {
951 return d->settingsAction;
952 }
953
aboutAction()954 QAction &V1541Commander::aboutAction()
955 {
956 return d->aboutAction;
957 }
958
exitAction()959 QAction &V1541Commander::exitAction()
960 {
961 return d->exitAction;
962 }
963
petsciiWindowAction()964 QAction &V1541Commander::petsciiWindowAction()
965 {
966 return d->petsciiWindowAction;
967 }
968
logWindowAction()969 QAction &V1541Commander::logWindowAction()
970 {
971 return d->logWindowAction;
972 }
973
fsOptionsAction()974 QAction &V1541Commander::fsOptionsAction()
975 {
976 return d->fsOptionsAction;
977 }
978
rewriteImageAction()979 QAction &V1541Commander::rewriteImageAction()
980 {
981 return d->rewriteImageAction;
982 }
983
autoMapLcAction()984 QAction &V1541Commander::autoMapLcAction()
985 {
986 return d->autoMapLcAction;
987 }
988
mapLcAction()989 QAction &V1541Commander::mapLcAction()
990 {
991 return d->mapLcAction;
992 }
993
newFileAction()994 QAction &V1541Commander::newFileAction()
995 {
996 return d->newFileAction;
997 }
998
deleteFileAction()999 QAction &V1541Commander::deleteFileAction()
1000 {
1001 return d->deleteFileAction;
1002 }
1003
lowerCaseAction()1004 QAction &V1541Commander::lowerCaseAction()
1005 {
1006 return d->lowerCaseAction;
1007 }
1008
instanceServerName() const1009 const QString &V1541Commander::instanceServerName() const
1010 {
1011 return d->instanceServerName;
1012 }
1013
isPrimaryInstance() const1014 bool V1541Commander::isPrimaryInstance() const
1015 {
1016 return d->isPrimaryInstance;
1017 }
1018
settings()1019 Settings &V1541Commander::settings()
1020 {
1021 return d->settings;
1022 }
1023
instance()1024 V1541Commander &V1541Commander::instance()
1025 {
1026 return *static_cast<V1541Commander *>(QCoreApplication::instance());
1027 }
1028