#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "diskimage.h"
#include "folderimage.h"
#include "miscdevices.h"
#include "aspeqtsettings.h"
#include "autobootdialog.h"
#include "autoboot.h"
#include "cassettedialog.h"

#include <QDragEnterEvent>
#include <QDropEvent>
#include <QUrl>
#include <QScrollBar>
#include <QTranslator>

#include "atarifilesystem.h"
#include "miscutils.h"

AspeqtSettings *aspeqtSettings;
MainWindow *mainWindow;

QFile *logFile;
QMutex *logMutex;

// Displayed only in debug mode    "!d"
// Unimportant     (gray)          "!u"
// Normal          (black)         "!n"
// Important       (blue)          "!i"
// Warning         (brown)         "!w"
// Error           (red)           "!e"

void logMessageOutput(QtMsgType type, const char *msg)
{
    logMutex->lock();
    logFile->write(QString::number((quint64)QThread::currentThreadId(), 16).toAscii());
    switch (type) {
        case QtDebugMsg:
            logFile->write(": [Debug]    ");
            break;
        case QtWarningMsg:
            logFile->write(": [Warning]  ");
            break;
        case QtCriticalMsg:
            logFile->write(": [Critical] ");
            break;
        case QtFatalMsg:
            logFile->write(": [Fatal]    ");
            break;
    }
    logFile->write(msg);
    logFile->write("\n");
    if (type == QtFatalMsg) {
        logFile->close();
        abort();
    }
    logMutex->unlock();

    if (msg[0] == '!') {
#ifdef QT_NO_DEBUG
        if (msg[1] == 'd') {
            return;
        }
#endif
        mainWindow->doLogMessage(msg[1], QString::fromLocal8Bit(msg+3));
    }
}

