AppleSAWS/src/ui/diskexplorer/diskexplorermapwidget.cpp

648 lines
21 KiB
C++

/*****************************************************************************
* AppleSAWS - The Apple Software Analysis WorkShop *
* Copyright (C) 2015-2021 Mark D. Long *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
*****************************************************************************/
#include "diskexplorermapwidget.h"
#include "util.h"
#include <QPushButton>
#include <QGridLayout>
#include <QButtonGroup>
#include <QDebug>
#include <QHBoxLayout>
#include <QGuiApplication>
DiskExplorerMapWidget::DiskExplorerMapWidget(int numtracks, int numsectors, QWidget *parent) : QWidget(parent)
{
m_numtracks = numtracks;
m_numsectors = numsectors;
m_diskLabel = nullptr;
m_statusWidget = nullptr;
m_currentChecked = nullptr;
m_deferredSetup = false;
setWindowTitle("Disk Explorer");
QGridLayout *gridlayout = new QGridLayout(this);
// gridlayout->setSizeConstraint(QLayout::SetMinimumSize);
gridlayout->setHorizontalSpacing(1);
gridlayout->setVerticalSpacing(1);
initColors();
makeStatusWidget();
m_bgroup = new QButtonGroup(this);
QLabel *tracklabel = new QLabel("Track",this);
tracklabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed));
gridlayout->addWidget(tracklabel,0,0,1,m_numtracks+1,Qt::AlignHCenter);
for (int track= 0; track < numtracks; track++)
{
QLabel *label = new QLabel(QString("%1").arg(track,2,10,QChar('0')));
label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed));
label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
gridlayout->addWidget(label,1,track+1);
}
for (int sec = 0; sec < numsectors; sec++)
{
QLabel *label = new QLabel(QString("Sec %1").arg(sec));
label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed));
label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
gridlayout->addWidget(label,sec+2,0);
}
for (int track = 0; track < 35; track++)
{
for (int sec = 0; sec < 16; sec++)
{
DEButton *tb = new DEButton(this,track,sec);
tb->setObjectName(QString("BtnT%1S%2").arg(track).arg(sec));
tb->setBgColor(m_defaultColor);
tb->setCheckable(true);
m_bgroup->addButton(tb,(track * numsectors) + sec);
connect(tb, &DEButton::checked, this, &DiskExplorerMapWidget::handleButtonCheck);
m_buttons[track][sec] = tb;
tb->setAutoFillBackground(true);
gridlayout->addWidget(tb,sec+2,track+1);
}
}
gridlayout->addWidget(getStatusWidget(),18,0,1,35); // Stretchy Row
// gridlayout->setRowStretch(18,900);
for (auto rownum = 0; rownum < gridlayout->rowCount(); rownum++)
{
gridlayout->setRowStretch(rownum,1);
}
gridlayout->setRowStretch(gridlayout->rowCount()-1, 5000);
for (auto colnum= 0; colnum < gridlayout->columnCount(); colnum++)
{
gridlayout->setColumnStretch(colnum,1);
}
}
void DiskExplorerMapWidget::makeStatusWidget()
{
QWidget *statusWidget = new QWidget;
QHBoxLayout *hbl = new QHBoxLayout();
statusWidget->setLayout(hbl);
m_trackSectorLabel = new QLabel(this);
m_trackSectorLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
m_trackSectorLabel->setText("No Track/Sector selected");
hbl->insertWidget(0,m_trackSectorLabel,0,Qt::AlignLeft | Qt::AlignVCenter);
m_diskLabel = new QLabel(this);
m_diskLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
m_diskLabel->setText("[No Disk]");
hbl->insertWidget(1,m_diskLabel,0,Qt::AlignLeft | Qt::AlignVCenter);
m_statusWidget = statusWidget;
}
QString DiskExplorerMapWidget::getSectorDescription(int track, int sector)
{
if (track == 0 && sector == 0) {
return "Boot Sector";
}
else if (track <= 2)
{
return "DOS Image";
}
else if (track == 17 && sector == 0)
{
return "VTOC";
}
else
{
QString desc = "xxx";
if (m_descriptions.contains(TSPair(track,sector)))
{
desc = m_descriptions[TSPair(track,sector)];
}
else
{
if (m_roles[TSPair(track,sector)] == DiskSectorRole::Unknown)
{
desc = "Unused";
}
else
{
desc = "Marked as used, but use unknown";
}
}
return desc.simplified();
}
}
void DiskExplorerMapWidget::handleButtonCheck(int track, int sector, bool checked)
{
DEButton *currbutton = buttonAt(track,sector);
if (m_currentChecked)
{
// Do anything needed to clean up after previous button click
}
if (checked)
{
pSector sec = m_disk->getSector(track,sector);
const QByteArray data = sec->rawData();
emit showSectorData(data,track,sector,QVariant((int) m_roles[TSPair(track,sector)]));
emit showSector(sec,track,sector,m_roles[TSPair(track,sector)]);
m_trackSectorLabel->setText(
QString("Track: %1 Sector: %2 (%3)")
.arg(track)
.arg(sector)
.arg(getSectorDescription(track,sector)));
}
else{
emit showSectorData(QByteArray(),-1,-1,QVariant(-1));
emit showSector(nullptr, -1, -1, DiskSectorRole::Unknown);
m_trackSectorLabel->setText("No Track/Sector selected");
}
m_currentChecked = currbutton;
}
void DiskExplorerMapWidget::setButtonBgColor(int track, int sector, QColor color)
{
buttonAt(track,sector)->setBgColor(color);
}
void DiskExplorerMapWidget::setDisk(Dos33DiskImage *disk)
{
if (disk)
{
m_descriptions.clear();
m_disk = disk;
setWindowTitle(QString("Disk Explorer - %1").arg(m_disk->getDiskImageName()));
m_diskLabel->setText(disk->getDiskImageName());
if (!isHidden())
{
if (m_disk)
{
defineRoles();
checkForUsedButUnknown();
mapButtonsFromRoles();
//mapDiskToButtons();
}
}
else
{
m_deferredSetup = true;
}
}
}
void DiskExplorerMapWidget::unloadDisk()
{
if (m_disk)
{
m_descriptions.clear();
m_bgroup->setExclusive(false);
for (int track = 0; track < m_numtracks; track++)
{
for (int sec = 0; sec < m_numsectors; sec++)
{
DEButton *button = buttonAt(track,sec);
button->setText("");
button->setBgColor(m_defaultColor);
button->setChecked(false);
}
}
setAllButtonsEnabled(false);
}
}
void DiskExplorerMapWidget::setAllButtonsEnabled(bool enabled)
{
for (int track = 0; track < m_numtracks; track++)
{
for (int sec = 0; sec < m_numsectors; sec++)
{
DEButton *button = buttonAt(track,sec);
button->setEnabled(enabled);
}
}
}
QLabel *DiskExplorerMapWidget::makeKeyLabel(QWidget *parent, QString name, QColor color)
{
QLabel *label = new QLabel(name,parent);
label->setStyleSheet(QString("background-color:%1; color: %2")
.arg(color.name())
.arg(determineFgColor(color).name()));
label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
return label;
}
QGroupBox *DiskExplorerMapWidget::makeKeyWidget()
{
int idx = 0;
QGroupBox *groupbox= new QGroupBox();
//groupbox->setTitle("Key");
QGridLayout *layout = new QGridLayout();
layout->setVerticalSpacing(0);
layout->setHorizontalSpacing(0);
groupbox->setLayout(layout);
layout->addWidget(makeKeyLabel(groupbox,"Unused Sector",m_defaultColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Boot Sector",m_bootSectorColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"DOS Image",m_dosImageColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"VTOC",m_vtocColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Dir Entry",m_dirEntryColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"T/S List",m_tsListColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Applesoft File",m_applesoftFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Int. Basic File",m_intBasicFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Binary File",m_binaryFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Text File",m_textFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Relocatable File",m_reloFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Type-A File",m_typeAFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Type-B File",m_typeBFileColor),idx++,0);
layout->addWidget(makeKeyLabel(groupbox,"Type-S File",m_typeSFileColor),idx++,0);
return groupbox;
}
ViewWidgetStack *DiskExplorerMapWidget::generateViewWidgetStack()
{
ViewWidgetStack *vws = new ViewWidgetStack();
vws->setSector(nullptr);
connect(this, &DiskExplorerMapWidget::showSector,
vws, &ViewWidgetStack::handleShowSectorData);
return vws;
}
DEButton *DiskExplorerMapWidget::buttonAt(int track, int sector)
{
//qDebug() << "Button At:" << track << "," << sector;
if (track >= m_numtracks || sector >= m_numsectors)
{
if (track >= m_numtracks) { track = 0; }
if (sector >= m_numsectors) { sector = 0; }
qWarning() << "Invalid track/sector for button (" << track << "," << sector << ")";
}
return m_buttons[track][sector];
}
void DiskExplorerMapWidget::initColors()
{
m_defaultColor = QColor(Qt::lightGray);
m_bootSectorColor = QColor("#20b060");
m_dosImageColor = QColor("#30d000");
m_vtocColor = QColor("#efc010");
m_tsListColor = QColor("#ff8000");
m_dirEntryColor = QColor("#dfdf00");
m_applesoftFileColor = QColor("#3030e0");
m_intBasicFileColor = QColor("#00d0d0");
m_binaryFileColor = QColor("#d060d0");
m_textFileColor = QColor("#F05060");
m_reloFileColor = QColor("#d00000");
m_typeAFileColor = QColor("#c040a0");
m_typeBFileColor = QColor("#c03030");
m_typeSFileColor = QColor("#20a0a0");
}
void DiskExplorerMapWidget::defineRoles(TSPair vtoc)
{
m_descriptions.clear();
m_numbers.clear();
m_roles.clear();
qDebug() << "in defineRoles with vtoc at : " << vtoc.track() << "," << vtoc.sector();
int buttonNumber = 0;
for (auto track = 0; track < m_numtracks; track++)
{
for (auto sec = 0; sec < m_numsectors; sec++)
{
TSPair ts(track,sec);
if (track == 0 && sec == 0)
{
if (setButtonRole(ts,DiskSectorRole::BootSector))
{
setButtonNumber(ts,buttonNumber++);
setDescription(ts,"Boot Sector");
}
}
else if (track <= 2)
{
if (setButtonRole(ts,DiskSectorRole::DosImage))
{
setButtonNumber(ts,buttonNumber++);
setDescription(ts,"DOS Image");
}
}
else
{
setButtonRole(ts,DiskSectorRole::Unknown);
}
}
}
if (setButtonRole(vtoc, DiskSectorRole::VTOC))
{
setButtonNumber(vtoc,buttonNumber++);
setDescription(vtoc,"VTOC");
}
mapCatalogSectors(buttonNumber);
}
void DiskExplorerMapWidget::mapCatalogSectors(int &buttonNumber)
{
int catSectorCount = 0;
foreach (auto cs, m_disk->getCatalogSectors())
{
qDebug() << "Processing buttons for catalog sectors";
TSPair ts = cs.sectorLocation();
qDebug() << "Sector location: " << ts.track() << "," << ts.sector();
if (setButtonRole(ts,DiskSectorRole::CatalogSector))
{
QString desc = QString("Catalog Sector #%1").arg(++catSectorCount);
qDebug() << "Desc: " << desc;
setDescription(ts,desc);
setButtonNumber(ts,buttonNumber++);
// qDebug() << "Setting button number " << buttonNumber-1
// << " at " << ts.track() << "," << ts.sector();
int fdeNum = 0;
foreach (FileDescriptiveEntry fde, cs.getFDEs())
{
mapFDE(fde, fdeNum++, buttonNumber);
}
}
else
{
qDebug("Not processing remap-attempted Catalog Sector");
}
}
}
void DiskExplorerMapWidget::mapFDE(FileDescriptiveEntry &fde, int /*fdeNum*/, int &buttonNumber)
{
TSPair ts_list = fde.firstTSListSector();
int tslCount = 0;
if (fde.lengthInSectors == 0)
{
qDebug("FDE Reports 0 sectors used, not looking at T/S List");
return;
}
mapTSListSector(ts_list,fde,buttonNumber, tslCount);
}
void DiskExplorerMapWidget::mapTSListSector(TSPair location,
FileDescriptiveEntry &fde,
int &buttonNumber, int &tslCount)
{
// Don't follow deleted links.
if (location.track() == 0xff) return;
if (location == TSPair(0,0))
{
return;
}
if (!location.isValid())
{
qDebug("Invalid mapTSListSector location: %d/%d. Not parsing.",
location.track(),location.sector());
return;
}
if (m_roles[location] != DiskSectorRole::Unknown)
{
qDebug("Not processing FDE for previously processed sector at %d/%d",
location.track(), location.sector());
return;
}
pSector s = m_disk->getSector(location);
TrackSectorList tsl(s);
if (setButtonRole(location,DiskSectorRole::TSList))
{
tslCount++;
QString desc = QString("T/S List #%1 for %2").arg(tslCount)
.arg(fde.filename.printable());
setDescription(location, desc);
setButtonNumber(location,buttonNumber++);
int sectorcount = 0;
auto pairs = tsl.getValidTSPairs();
foreach(TSPair tsp, pairs)
{
if (tsp.isValid())
{
auto filetype = getFileTypeFromID(fde.fileTypeIdentifier());
if (setButtonRole(tsp,filetype))
{
setButtonNumber(tsp,buttonNumber++);
QString description = QString("Sector #%1.%2 of %3")
.arg(tslCount)
.arg(++sectorcount)
.arg(fde.filename.printable());
setDescription(tsp,description);
}
}
else
{
qDebug("Invalid value in T/S List");
}
}
TSPair next = tsl.getNextTSList();
mapTSListSector(next, fde, buttonNumber, tslCount);
}
}
DiskSectorRole DiskExplorerMapWidget::getFileTypeFromID(QString id)
{
DiskSectorRole role = DiskSectorRole::Used;
if (id == "I") role = DiskSectorRole::IntBasicFile;
else if (id == "A") role = DiskSectorRole::ApplesoftFile;
else if (id== "R") role = DiskSectorRole::RelocatableFile;
else if (id == "B") role = DiskSectorRole::BinaryFile;
else if (id== "S") role = DiskSectorRole::TypeSFile;
else if (id == "T") role = DiskSectorRole::TextFile;
else if (id == "a") role = DiskSectorRole::TypeAFile;
else if (id == "b") role = DiskSectorRole::TypeBFile;
return role;
}
bool DiskExplorerMapWidget::setButtonRole(TSPair ts, DiskSectorRole role)
{
if (m_roles.contains(ts) && m_roles[ts] != DiskSectorRole::Unknown)
{
qDebug("Trying to remap button %d (Role: %d) with new role %d.",
m_numbers[ts], (int) m_roles[ts], (int) role);
return false;
}
m_roles[ts] = role;
return true;
}
void DiskExplorerMapWidget::setButtonNumber(TSPair ts, int number)
{
m_numbers[ts] = number;
}
void DiskExplorerMapWidget::setDescription(TSPair ts, QString description)
{
m_descriptions[ts] = description;
}
void DiskExplorerMapWidget::mapButtonsFromRoles()
{
for (auto track = 0; track < m_numtracks; track++)
{
for (auto sec = 0; sec < m_numsectors; sec++)
{
auto button = buttonAt(track,sec);
TSPair ts(track,sec);
QColor buttonColor;
switch (m_roles[ts])
{
case DiskSectorRole::BootSector:
buttonColor = m_bootSectorColor;
break;
case DiskSectorRole::DosImage:
buttonColor = m_dosImageColor;
break;
case DiskSectorRole::VTOC:
buttonColor = m_vtocColor;
break;
case DiskSectorRole::CatalogSector:
buttonColor = m_dirEntryColor;
break;
case DiskSectorRole::TSList:
buttonColor = m_tsListColor;
break;
case DiskSectorRole::ApplesoftFile:
buttonColor = m_applesoftFileColor;
break;
case DiskSectorRole::IntBasicFile:
buttonColor = m_intBasicFileColor;
break;
case DiskSectorRole::TextFile:
buttonColor = m_textFileColor;
break;
case DiskSectorRole::RelocatableFile:
buttonColor = m_reloFileColor;
break;
case DiskSectorRole::BinaryFile:
buttonColor = m_binaryFileColor;
break;
case DiskSectorRole::TypeAFile:
buttonColor = m_typeAFileColor;
break;
case DiskSectorRole::TypeBFile:
buttonColor = m_typeBFileColor;
break;
case DiskSectorRole::TypeSFile:
buttonColor = m_typeSFileColor;
break;
case DiskSectorRole::Used:
buttonColor = m_defaultColor.lighter(120);
break;
case DiskSectorRole::Unknown:
default:
buttonColor = m_defaultColor;
}
button->setBgColor(buttonColor);
if (m_roles.contains(ts))
{
if (m_roles[ts] != DiskSectorRole::Used && m_roles[ts] != DiskSectorRole::Unknown)
{
setButtonText(track,sec,QString("%1").arg(m_numbers[ts]));
}
else
{
if (m_roles[ts] == DiskSectorRole::Used)
{
setButtonText(track,sec,"?");
}
}
}
}
}
}
void DiskExplorerMapWidget::checkForUsedButUnknown(TSPair vtoc)
{
auto vtocsector = m_disk->getSector(vtoc)->promoteToVTOC();
for (auto track = 0; track < m_numtracks; track++)
{
for (auto sec = 0; sec < m_numsectors; sec++)
{
TSPair ts(track,sec);
if (m_roles[ts] == DiskSectorRole::Unknown)
{
if (vtocsector.isSectorInUse(ts))
{
m_roles[ts] = DiskSectorRole::Used;
}
}
}
}
}
void DiskExplorerMapWidget::showEvent(QShowEvent *)
{
if (m_deferredSetup)
{
if (m_disk)
{
defineRoles();
checkForUsedButUnknown();
mapButtonsFromRoles();
//mapDiskToButtons();
}
m_deferredSetup = false;
}
}