Initial Commit
This commit is contained in:
512
MainWindow.cpp
Normal file
512
MainWindow.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
#include "MainWindow.h"
|
||||
#include "ui_MainWindow.h"
|
||||
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QtMath>
|
||||
#include <QScrollBar>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include <QCloseEvent>
|
||||
#include <QDir>
|
||||
|
||||
#include "Connector.h"
|
||||
#include "Connector.h"
|
||||
#include "Wire.h"
|
||||
#include "Part.h"
|
||||
#include "Scene.h"
|
||||
#include "Logic.h"
|
||||
|
||||
#include "UndoCommands/CopyParts.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent, QString loadFile)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::MainWindow)
|
||||
{
|
||||
// Setup UI
|
||||
ui->setupUi(this);
|
||||
// Add tab widget to tool bar to make it moveable and floatable
|
||||
// TODO: Use a less hacky solution and make it work horizontally as well
|
||||
ui->toolBarBottom->addWidget(ui->tabWidget);
|
||||
// Set title name
|
||||
m_title = "Circuit Simulator";
|
||||
// Set initial window title
|
||||
setWindowTitle(m_title + " - Unsaved Circuit");
|
||||
// Initialize graphics scene
|
||||
m_scene = new Scene(ui->graphicsView, this);
|
||||
if(!loadFile.isEmpty())
|
||||
{
|
||||
m_currFilename = loadFile;
|
||||
m_fileLoaded = true;
|
||||
if(!m_scene->m_logic->loadFromFile(loadFile))
|
||||
{
|
||||
m_fileLoaded = false;
|
||||
}
|
||||
else
|
||||
setWindowTitle(m_title + " - " + QFileInfo(m_currFilename).fileName());
|
||||
}
|
||||
// Set active tool mode to select
|
||||
toolSelect();
|
||||
// Set active mode to sim started
|
||||
simStart();
|
||||
// Assign scene to graphics view and show it
|
||||
ui->graphicsView->setScene(m_scene);
|
||||
ui->graphicsView->show();
|
||||
// Setup undoView
|
||||
undoView = new QUndoView(m_scene->m_undoStack);
|
||||
undoView->setWindowTitle("Undo View");
|
||||
undoView->setAttribute(Qt::WA_QuitOnClose, false);
|
||||
// Setup graphics timer
|
||||
QTimer *gfxTimer = new QTimer(this);
|
||||
connect(gfxTimer, &QTimer::timeout, this, &MainWindow::onGfxTimerTimeout);
|
||||
gfxTimer->start(17);
|
||||
// Setup logic timer
|
||||
QTimer *logicTimer = new QTimer(this);
|
||||
connect(logicTimer, &QTimer::timeout, this, &MainWindow::onLogicTimerTimeout);
|
||||
logicTimer->start(1);
|
||||
// Refresh IC list
|
||||
reloadLocalICs();
|
||||
//CONNECT EVENTS
|
||||
connect(ui->zoomSlider, &QSlider::valueChanged, this, &MainWindow::updateMatrix);
|
||||
|
||||
connect(ui->btnZoomIn, &QToolButton::clicked, this, &MainWindow::zoomIn);
|
||||
connect(ui->btnZoomOut, &QToolButton::clicked, this, &MainWindow::zoomOut);
|
||||
|
||||
connect(ui->actionZoom_In, &QAction::triggered, this, &MainWindow::zoomIn);
|
||||
connect(ui->actionZoom_Out, &QAction::triggered, this, &MainWindow::zoomOut);
|
||||
connect(ui->actionReset_Zoom, &QAction::triggered, this, &MainWindow::resetZoom);
|
||||
|
||||
connect(ui->actionToggle_Undo_View, &QAction::triggered, this, &MainWindow::toggleUndoView);
|
||||
|
||||
connect(ui->actionUndo, &QAction::triggered, this, &MainWindow::editUndo);
|
||||
connect(ui->actionRedo, &QAction::triggered, this, &MainWindow::editRedo);
|
||||
connect(ui->actionCut, &QAction::triggered, this, &MainWindow::editCut);
|
||||
connect(ui->actionCopy, &QAction::triggered, this, &MainWindow::editCopy);
|
||||
connect(ui->actionPaste, &QAction::triggered, this, &MainWindow::editPaste);
|
||||
connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::editDelete);
|
||||
|
||||
connect(ui->actionNew, &QAction::triggered, this, &MainWindow::fileNew);
|
||||
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::fileOpen);
|
||||
connect(ui->actionSave, &QAction::triggered, this, &MainWindow::fileSave);
|
||||
connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::fileSaveAs);
|
||||
connect(ui->actionExit, &QAction::triggered, this, &MainWindow::fileExit);
|
||||
|
||||
connect(ui->actionConnect, &QAction::triggered, this, &MainWindow::toolConnect);
|
||||
connect(ui->actionRemove_Connections, &QAction::triggered, this, &MainWindow::toolDisconnect);
|
||||
connect(ui->actionSelect, &QAction::triggered, this, &MainWindow::toolSelect);
|
||||
connect(ui->actionPan, &QAction::triggered, this, &MainWindow::toolPan);
|
||||
|
||||
connect(ui->actionStep, &QAction::triggered, this, &MainWindow::simStep);
|
||||
connect(ui->actionStart, &QAction::triggered, this, &MainWindow::simStart);
|
||||
connect(ui->actionStop, &QAction::triggered, this, &MainWindow::simStop);
|
||||
|
||||
connect(ui->btnGateBuffer, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateNot, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateAnd, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateOr, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateNand, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateNor, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateXor, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnGateXnor, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
|
||||
connect(ui->btnIOHighConstant, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnIOLowConstant, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnIOLightBulb, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
connect(ui->btnIOToggleButton, &QToolButton::clicked, this, &MainWindow::toolAddComponent);
|
||||
//END CONNECT EVENTS
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool MainWindow::isFileLoaded()
|
||||
{
|
||||
return m_fileLoaded;
|
||||
}
|
||||
|
||||
QString MainWindow::currFilename()
|
||||
{
|
||||
return m_currFilename;
|
||||
}
|
||||
|
||||
//PUBLIC
|
||||
|
||||
void MainWindow::resetTools()
|
||||
{
|
||||
// Reset button checked states
|
||||
ui->actionConnect->setChecked(false);
|
||||
ui->actionRemove_Connections->setChecked(false);
|
||||
ui->actionSelect->setChecked(false);
|
||||
ui->actionPan->setChecked(false);
|
||||
// Reset drag mode
|
||||
ui->graphicsView->setDragMode(QGraphicsView::NoDrag);
|
||||
// Unselect selected items
|
||||
for(auto item : m_scene->selectedItems())
|
||||
item->setSelected(false);
|
||||
// Reset graphicsView interactivity
|
||||
ui->graphicsView->setInteractive(true);
|
||||
//Reset cursor shape
|
||||
ui->graphicsView->setCursor(QCursor(Qt::CursorShape::ArrowCursor));
|
||||
}
|
||||
|
||||
void MainWindow::changeMade()
|
||||
{
|
||||
unsavedChanges = true;
|
||||
if(m_fileLoaded)
|
||||
setWindowTitle(m_title + " - " + QFileInfo(m_currFilename).fileName() + "*");
|
||||
else
|
||||
setWindowTitle(m_title + " - Unsaved Circuit*");
|
||||
}
|
||||
|
||||
void MainWindow::changesSaved()
|
||||
{
|
||||
unsavedChanges = false;
|
||||
setWindowTitle(m_title + " - " + QFileInfo(m_currFilename).fileName());
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if(!unsavedChanges)
|
||||
{
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// I just blatantly ripped off the following code from this example https://doc.qt.io/qt-5/qmessagebox.html
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("The Circuit has unsaved changes.");
|
||||
msgBox.setInformativeText("Do you really want to quit?");
|
||||
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
||||
msgBox.setDefaultButton(QMessageBox::Save);
|
||||
int ret = msgBox.exec();
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case QMessageBox::Save:
|
||||
fileSave();
|
||||
if(!unsavedChanges)
|
||||
event->accept();
|
||||
event->ignore();
|
||||
break;
|
||||
case QMessageBox::Discard:
|
||||
event->accept();
|
||||
break;
|
||||
case QMessageBox::Cancel:
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::reloadLocalICs()
|
||||
{
|
||||
// Return if no file is loaded
|
||||
if(!m_fileLoaded)
|
||||
return;
|
||||
|
||||
if(tabICInfoLabelExists)
|
||||
{
|
||||
delete ui->tabICInfoLabel;
|
||||
tabICInfoLabelExists = false;
|
||||
}
|
||||
|
||||
for(auto btn : m_icPushButtons)
|
||||
{
|
||||
delete btn;
|
||||
}
|
||||
m_icPushButtons.clear();
|
||||
|
||||
QDir fileDir(m_currFilename);
|
||||
fileDir.cdUp();
|
||||
QStringList nameFilter("*.csim");
|
||||
QStringList icFiles = fileDir.entryList(nameFilter, QDir::Files);
|
||||
|
||||
for(const auto& icFile : icFiles)
|
||||
{
|
||||
// Skip step if file name is equal to currently loaded file
|
||||
if(icFile == QFileInfo(m_currFilename).fileName())
|
||||
continue;
|
||||
|
||||
QPushButton* btn = new QPushButton(ui->tabICScrollAreaWidgetContents);
|
||||
|
||||
ui->tabICScrollAreaWidgetContents->layout()->addWidget(btn);
|
||||
btn->setText(icFile);
|
||||
btn->setObjectName(icFile);
|
||||
btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
m_icPushButtons.append(btn);
|
||||
|
||||
connect(btn, &QPushButton::clicked, this, &MainWindow::toolAddIC);
|
||||
}
|
||||
}
|
||||
|
||||
//SIGNALS
|
||||
void MainWindow::updateMatrix()
|
||||
{
|
||||
qreal scale = qPow(qreal(2), qreal(ui->zoomSlider->value() - 100) / qreal(20));
|
||||
|
||||
ui->labelZoomLevel->setText(QString::number(int(scale * 100.0)) + '%');
|
||||
|
||||
QMatrix matrix;
|
||||
matrix.scale(scale, scale);
|
||||
ui->graphicsView->setMatrix(matrix);
|
||||
}
|
||||
|
||||
void MainWindow::zoomIn()
|
||||
{
|
||||
ui->zoomSlider->setValue(ui->zoomSlider->value() + ui->zoomSlider->pageStep());
|
||||
}
|
||||
|
||||
void MainWindow::zoomOut()
|
||||
{
|
||||
ui->zoomSlider->setValue(ui->zoomSlider->value() - ui->zoomSlider->pageStep());
|
||||
}
|
||||
|
||||
void MainWindow::resetZoom()
|
||||
{
|
||||
ui->zoomSlider->setValue(100);
|
||||
}
|
||||
|
||||
void MainWindow::toggleUndoView()
|
||||
{
|
||||
if(undoView->isHidden())
|
||||
undoView->show();
|
||||
else
|
||||
undoView->hide();
|
||||
}
|
||||
|
||||
void MainWindow::editUndo()
|
||||
{
|
||||
m_scene->undo();
|
||||
}
|
||||
|
||||
void MainWindow::editRedo()
|
||||
{
|
||||
m_scene->redo();
|
||||
}
|
||||
|
||||
void MainWindow::editCut()
|
||||
{
|
||||
// Put parts into copy buffer
|
||||
editCopy();
|
||||
// Remove selected parts
|
||||
QList<QGraphicsItem*> selItems = m_scene->selectedItems();
|
||||
QList<Part*> selParts;
|
||||
for(auto convItm : selItems)
|
||||
selParts.append((Part*)convItm);
|
||||
m_scene->removeParts(selParts);
|
||||
}
|
||||
|
||||
void MainWindow::editCopy()
|
||||
{
|
||||
QList<QGraphicsItem*> selItems = m_scene->selectedItems();
|
||||
QSet<Part*> selParts;
|
||||
QList<Part*> selPartsList;
|
||||
QList<Wire*> selPartsWires;
|
||||
|
||||
for(auto item : selItems)
|
||||
selParts.insert((Part*)item);
|
||||
|
||||
selPartsList = selParts.values();
|
||||
|
||||
// Problem: find all wires of which both sides are being used by selected parts
|
||||
// Go through all selected parts
|
||||
for(auto part : selPartsList)
|
||||
{
|
||||
// Go through their outgoing connectors
|
||||
for(auto outConnector : part->m_outputs)
|
||||
{
|
||||
// Go through their wires
|
||||
for(auto outWire : outConnector->m_wires)
|
||||
{
|
||||
// See if the other end of the wire contains a part which is also selected
|
||||
if(selParts.contains((Part*)outWire->m_connectorOutput->parentItem()))
|
||||
selPartsWires.append(outWire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_scene->initCopy(selPartsList, selPartsWires);
|
||||
}
|
||||
|
||||
void MainWindow::editPaste()
|
||||
{
|
||||
// Only do copy relative to mouse if graphicsView under mouse
|
||||
// Else, copy into center of graphics view
|
||||
m_scene->doCopy(ui->graphicsView->underMouse());
|
||||
}
|
||||
|
||||
void MainWindow::editDelete()
|
||||
{
|
||||
QList<QGraphicsItem*> selItems = m_scene->selectedItems();
|
||||
QList<Part*> selParts;
|
||||
for(auto convItm : selItems)
|
||||
selParts.append((Part*)convItm);
|
||||
m_scene->removeParts(selParts);
|
||||
}
|
||||
|
||||
void MainWindow::fileNew()
|
||||
{
|
||||
MainWindow* newWindow = new MainWindow();
|
||||
newWindow->show();
|
||||
}
|
||||
|
||||
void MainWindow::fileOpen()
|
||||
{
|
||||
QString filter = tr("CSIM Files(*.csim)");
|
||||
auto filename = QFileDialog::getOpenFileName(this, tr("Open File"), "", filter, &filter);
|
||||
if(!filename.isEmpty())
|
||||
{
|
||||
// If there is already a file loaded or there are unsaved changes, create a new window
|
||||
if(m_fileLoaded || unsavedChanges)
|
||||
{
|
||||
MainWindow* newWindow = new MainWindow(nullptr, filename);
|
||||
newWindow->show();
|
||||
}
|
||||
// Else, load file into current window
|
||||
else
|
||||
{
|
||||
if(m_scene->m_logic->loadFromFile(filename))
|
||||
{
|
||||
m_currFilename = filename;
|
||||
m_fileLoaded = true;
|
||||
setWindowTitle(m_title + " - " + QFileInfo(m_currFilename).fileName());
|
||||
}
|
||||
}
|
||||
}
|
||||
reloadLocalICs();
|
||||
}
|
||||
|
||||
void MainWindow::fileSave()
|
||||
{
|
||||
if(m_fileLoaded)
|
||||
m_scene->m_logic->saveToFile(m_currFilename);
|
||||
else
|
||||
fileSaveAs();
|
||||
reloadLocalICs();
|
||||
}
|
||||
|
||||
void MainWindow::fileSaveAs()
|
||||
{
|
||||
QString filter = tr("CSIM Files(*.csim)");
|
||||
auto filename = QFileDialog::getSaveFileName(this, tr("Save As"), "", filter, &filter);
|
||||
if(!filename.isEmpty())
|
||||
{
|
||||
if(m_scene->m_logic->saveToFile(filename))
|
||||
{
|
||||
m_fileLoaded = true;
|
||||
m_currFilename = filename;
|
||||
setWindowTitle("Circuit Simulator - " + QFileInfo(m_currFilename).fileName());
|
||||
}
|
||||
}
|
||||
reloadLocalICs();
|
||||
}
|
||||
|
||||
void MainWindow::fileExit()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
void MainWindow::toolConnect()
|
||||
{
|
||||
resetTools();
|
||||
toolMode = Connect;
|
||||
ui->actionConnect->setChecked(true);
|
||||
ui->graphicsView->setCursor(QCursor(Qt::CursorShape::PointingHandCursor));
|
||||
}
|
||||
|
||||
void MainWindow::toolDisconnect()
|
||||
{
|
||||
resetTools();
|
||||
toolMode = Disconnect;
|
||||
ui->actionRemove_Connections->setChecked(true);
|
||||
ui->graphicsView->setCursor(QCursor(Qt::CursorShape::PointingHandCursor));
|
||||
}
|
||||
|
||||
void MainWindow::toolSelect()
|
||||
{
|
||||
resetTools();
|
||||
toolMode = Select;
|
||||
ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
|
||||
ui->actionSelect->setChecked(true);
|
||||
}
|
||||
|
||||
void MainWindow::toolPan()
|
||||
{
|
||||
resetTools();
|
||||
toolMode = Pan;
|
||||
ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
|
||||
ui->graphicsView->setInteractive(false);
|
||||
ui->actionPan->setChecked(true);
|
||||
}
|
||||
|
||||
void MainWindow::toolAddComponent()
|
||||
{
|
||||
QToolButton *button = (QToolButton*)sender();
|
||||
|
||||
if(button->objectName() == "btnGateBuffer")
|
||||
m_scene->addPart(PartType::GateBuffer);
|
||||
else if(button->objectName() == "btnGateNot")
|
||||
m_scene->addPart(PartType::GateNot);
|
||||
else if(button->objectName() == "btnGateAnd")
|
||||
m_scene->addPart(PartType::GateAnd);
|
||||
else if(button->objectName() == "btnGateOr")
|
||||
m_scene->addPart(PartType::GateOr);
|
||||
else if(button->objectName() == "btnGateNand")
|
||||
m_scene->addPart(PartType::GateNand);
|
||||
else if(button->objectName() == "btnGateNor")
|
||||
m_scene->addPart(PartType::GateNor);
|
||||
else if(button->objectName() == "btnGateXor")
|
||||
m_scene->addPart(PartType::GateXor);
|
||||
else if(button->objectName() == "btnGateXnor")
|
||||
m_scene->addPart(PartType::GateXnor);
|
||||
else if(button->objectName() == "btnIOHighConstant")
|
||||
m_scene->addPart(PartType::IOHighConstant);
|
||||
else if(button->objectName() == "btnIOLowConstant")
|
||||
m_scene->addPart(PartType::IOLowConstant);
|
||||
else if(button->objectName() == "btnIOLightBulb")
|
||||
m_scene->addPart(PartType::IOLightBulb);
|
||||
else if(button->objectName() == "btnIOToggleButton")
|
||||
m_scene->addPart(PartType::IOToggleButton);
|
||||
}
|
||||
|
||||
void MainWindow::simStep()
|
||||
{
|
||||
m_scene->m_logic->doLogicStep();
|
||||
}
|
||||
|
||||
void MainWindow::simStart()
|
||||
{
|
||||
ui->actionStart->setEnabled(false);
|
||||
ui->actionStep->setEnabled(false);
|
||||
ui->actionStop->setEnabled(true);
|
||||
simRunning = true;
|
||||
}
|
||||
|
||||
void MainWindow::simStop()
|
||||
{
|
||||
ui->actionStart->setEnabled(true);
|
||||
ui->actionStep->setEnabled(true);
|
||||
ui->actionStop->setEnabled(false);
|
||||
simRunning = false;
|
||||
}
|
||||
|
||||
void MainWindow::onLogicTimerTimeout()
|
||||
{
|
||||
if(simRunning)
|
||||
m_scene->m_logic->doLogicStep();
|
||||
}
|
||||
|
||||
void MainWindow::onGfxTimerTimeout()
|
||||
{
|
||||
m_scene->updateGraphics();
|
||||
}
|
||||
|
||||
void MainWindow::toolAddIC()
|
||||
{
|
||||
QDir fileDir(m_currFilename);
|
||||
fileDir.cdUp();
|
||||
QString path = fileDir.absoluteFilePath(sender()->objectName());
|
||||
m_scene->addIC(path);
|
||||
}
|
||||
//END SIGNALS
|
||||
Reference in New Issue
Block a user