void MainWindow::doLogMessage(int type, const QString &msg)
{
    emit logMessage(type, msg);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    /* Setup the logging system */
    mainWindow = this;
    logFile = new QFile(QDir::temp().absoluteFilePath("aspeqt.log"));
    logFile->open(QFile::WriteOnly | QFile::Truncate | QFile::Unbuffered | QFile::Text);
    logMutex = new QMutex();
    connect(this, SIGNAL(logMessage(int,QString)), this, SLOT(uiMessage(int,QString)), Qt::QueuedConnection);
    qInstallMsgHandler(logMessageOutput);
    qDebug() << "!d" << tr("AspeQt started at %1.").arg(QDateTime::currentDateTime().toString());

    /* Remove old temporaries */
    QDir tempDir = QDir::temp();
    QStringList filters;
    filters << "aspeqt-*";
    QFileInfoList list = tempDir.entryInfoList(filters, QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);
    foreach(QFileInfo file, list) {
        deltree(file.absoluteFilePath());
    }
    
    /* Set application properties */
    QCoreApplication::setOrganizationName("TrayGun Software");
    QCoreApplication::setApplicationName("AspeQt");
    aspeqtSettings = new AspeqtSettings();
    
    /* load translators */
    loadTranslators();
    
    ui->setupUi(this);

    speedLabel = new QLabel(this);
    onOffLabel = new QLabel(this);
    clearMessagesLabel = new QLabel(this);

    speedLabel->setText(tr("19200 kbps"));

    onOffLabel->setMinimumWidth(21);

    clearMessagesLabel->setMinimumWidth(21);
    clearMessagesLabel->setPixmap(QIcon(":/icons/silk-icons/icons/page_white.png").pixmap(16, 16, QIcon::Normal));
    clearMessagesLabel->setToolTip(tr("Clear messages"));
    clearMessagesLabel->setStatusTip(clearMessagesLabel->toolTip());

    speedLabel->setMinimumWidth(80);

    ui->statusBar->addPermanentWidget(speedLabel);
    ui->statusBar->addPermanentWidget(onOffLabel);
    ui->statusBar->addPermanentWidget(clearMessagesLabel);

    /* Set tool button actions */
    ui->buttonMountDisk_1->setDefaultAction(ui->actionMountDisk_1);
    ui->buttonMountDisk_2->setDefaultAction(ui->actionMountDisk_2);
    ui->buttonMountDisk_3->setDefaultAction(ui->actionMountDisk_3);
    ui->buttonMountDisk_4->setDefaultAction(ui->actionMountDisk_4);
    ui->buttonMountDisk_5->setDefaultAction(ui->actionMountDisk_5);
    ui->buttonMountDisk_6->setDefaultAction(ui->actionMountDisk_6);
    ui->buttonMountDisk_7->setDefaultAction(ui->actionMountDisk_7);
    ui->buttonMountDisk_8->setDefaultAction(ui->actionMountDisk_8);
    ui->buttonMountFolder_1->setDefaultAction(ui->actionMountFolder_1);
    ui->buttonMountFolder_2->setDefaultAction(ui->actionMountFolder_2);
    ui->buttonMountFolder_3->setDefaultAction(ui->actionMountFolder_3);
    ui->buttonMountFolder_4->setDefaultAction(ui->actionMountFolder_4);
    ui->buttonMountFolder_5->setDefaultAction(ui->actionMountFolder_5);
    ui->buttonMountFolder_6->setDefaultAction(ui->actionMountFolder_6);
    ui->buttonMountFolder_7->setDefaultAction(ui->actionMountFolder_7);
    ui->buttonMountFolder_8->setDefaultAction(ui->actionMountFolder_8);
    ui->buttonEject_1->setDefaultAction(ui->actionEject_1);
    ui->buttonEject_2->setDefaultAction(ui->actionEject_2);
    ui->buttonEject_3->setDefaultAction(ui->actionEject_3);
    ui->buttonEject_4->setDefaultAction(ui->actionEject_4);
    ui->buttonEject_5->setDefaultAction(ui->actionEject_5);
    ui->buttonEject_6->setDefaultAction(ui->actionEject_6);
    ui->buttonEject_7->setDefaultAction(ui->actionEject_7);
    ui->buttonEject_8->setDefaultAction(ui->actionEject_8);
    ui->buttonSave_1->setDefaultAction(ui->actionSave_1);
    ui->buttonSave_2->setDefaultAction(ui->actionSave_2);
    ui->buttonSave_3->setDefaultAction(ui->actionSave_3);
    ui->buttonSave_4->setDefaultAction(ui->actionSave_4);
    ui->buttonSave_5->setDefaultAction(ui->actionSave_5);
    ui->buttonSave_6->setDefaultAction(ui->actionSave_6);
    ui->buttonSave_7->setDefaultAction(ui->actionSave_7);
    ui->buttonSave_8->setDefaultAction(ui->actionSave_8);
    ui->buttonEditDisk_1->setDefaultAction(ui->actionEditDisk_1);
    ui->buttonEditDisk_2->setDefaultAction(ui->actionEditDisk_2);
    ui->buttonEditDisk_3->setDefaultAction(ui->actionEditDisk_3);
    ui->buttonEditDisk_4->setDefaultAction(ui->actionEditDisk_4);
    ui->buttonEditDisk_5->setDefaultAction(ui->actionEditDisk_5);
    ui->buttonEditDisk_6->setDefaultAction(ui->actionEditDisk_6);
    ui->buttonEditDisk_7->setDefaultAction(ui->actionEditDisk_7);
    ui->buttonEditDisk_8->setDefaultAction(ui->actionEditDisk_8);

    /* Initialize disk widget array */
    diskWidgets[0].fileNameLabel = ui->labelFileName_1;
    diskWidgets[1].fileNameLabel = ui->labelFileName_2;
    diskWidgets[2].fileNameLabel = ui->labelFileName_3;
    diskWidgets[3].fileNameLabel = ui->labelFileName_4;
    diskWidgets[4].fileNameLabel = ui->labelFileName_5;
    diskWidgets[5].fileNameLabel = ui->labelFileName_6;
    diskWidgets[6].fileNameLabel = ui->labelFileName_7;
    diskWidgets[7].fileNameLabel = ui->labelFileName_8;

    diskWidgets[0].imagePropertiesLabel = ui->labelImageProperties_1;
    diskWidgets[1].imagePropertiesLabel = ui->labelImageProperties_2;
    diskWidgets[2].imagePropertiesLabel = ui->labelImageProperties_3;
    diskWidgets[3].imagePropertiesLabel = ui->labelImageProperties_4;
    diskWidgets[4].imagePropertiesLabel = ui->labelImageProperties_5;
    diskWidgets[5].imagePropertiesLabel = ui->labelImageProperties_6;
    diskWidgets[6].imagePropertiesLabel = ui->labelImageProperties_7;
    diskWidgets[7].imagePropertiesLabel = ui->labelImageProperties_8;

    diskWidgets[0].ejectAction = ui->actionEject_1;
    diskWidgets[1].ejectAction = ui->actionEject_2;
    diskWidgets[2].ejectAction = ui->actionEject_3;
    diskWidgets[3].ejectAction = ui->actionEject_4;
    diskWidgets[4].ejectAction = ui->actionEject_5;
    diskWidgets[5].ejectAction = ui->actionEject_6;
    diskWidgets[6].ejectAction = ui->actionEject_7;
    diskWidgets[7].ejectAction = ui->actionEject_8;

    diskWidgets[0].writeProtectAction = ui->actionWriteProtect_1;
    diskWidgets[1].writeProtectAction = ui->actionWriteProtect_2;
    diskWidgets[2].writeProtectAction = ui->actionWriteProtect_3;
    diskWidgets[3].writeProtectAction = ui->actionWriteProtect_4;
    diskWidgets[4].writeProtectAction = ui->actionWriteProtect_5;
    diskWidgets[5].writeProtectAction = ui->actionWriteProtect_6;
    diskWidgets[6].writeProtectAction = ui->actionWriteProtect_7;
    diskWidgets[7].writeProtectAction = ui->actionWriteProtect_8;

    diskWidgets[0].editAction = ui->actionEditDisk_1;
    diskWidgets[1].editAction = ui->actionEditDisk_2;
    diskWidgets[2].editAction = ui->actionEditDisk_3;
    diskWidgets[3].editAction = ui->actionEditDisk_4;
    diskWidgets[4].editAction = ui->actionEditDisk_5;
    diskWidgets[5].editAction = ui->actionEditDisk_6;
    diskWidgets[6].editAction = ui->actionEditDisk_7;
    diskWidgets[7].editAction = ui->actionEditDisk_8;

    diskWidgets[0].mountDiskAction = ui->actionMountDisk_1;
    diskWidgets[1].mountDiskAction = ui->actionMountDisk_2;
    diskWidgets[2].mountDiskAction = ui->actionMountDisk_3;
    diskWidgets[3].mountDiskAction = ui->actionMountDisk_4;
    diskWidgets[4].mountDiskAction = ui->actionMountDisk_5;
    diskWidgets[5].mountDiskAction = ui->actionMountDisk_6;
    diskWidgets[6].mountDiskAction = ui->actionMountDisk_7;
    diskWidgets[7].mountDiskAction = ui->actionMountDisk_8;

    diskWidgets[0].mountFolderAction = ui->actionMountFolder_1;
    diskWidgets[1].mountFolderAction = ui->actionMountFolder_2;
    diskWidgets[2].mountFolderAction = ui->actionMountFolder_3;
    diskWidgets[3].mountFolderAction = ui->actionMountFolder_4;
    diskWidgets[4].mountFolderAction = ui->actionMountFolder_5;
    diskWidgets[5].mountFolderAction = ui->actionMountFolder_6;
    diskWidgets[6].mountFolderAction = ui->actionMountFolder_7;
    diskWidgets[7].mountFolderAction = ui->actionMountFolder_8;

    diskWidgets[0].saveAction = ui->actionSave_1;
    diskWidgets[1].saveAction = ui->actionSave_2;
    diskWidgets[2].saveAction = ui->actionSave_3;
    diskWidgets[3].saveAction = ui->actionSave_4;
    diskWidgets[4].saveAction = ui->actionSave_5;
    diskWidgets[5].saveAction = ui->actionSave_6;
    diskWidgets[6].saveAction = ui->actionSave_7;
    diskWidgets[7].saveAction = ui->actionSave_8;

    diskWidgets[0].revertAction = ui->actionRevert_1;
    diskWidgets[1].revertAction = ui->actionRevert_2;
    diskWidgets[2].revertAction = ui->actionRevert_3;
    diskWidgets[3].revertAction = ui->actionRevert_4;
    diskWidgets[4].revertAction = ui->actionRevert_5;
    diskWidgets[5].revertAction = ui->actionRevert_6;
    diskWidgets[6].revertAction = ui->actionRevert_7;
    diskWidgets[7].revertAction = ui->actionRevert_8;

    diskWidgets[0].saveAsAction = ui->actionSaveAs_1;
    diskWidgets[1].saveAsAction = ui->actionSaveAs_2;
    diskWidgets[2].saveAsAction = ui->actionSaveAs_3;
    diskWidgets[3].saveAsAction = ui->actionSaveAs_4;
    diskWidgets[4].saveAsAction = ui->actionSaveAs_5;
    diskWidgets[5].saveAsAction = ui->actionSaveAs_6;
    diskWidgets[6].saveAsAction = ui->actionSaveAs_7;
    diskWidgets[7].saveAsAction = ui->actionSaveAs_8;

    diskWidgets[0].frame = ui->horizontalFrame_1;
    diskWidgets[1].frame = ui->horizontalFrame_2;
    diskWidgets[2].frame = ui->horizontalFrame_3;
    diskWidgets[3].frame = ui->horizontalFrame_4;
    diskWidgets[4].frame = ui->horizontalFrame_5;
    diskWidgets[5].frame = ui->horizontalFrame_6;
    diskWidgets[6].frame = ui->horizontalFrame_7;
    diskWidgets[7].frame = ui->horizontalFrame_8;

    for (int i = 0; i < 8; i++) {
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].saveAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].saveAsAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].revertAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].mountDiskAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].mountFolderAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].ejectAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].writeProtectAction);
        diskWidgets[i].frame->insertAction(0, diskWidgets[i].editAction);
    }

    /* Connect SioWorker signals */
    sio = new SioWorker();
    connect(sio, SIGNAL(started()), this, SLOT(sioStarted()));
    connect(sio, SIGNAL(finished()), this, SLOT(sioFinished()));
    connect(sio, SIGNAL(statusChanged(QString)), this, SLOT(sioStatusChanged(QString)));
    shownFirstTime = true;

    /* Restore application state */
    for (int i = 0; i < 8; i++) {
        AspeqtSettings::ImageSettings is;
        is = aspeqtSettings->mountedImageSetting(i);
        mountFile(i, is.fileName, is.isWriteProtected);
    }
    updateRecentFileActions();

    setAcceptDrops(true);

    ApeTime *ape = new ApeTime(sio);
    sio->installDevice(0x45, ape);

    textPrinterWindow = new TextPrinterWindow();

    connect(textPrinterWindow, SIGNAL(closed()), this, SLOT(textPrinterWindowClosed()));

    Printer *printer = new Printer(sio);
    connect(printer, SIGNAL(print(QString)), textPrinterWindow, SLOT(print(QString)));
    sio->installDevice(0x40, printer);
    untitledName = 0;
}

MainWindow::~MainWindow()
{
    if (ui->actionStartEmulation->isChecked()) {
        ui->actionStartEmulation->trigger();
    }

    delete aspeqtSettings;
    delete sio;

    delete ui;

    qDebug() << "!d" << tr("AspeQt stopped at %1.").arg(QDateTime::currentDateTime().toString());
    qInstallMsgHandler(0);
    delete logMutex;
    delete logFile;
}

 void MainWindow::mousePressEvent(QMouseEvent *event)
 {
     int slot = containingDiskSlot(event->pos());

     if (event->button() == Qt::LeftButton
         && slot >= 0) {

         QDrag *drag = new QDrag(this);
         QMimeData *mimeData = new QMimeData;

         mimeData->setData("application/x-aspeqt-disk-image", QByteArray(1, slot));
         drag->setMimeData(mimeData);

         drag->exec();
     }

     if (event->button() == Qt::LeftButton && onOffLabel->geometry().translated(ui->statusBar->geometry().topLeft()).contains(event->pos())) {
         ui->actionStartEmulation->trigger();
     }

      if (event->button() == Qt::LeftButton && clearMessagesLabel->geometry().translated(ui->statusBar->geometry().topLeft()).contains(event->pos())) {
         ui->textEdit->clear();
     }
}

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
    int i = containingDiskSlot(event->pos());
    if (i >= 0 && (event->mimeData()->hasUrls() ||
                   event->mimeData()->hasFormat("application/x-aspeqt-disk-image"))) {
        event->acceptProposedAction();
    } else {
        i = -1;
    }
    for (int j = 0; j <= 7; j++) {
        if (i == j) {
            diskWidgets[j].frame->setFrameShadow(QFrame::Sunken);
        } else {
            diskWidgets[j].frame->setFrameShadow(QFrame::Raised);
        }
    }
}

void MainWindow::dragMoveEvent(QDragMoveEvent *event)
{
    int i = containingDiskSlot(event->pos());
    if (i >= 0 && (event->mimeData()->hasUrls() ||
                   event->mimeData()->hasFormat("application/x-aspeqt-disk-image"))) {
        event->acceptProposedAction();
    } else {
        i = -1;
    }
    for (int j = 0; j <= 7; j++) {
        if (i == j) {
            diskWidgets[j].frame->setFrameShadow(QFrame::Sunken);
        } else {
            diskWidgets[j].frame->setFrameShadow(QFrame::Raised);
        }
    }
}

void MainWindow::dragLeaveEvent(QDragLeaveEvent *)
{
    for (int j = 0; j <= 7; j++) {
        diskWidgets[j].frame->setFrameShadow(QFrame::Raised);
    }
}

void MainWindow::dropEvent(QDropEvent *event)
{
    for (int j = 0; j <= 7; j++) {
        diskWidgets[j].frame->setFrameShadow(QFrame::Raised);
    }
    int slot = containingDiskSlot(event->pos());
    if (!(event->mimeData()->hasUrls() ||
          event->mimeData()->hasFormat("application/x-aspeqt-disk-image")) ||
          slot < 0) {
        return;
    }

    if (event->mimeData()->hasFormat("application/x-aspeqt-disk-image")) {
        int source = event->mimeData()->data("application/x-aspeqt-disk-image").at(0);

        if (slot == source) {
            return;
        }

        sio->swapDevices(slot + 0x31, source + 0x31);

        aspeqtSettings->swapImages(slot, source);

        qDebug() << "!n" << tr("Swapped disk %1 with disk %2.").arg(slot + 1).arg(source + 1);

        return;
    }

    QStringList files;
    foreach (QUrl url, event->mimeData()->urls()) {
        if (!url.toLocalFile().isEmpty()) {
            files.append(url.toLocalFile());
        }
    }
    if (files.isEmpty()) {
        return;
    }

    if (files.at(0).endsWith(".XEX", Qt::CaseInsensitive) ||
            files.at(0).endsWith(".EXE", Qt::CaseInsensitive) ||
            files.at(0).endsWith(".COM", Qt::CaseInsensitive)) {
        bootExe(files.at(0));
        return;
    }

    if (files.at(0).endsWith(".CAS", Qt::CaseInsensitive)) {
        bool restart;
        restart = ui->actionStartEmulation->isChecked();
        if (restart) {
            ui->actionStartEmulation->trigger();
            sio->wait();
            qApp->processEvents();
        }

        CassetteDialog *dlg = new CassetteDialog(this, files.at(0));
        dlg->exec();
        delete dlg;

        if (restart) {
            ui->actionStartEmulation->trigger();
        }
        return;
    }

    mountFileWithDefaultProtection(slot, files[0]);
    files.removeAt(0);
    while (!files.isEmpty() && (slot = firstEmptyDiskSlot(slot, false)) >= 0) {
        mountFileWithDefaultProtection(slot, files[0]);
        files.removeAt(0);
    }
    slot = 0;
    while (!files.isEmpty() && (slot = firstEmptyDiskSlot(slot, false)) >= 0) {
        mountFileWithDefaultProtection(slot, files[0]);
        files.removeAt(0);
    }
    foreach(QString file, files) {
        qCritical() << "!e" << tr("Cannot mount '%1': No empty disk slots.").arg(file);
    }
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    bool wasRunning = ui->actionStartEmulation->isChecked();
    QMessageBox::StandardButton answer = QMessageBox::No;

    if (wasRunning) {
        ui->actionStartEmulation->trigger();
    }

    int toBeSaved = 0;

    for (int i = 0; i < 8; i++) {
        SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(i + 0x31));
        if (img && img->isModified()) {
            toBeSaved++;
        }
    }

    for (int i = 0; i < 8; i++) {
        SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(i + 0x31));
        if (img && img->isModified()) {
            toBeSaved--;
            answer = saveImageWhenClosing(i, answer, toBeSaved);
            if (answer == QMessageBox::NoToAll) {
                break;
            }
            if (answer == QMessageBox::Cancel) {
                if (wasRunning) {
                    ui->actionStartEmulation->trigger();
                }
                event->ignore();
                return;
            }
        }
    }

    delete textPrinterWindow;

    for (int i = 0x31; i < 0x39; i++) {
        SimpleDiskImage *s = qobject_cast <SimpleDiskImage*> (sio->getDevice(i));
        if (s) {
            s->close();
        }
    }

    event->accept();
}

void MainWindow::show()
{
    QMainWindow::show();
    if (shownFirstTime) {
        /* Open options dialog if it's the first time */
        if (aspeqtSettings->isFirstTime()) {
            if (QMessageBox::Yes == QMessageBox::question(this, tr("First run"),
                                       tr("You are running AspeQt for the first time.\n\nDo you want to open the options dialog?"),
                                       QMessageBox::Yes, QMessageBox::No)) {
                ui->actionOptions->trigger();
            }
        }
        qDebug() << "!d" << "Starting emulation";
        ui->actionStartEmulation->trigger();
    }
}

void MainWindow::on_actionStartEmulation_triggered()
{
    if (ui->actionStartEmulation->isChecked()) {
        sio->start(QThread::TimeCriticalPriority);
    } else {
        sio->setPriority(QThread::NormalPriority);
        sio->wait();
        qApp->processEvents();
    }
}

void MainWindow::sioStarted()
{
    ui->actionStartEmulation->setText(tr("&Stop emulation"));
    ui->actionStartEmulation->setToolTip(tr("Stop SIO peripheral emulation"));
    ui->actionStartEmulation->setStatusTip(tr("Stop SIO peripheral emulation"));
    onOffLabel->setPixmap(ui->actionStartEmulation->icon().pixmap(16, QIcon::Normal, QIcon::On));
    onOffLabel->setToolTip(ui->actionStartEmulation->toolTip());
    onOffLabel->setStatusTip(ui->actionStartEmulation->statusTip());
}

void MainWindow::sioFinished()
{
    ui->actionStartEmulation->setText(tr("&Start emulation"));
    ui->actionStartEmulation->setToolTip(tr("Start SIO peripheral emulation"));
    ui->actionStartEmulation->setStatusTip(tr("Start SIO peripheral emulation"));
    ui->actionStartEmulation->setChecked(false);
    onOffLabel->setPixmap(ui->actionStartEmulation->icon().pixmap(16, QIcon::Normal, QIcon::Off));
    onOffLabel->setToolTip(ui->actionStartEmulation->toolTip());
    onOffLabel->setStatusTip(ui->actionStartEmulation->statusTip());
    speedLabel->hide();
    speedLabel->clear();
    qWarning() << "!i" << tr("Emulation stopped.");
}

void MainWindow::sioStatusChanged(QString status)
{
    speedLabel->setText(status);
    speedLabel->show();
}

void MainWindow::deviceStatusChanged(int deviceNo)
{
    if (deviceNo >= 0x31 && deviceNo <= 0x38) {
        SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(deviceNo));
        if (img) {
            diskWidgets[deviceNo - 0x31].fileNameLabel->setText(img->originalFileName());
            diskWidgets[deviceNo - 0x31].imagePropertiesLabel->setText(img->description());
            diskWidgets[deviceNo - 0x31].ejectAction->setEnabled(true);
            diskWidgets[deviceNo - 0x31].editAction->setChecked(img->editDialog() != 0);
            if (img->description() == tr("Folder image")) {
                diskWidgets[deviceNo - 0x31].editAction->setEnabled(false);
                diskWidgets[deviceNo - 0x31].saveAsAction->setEnabled(false);
                diskWidgets[deviceNo - 0x31].saveAction->setEnabled(false);
                diskWidgets[deviceNo - 0x31].revertAction->setEnabled(false);
            } else {
                diskWidgets[deviceNo - 0x31].editAction->setEnabled(true);
                diskWidgets[deviceNo - 0x31].saveAsAction->setEnabled(true);
                if (img->isModified()) {
                    diskWidgets[deviceNo - 0x31].saveAction->setEnabled(true);
                    diskWidgets[deviceNo - 0x31].revertAction->setEnabled(true);
                } else {
                    diskWidgets[deviceNo - 0x31].saveAction->setEnabled(false);
                    diskWidgets[deviceNo - 0x31].revertAction->setEnabled(false);
                }
            }
        } else {
            diskWidgets[deviceNo - 0x31].saveAction->setEnabled(false);
            diskWidgets[deviceNo - 0x31].fileNameLabel->clear();
            diskWidgets[deviceNo - 0x31].imagePropertiesLabel->clear();
            diskWidgets[deviceNo - 0x31].ejectAction->setEnabled(false);
            diskWidgets[deviceNo - 0x31].revertAction->setEnabled(false);
            diskWidgets[deviceNo - 0x31].saveAsAction->setEnabled(false);
            diskWidgets[deviceNo - 0x31].editAction->setEnabled(false);
            diskWidgets[deviceNo - 0x31].editAction->setChecked(false);
        }
    }
}

void MainWindow::uiMessage(int t, QString message)
{
    if (message.at(0) == '"') {
        message.remove(0, 1);
    }
    if (message.at(message.count() - 1) == ' ' && message.at(message.count() - 2) == '"') {
        message.resize(message.count() - 2);
    }
    switch (t) {
        case 'd':
            ui->textEdit->append(QString("<span style='color:green'>%1</span>").arg(message));
            break;
        case 'u':
            ui->textEdit->append(QString("<span style='color:gray'>%1</span>").arg(message));
            break;
        case 'n':
            ui->textEdit->append(QString("<span style='color:black'>%1</span>").arg(message));
            break;
        case 'i':
            ui->textEdit->append(QString("<span style='color:blue'>%1</span>").arg(message));
            break;
        case 'w':
            ui->textEdit->append(QString("<span style='color:brown'>%1</span>").arg(message));
            break;
        case 'e':
            ui->textEdit->append(QString("<span style='color:red'>%1</span>").arg(message));
            break;
        default:
            ui->textEdit->append(QString("<span style='color:purple'>%1</span>").arg(message));
            break;
    }

    ui->statusBar->showMessage(message, 3000);
    ui->textEdit->verticalScrollBar()->setSliderPosition(ui->textEdit->verticalScrollBar()->maximum());
    ui->textEdit->horizontalScrollBar()->setSliderPosition(ui->textEdit->horizontalScrollBar()->minimum());
}

void MainWindow::on_actionOptions_triggered()
{
    bool restart;
    restart = ui->actionStartEmulation->isChecked();
    if (restart) {
        ui->actionStartEmulation->trigger();
        sio->wait();
        qApp->processEvents();
    }
    OptionsDialog optionsDialog(this);
    optionsDialog.exec();

    // load translators and retranslate
    loadTranslators();
    ui->retranslateUi(this);
    for (int i = 0x31; i <= 0x38; i++) {
        deviceStatusChanged(i);
    }
    
    ui->actionStartEmulation->trigger();
}

void MainWindow::on_actionAbout_triggered()
{
    AboutDialog aboutDialog(this, VERSION);
    aboutDialog.exec();
}

void MainWindow::updateRecentFileActions()
{
    ui->actionMountRecent_0->setText(aspeqtSettings->recentImageSetting(0).fileName);
    ui->actionMountRecent_1->setText(aspeqtSettings->recentImageSetting(1).fileName);
    ui->actionMountRecent_2->setText(aspeqtSettings->recentImageSetting(2).fileName);
    ui->actionMountRecent_3->setText(aspeqtSettings->recentImageSetting(3).fileName);
    ui->actionMountRecent_4->setText(aspeqtSettings->recentImageSetting(4).fileName);
    ui->actionMountRecent_5->setText(aspeqtSettings->recentImageSetting(5).fileName);
    ui->actionMountRecent_6->setText(aspeqtSettings->recentImageSetting(6).fileName);
    ui->actionMountRecent_7->setText(aspeqtSettings->recentImageSetting(7).fileName);
    ui->actionMountRecent_8->setText(aspeqtSettings->recentImageSetting(8).fileName);
    ui->actionMountRecent_9->setText(aspeqtSettings->recentImageSetting(9).fileName);

    ui->actionMountRecent_0->setVisible(!ui->actionMountRecent_0->text().isEmpty());
    ui->actionMountRecent_1->setVisible(!ui->actionMountRecent_1->text().isEmpty());
    ui->actionMountRecent_2->setVisible(!ui->actionMountRecent_2->text().isEmpty());
    ui->actionMountRecent_3->setVisible(!ui->actionMountRecent_3->text().isEmpty());
    ui->actionMountRecent_4->setVisible(!ui->actionMountRecent_4->text().isEmpty());
    ui->actionMountRecent_5->setVisible(!ui->actionMountRecent_5->text().isEmpty());
    ui->actionMountRecent_6->setVisible(!ui->actionMountRecent_6->text().isEmpty());
    ui->actionMountRecent_7->setVisible(!ui->actionMountRecent_7->text().isEmpty());
    ui->actionMountRecent_8->setVisible(!ui->actionMountRecent_8->text().isEmpty());
    ui->actionMountRecent_9->setVisible(!ui->actionMountRecent_9->text().isEmpty());
}


bool MainWindow::ejectImage(int no, bool ask)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));

    if (ask && img && img->isModified()) {
        QMessageBox::StandardButton answer;
        answer = saveImageWhenClosing(no, QMessageBox::No, 0);
        if (answer == QMessageBox::Cancel) {
            return false;
        }
    }

    sio->uninstallDevice(no + 0x31);
    if (!img) {
        return true;
    }
    delete img;
    diskWidgets[no].ejectAction->setEnabled(false);
    QString fileName = diskWidgets[no].fileNameLabel->text();
    diskWidgets[no].fileNameLabel->clear();
    diskWidgets[no].writeProtectAction->setChecked(false);
    diskWidgets[no].writeProtectAction->setEnabled(false);
    diskWidgets[no].editAction->setEnabled(false);

    aspeqtSettings->unmountImage(no);
    updateRecentFileActions();
    deviceStatusChanged(no + 0x31);
    qDebug() << "!n" << tr("Unmounted disk %1").arg(no + 1);
    return true;
}

int MainWindow::containingDiskSlot(const QPoint &point)
{
    int i;
    QPoint distance = centralWidget()->geometry().topLeft();
    for (i=0; i < 8; i++) {
        QRect rect = diskWidgets[i].frame->geometry().translated(distance);
        if (rect.contains(point)) {
            break;
        }
    }
    if (i > 7) {
        i = -1;
    }
    return i;
}

int MainWindow::firstEmptyDiskSlot(int startFrom, bool createOne)
{
    int i;
    for (i = startFrom; i < 8; i++) {
        if (!sio->getDevice(0x31 + i)) {
            break;
        }
    }
    if (i > 7) {
        if (createOne) {
            i = 7;
        } else {
            i = -1;
        }
    }
    return i;
}

void MainWindow::bootExe(const QString &fileName)
{
    SioDevice *old = sio->getDevice(0x31);
    AutoBoot loader(sio, old);
    AutoBootDialog dlg(this);

    if (!loader.open(fileName, aspeqtSettings->useHighSpeedExeLoader())) {
        return;
    }

    sio->uninstallDevice(0x31);
    sio->installDevice(0x31, &loader);
    connect(&loader, SIGNAL(booterStarted()), &dlg, SLOT(booterStarted()));
    connect(&loader, SIGNAL(booterLoaded()), &dlg, SLOT(booterLoaded()));
    connect(&loader, SIGNAL(blockRead(int, int)), &dlg, SLOT(blockRead(int, int)));
    connect(&loader, SIGNAL(loaderDone()), &dlg, SLOT(loaderDone()));
    dlg.exec();
    sio->uninstallDevice(0x31);
    if (old) {
        sio->installDevice(0x31, old);
        SimpleDiskImage *d = qobject_cast <SimpleDiskImage*> (old);
        d = qobject_cast <SimpleDiskImage*> (sio->getDevice(0x31));
    }
}

void MainWindow::mountFileWithDefaultProtection(int no, const QString &fileName)
{
    bool prot = aspeqtSettings->getImageSettingsFromName(fileName).isWriteProtected;
    mountFile(no, fileName, prot);
}

void MainWindow::mountFile(int no, const QString &fileName, bool /*prot*/)
{
    SimpleDiskImage *disk;
    bool isDir = false;

    if (fileName.isEmpty()) {
        return;
    } else if (QFileInfo(fileName).isDir()) {
        disk = new FolderImage(sio);
        isDir = true;
    } else {
        disk = new SimpleDiskImage(sio);
    }

    if (disk) {
        if (!disk->open(fileName)) {
            aspeqtSettings->unmountImage(no);
            delete disk;
            return;
        }
        if (!ejectImage(no)) {
            aspeqtSettings->unmountImage(no);
            delete disk;
            return;
        }

        sio->installDevice(0x31 + no, disk);
        diskWidgets[no].ejectAction->setEnabled(true);
        diskWidgets[no].editAction->setEnabled(true);
        diskWidgets[no].writeProtectAction->setChecked(disk->isReadOnly());
        diskWidgets[no].writeProtectAction->setEnabled(!disk->isUnmodifiable());

        diskWidgets[no].fileNameLabel->setText(fileName);
        aspeqtSettings->mountImage(no, fileName, disk->isReadOnly());
        updateRecentFileActions();
        connect(disk, SIGNAL(statusChanged(int)), this, SLOT(deviceStatusChanged(int)), Qt::QueuedConnection);
        deviceStatusChanged(0x31 + no);
        qDebug() << "!n" << tr("[%1] Mounted '%2' as '%3'.")
                .arg(disk->deviceName())
                .arg(fileName)
                .arg(disk->description());
    }
}

void MainWindow::mountDiskImage(int no)
{
    QString dir;
    if (diskWidgets[no].fileNameLabel->text().isEmpty()) {
        dir = aspeqtSettings->lastDiskImageDir();
    } else {
        dir = QFileInfo(diskWidgets[no].fileNameLabel->text()).absolutePath();
    }
    QString fileName = QFileDialog::getOpenFileName(this,
                                                    tr("Open a disk image"),
                                                    dir,
                                                    tr(
                                                    "All Atari disk images (*.atr *.xfd);;"
                                                    "SIO2PC ATR images (*.atr);;"
                                                    "XFormer XFD images (*.xfd);;"
                                                    "All files (*)"));
    if (fileName.isEmpty()) {
        return;
    }
    aspeqtSettings->setLastDiskImageDir(QFileInfo(fileName).absolutePath());
    mountFileWithDefaultProtection(no, fileName);
}

void MainWindow::mountFolderImage(int no)
{
    QString dir;
    if (diskWidgets[no].fileNameLabel->text().isEmpty()) {
        dir = aspeqtSettings->lastFolderImageDir();
    } else {
        dir = diskWidgets[no].fileNameLabel->text();
    }
    QString fileName = QFileDialog::getExistingDirectory(this, tr("Open a folder image"), dir);
    if (fileName.isEmpty()) {
        return;
    }
    aspeqtSettings->setLastFolderImageDir(fileName);
    mountFileWithDefaultProtection(no, fileName);
}

void MainWindow::toggleWriteProtection(int no)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));
    if (diskWidgets[no].writeProtectAction->isChecked()) {
        img->setReadOnly(true);
    } else {
        img->setReadOnly(false);
    }
    aspeqtSettings->setMountedImageSetting(no, diskWidgets[no].fileNameLabel->text(), diskWidgets[no].writeProtectAction->isChecked());
}

void MainWindow::openEditor(int no)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));
    if (img->editDialog()) {
        img->editDialog()->close();
    } else {
        DiskEditDialog *dlg = new DiskEditDialog();
        dlg->go(img);
        dlg->show();
    }
}

QMessageBox::StandardButton MainWindow::saveImageWhenClosing(int no, QMessageBox::StandardButton previousAnswer, int number)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));

    if (previousAnswer != QMessageBox::YesToAll) {
        QMessageBox::StandardButtons buttons;
        if (number) {
            buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::YesToAll | QMessageBox::NoToAll | QMessageBox::Cancel;
        } else {
            buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel;
        }
        previousAnswer = QMessageBox::question(this, tr("Image file unsaved"), tr("'%1' has unsaved changes, do you want to save it?")
                                       .arg(img->originalFileName()), buttons);
    }
    if (previousAnswer == QMessageBox::Yes || previousAnswer == QMessageBox::YesToAll) {
        saveDisk(no);
    }
    if (previousAnswer == QMessageBox::Close) {
        previousAnswer = QMessageBox::Cancel;
    }
    return previousAnswer;
}

void MainWindow::loadTranslators()
{
    qApp->removeTranslator(&aspeqt_qt_translator);
    qApp->removeTranslator(&aspeqt_translator);
    if (aspeqtSettings->i18nLanguage().compare("auto") == 0) {
	QString locale = QLocale::system().name();
        aspeqt_translator.load(":/translations/i18n/aspeqt_" + locale);
        aspeqt_qt_translator.load(":/translations/i18n/qt_" + locale);
	qApp->installTranslator(&aspeqt_qt_translator);
	qApp->installTranslator(&aspeqt_translator);
    } else if (aspeqtSettings->i18nLanguage().compare("en") != 0) {
        aspeqt_translator.load(":/translations/i18n/aspeqt_" + aspeqtSettings->i18nLanguage());
        aspeqt_qt_translator.load(":/translations/i18n/qt_" + aspeqtSettings->i18nLanguage());
	qApp->installTranslator(&aspeqt_qt_translator);
	qApp->installTranslator(&aspeqt_translator);
    }
}

void MainWindow::saveDisk(int no)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));

    if (img->isUnnamed()) {
        saveDiskAs(no);
        return;
    }

    bool saved;

    img->lock();
    saved = img->save();
    img->unlock();
    if (!saved) {
        if (QMessageBox::question(this, tr("Save failed"), tr("'%1' cannot be saved, do you want to save the image with another name?")
            .arg(img->originalFileName()), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
            saveDiskAs(no);
        }
    }
}

void MainWindow::saveDiskAs(int no)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));
    QString dir, fileName;
    bool saved = false;

    if (img->isUnnamed()) {
        dir = aspeqtSettings->lastDiskImageDir();
    } else {
        dir = QFileInfo(img->originalFileName()).absolutePath();
    }

    do {
        fileName = QFileDialog::getSaveFileName(this, tr("Save image as"),
                                 dir,
                                 tr(
                                         "All Atari disk images (*.atr *.xfd);;"
                                         "SIO2PC ATR images (*.atr);;"
                                         "XFormer XFD images (*.xfd);;"
                                         "All files (*)"));
        if (fileName.isEmpty()) {
            return;
        }

        img->lock();
        saved = img->saveAs(fileName);
        img->unlock();

        if (!saved) {
            if (QMessageBox::question(this, tr("Save failed"), tr("'%1' cannot be saved, do you want to save the image with another name?")
                .arg(fileName), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
                break;
            }
        }

    } while (!saved);

    if (saved) {
        aspeqtSettings->setLastDiskImageDir(QFileInfo(fileName).absolutePath());
    }
    aspeqtSettings->unmountImage(no);
    aspeqtSettings->mountImage(no, fileName, img->isReadOnly());
}

void MainWindow::revertDisk(int no)
{
    SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(no + 0x31));
    if (QMessageBox::question(this, tr("Revert to last saved"),
            tr("Do you really want to revert '%1' to its last saved state? You will lose the changes that has been made.")
            .arg(img->originalFileName()), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
        img->lock();
        img->reopen();
        img->unlock();
        deviceStatusChanged(0x31 + no);
    }
}

void MainWindow::on_actionMountDisk_1_triggered() {mountDiskImage(0);}
void MainWindow::on_actionMountDisk_2_triggered() {mountDiskImage(1);}
void MainWindow::on_actionMountDisk_3_triggered() {mountDiskImage(2);}
void MainWindow::on_actionMountDisk_4_triggered() {mountDiskImage(3);}
void MainWindow::on_actionMountDisk_5_triggered() {mountDiskImage(4);}
void MainWindow::on_actionMountDisk_6_triggered() {mountDiskImage(5);}
void MainWindow::on_actionMountDisk_7_triggered() {mountDiskImage(6);}
void MainWindow::on_actionMountDisk_8_triggered() {mountDiskImage(7);}

void MainWindow::on_actionMountFolder_1_triggered() {mountFolderImage(0);}
void MainWindow::on_actionMountFolder_2_triggered() {mountFolderImage(1);}
void MainWindow::on_actionMountFolder_3_triggered() {mountFolderImage(2);}
void MainWindow::on_actionMountFolder_4_triggered() {mountFolderImage(3);}
void MainWindow::on_actionMountFolder_5_triggered() {mountFolderImage(4);}
void MainWindow::on_actionMountFolder_6_triggered() {mountFolderImage(5);}
void MainWindow::on_actionMountFolder_7_triggered() {mountFolderImage(6);}
void MainWindow::on_actionMountFolder_8_triggered() {mountFolderImage(7);}

void MainWindow::on_actionEject_1_triggered() {ejectImage(0);}
void MainWindow::on_actionEject_2_triggered() {ejectImage(1);}
void MainWindow::on_actionEject_3_triggered() {ejectImage(2);}
void MainWindow::on_actionEject_4_triggered() {ejectImage(3);}
void MainWindow::on_actionEject_5_triggered() {ejectImage(4);}
void MainWindow::on_actionEject_6_triggered() {ejectImage(5);}
void MainWindow::on_actionEject_7_triggered() {ejectImage(6);}
void MainWindow::on_actionEject_8_triggered() {ejectImage(7);}

void MainWindow::on_actionWriteProtect_1_triggered() {toggleWriteProtection(0);}
void MainWindow::on_actionWriteProtect_2_triggered() {toggleWriteProtection(1);}
void MainWindow::on_actionWriteProtect_3_triggered() {toggleWriteProtection(2);}
void MainWindow::on_actionWriteProtect_4_triggered() {toggleWriteProtection(3);}
void MainWindow::on_actionWriteProtect_5_triggered() {toggleWriteProtection(4);}
void MainWindow::on_actionWriteProtect_6_triggered() {toggleWriteProtection(5);}
void MainWindow::on_actionWriteProtect_7_triggered() {toggleWriteProtection(6);}
void MainWindow::on_actionWriteProtect_8_triggered() {toggleWriteProtection(7);}

void MainWindow::on_actionMountRecent_0_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_0->text());}
void MainWindow::on_actionMountRecent_1_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_1->text());}
void MainWindow::on_actionMountRecent_2_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_2->text());}
void MainWindow::on_actionMountRecent_3_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_3->text());}
void MainWindow::on_actionMountRecent_4_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_4->text());}
void MainWindow::on_actionMountRecent_5_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_5->text());}
void MainWindow::on_actionMountRecent_6_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_6->text());}
void MainWindow::on_actionMountRecent_7_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_7->text());}
void MainWindow::on_actionMountRecent_8_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_8->text());}
void MainWindow::on_actionMountRecent_9_triggered() {mountFileWithDefaultProtection(firstEmptyDiskSlot(), ui->actionMountRecent_9->text());}

void MainWindow::on_actionEditDisk_1_triggered() {openEditor(0);}
void MainWindow::on_actionEditDisk_2_triggered() {openEditor(1);}
void MainWindow::on_actionEditDisk_3_triggered() {openEditor(2);}
void MainWindow::on_actionEditDisk_4_triggered() {openEditor(3);}
void MainWindow::on_actionEditDisk_5_triggered() {openEditor(4);}
void MainWindow::on_actionEditDisk_6_triggered() {openEditor(5);}
void MainWindow::on_actionEditDisk_7_triggered() {openEditor(6);}
void MainWindow::on_actionEditDisk_8_triggered() {openEditor(7);}

void MainWindow::on_actionSave_1_triggered() {saveDisk(0);}
void MainWindow::on_actionSave_2_triggered() {saveDisk(1);}
void MainWindow::on_actionSave_3_triggered() {saveDisk(2);}
void MainWindow::on_actionSave_4_triggered() {saveDisk(3);}
void MainWindow::on_actionSave_5_triggered() {saveDisk(4);}
void MainWindow::on_actionSave_6_triggered() {saveDisk(5);}
void MainWindow::on_actionSave_7_triggered() {saveDisk(6);}
void MainWindow::on_actionSave_8_triggered() {saveDisk(7);}

void MainWindow::on_actionSaveAs_1_triggered() {saveDiskAs(0);}
void MainWindow::on_actionSaveAs_2_triggered() {saveDiskAs(1);}
void MainWindow::on_actionSaveAs_3_triggered() {saveDiskAs(2);}
void MainWindow::on_actionSaveAs_4_triggered() {saveDiskAs(3);}
void MainWindow::on_actionSaveAs_5_triggered() {saveDiskAs(4);}
void MainWindow::on_actionSaveAs_6_triggered() {saveDiskAs(5);}
void MainWindow::on_actionSaveAs_7_triggered() {saveDiskAs(6);}
void MainWindow::on_actionSaveAs_8_triggered() {saveDiskAs(7);}

void MainWindow::on_actionRevert_1_triggered() {revertDisk(0);}
void MainWindow::on_actionRevert_2_triggered() {revertDisk(1);}
void MainWindow::on_actionRevert_3_triggered() {revertDisk(2);}
void MainWindow::on_actionRevert_4_triggered() {revertDisk(3);}
void MainWindow::on_actionRevert_5_triggered() {revertDisk(4);}
void MainWindow::on_actionRevert_6_triggered() {revertDisk(5);}
void MainWindow::on_actionRevert_7_triggered() {revertDisk(6);}
void MainWindow::on_actionRevert_8_triggered() {revertDisk(7);}

void MainWindow::on_actionEjectAll_triggered()
{
    QMessageBox::StandardButton answer = QMessageBox::No;

    int toBeSaved = 0;

    for (int i = 0; i < 8; i++) {
        SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(i + 0x31));
        if (img && img->isModified()) {
            toBeSaved++;
        }
    }

    if (!toBeSaved) {
        for (int i = 7; i >= 0; i--) {
            ejectImage(i);
        }
        return;
    }

    bool wasRunning = ui->actionStartEmulation->isChecked();
    if (wasRunning) {
        ui->actionStartEmulation->trigger();
    }

    for (int i = 7; i >= 0; i--) {
        SimpleDiskImage *img = qobject_cast <SimpleDiskImage*> (sio->getDevice(i + 0x31));
        if (img && img->isModified()) {
            toBeSaved--;
            answer = saveImageWhenClosing(i, answer, toBeSaved);
            if (answer == QMessageBox::NoToAll) {
                break;
            }
            if (answer == QMessageBox::Cancel) {
                if (wasRunning) {
                    ui->actionStartEmulation->trigger();
                }
                return;
            }
        }
    }
    for (int i = 7; i >= 0; i--) {
        ejectImage(i, false);
    }
    if (wasRunning) {
        ui->actionStartEmulation->trigger();
    }
}

void MainWindow::on_actionMountDisk_triggered()
{
    mountDiskImage(firstEmptyDiskSlot(0, true));
}

void MainWindow::on_actionMountFolder_triggered()
{
    mountFolderImage(firstEmptyDiskSlot(0, true));
}

void MainWindow::on_actionNewImage_triggered()
{
    CreateImageDialog dlg(this);
    if (!dlg.exec()) {
        return;
    };

    SimpleDiskImage *disk = new SimpleDiskImage(sio);
    connect(disk, SIGNAL(statusChanged(int)), this, SLOT(deviceStatusChanged(int)), Qt::QueuedConnection);

    if (!disk->create(++untitledName)) {
        delete disk;
        return;
    }

    DiskGeometry g;
    uint size = dlg.sectorCount() * dlg.sectorSize();
    if (dlg.sectorSize() == 256) {
        if (dlg.sectorCount() >= 3) {
            size -= 384;
        } else {
            size -= dlg.sectorCount() * 128;
        }
    }
    g.initialize(size, dlg.sectorSize());

    if (!disk->format(g)) {
        delete disk;
        return;
    }

    int no = firstEmptyDiskSlot(0, true);

    if (!ejectImage(no)) {
        delete disk;
        return;
    }

    sio->installDevice(0x31 + no, disk);
    deviceStatusChanged(0x31 + no);
    qDebug() << "!n" << tr("[%1] Mounted '%2' as '%3'.")
            .arg(disk->deviceName())
            .arg(disk->originalFileName())
            .arg(disk->description());
}

void MainWindow::on_actionOpenSession_triggered()
{
    QString dir = aspeqtSettings->lastSessionDir();
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open session"),
                                 dir,
                                 tr(
                                         "AspeQt sessions (*.aspeqt);;"
                                         "All files (*)"));
    if (fileName.isEmpty()) {
        return;
    }
    aspeqtSettings->setLastSessionDir(QFileInfo(fileName).absolutePath());
    aspeqtSettings->loadSessionFromFile(fileName);
    for (int i = 0; i < 8; i++) {
        AspeqtSettings::ImageSettings is;
        is = aspeqtSettings->mountedImageSetting(i);
        mountFile(i, is.fileName, is.isWriteProtected);
    }
}

void MainWindow::on_actionSaveSession_triggered()
{
    QString dir = aspeqtSettings->lastSessionDir();
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save session as"),
                                 dir,
                                 tr(
                                         "AspeQt sessions (*.aspeqt);;"
                                         "All files (*)"));
    if (fileName.isEmpty()) {
        return;
    }
    aspeqtSettings->setLastSessionDir(QFileInfo(fileName).absolutePath());
    aspeqtSettings->saveSessionToFile(fileName);
}

void MainWindow::on_actionBootExe_triggered()
{
    QString dir = aspeqtSettings->lastExeDir();
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open executable"),
                                 dir,
                                 tr(
                                         "Atari executables (*.xex *.com *.exe);;"
                                         "All files (*)"));
    if (fileName.isEmpty()) {
        return;
    }
    aspeqtSettings->setLastExeDir(QFileInfo(fileName).absolutePath());
    bootExe(fileName);
}

void MainWindow::on_actionShowPrinterTextOutput_triggered()
{
    if (ui->actionShowPrinterTextOutput->isChecked()) {
        textPrinterWindow->show();
    } else {
        textPrinterWindow->hide();
    }
}

void MainWindow::textPrinterWindowClosed()
{
    ui->actionShowPrinterTextOutput->setChecked(false);
}

void MainWindow::on_actionPlaybackCassette_triggered()
{
    QString fileName = QFileDialog::getOpenFileName(this,
                                                    tr("Open a cassette image"),
                                                    aspeqtSettings->lastCasDir(),
                                                    tr(
                                                    "CAS images (*.cas);;"
                                                    "All files (*)"));

    if (fileName.isEmpty()) {
        return;
    }
    aspeqtSettings->setLastCasDir(QFileInfo(fileName).absolutePath());

    bool restart;
    restart = ui->actionStartEmulation->isChecked();
    if (restart) {
        ui->actionStartEmulation->trigger();
        sio->wait();
        qApp->processEvents();
    }

    CassetteDialog *dlg = new CassetteDialog(this, fileName);
    dlg->exec();
    delete dlg;

    if (restart) {
        ui->actionStartEmulation->trigger();
    }
}

void MainWindow::on_actionQuit_triggered()
{
    close();
}
