Loading files
bae9c0e4eThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "boardeditorfsm.h"
#include "boardeditorstate_adddevice.h"
#include "boardeditorstate_addhole.h"
#include "boardeditorstate_addpad.h"
#include "boardeditorstate_addstroketext.h"
#include "boardeditorstate_addvia.h"
#include "boardeditorstate_drawplane.h"
#include "boardeditorstate_drawpolygon.h"
#include "boardeditorstate_drawtrace.h"
#include "boardeditorstate_drawzone.h"
#include "boardeditorstate_measure.h"
#include "boardeditorstate_select.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
BoardEditorFsm::BoardEditorFsm(const Context& context, QObject* parent) noexcept
: QObject(parent),
mStates(),
mCurrentState(State::IDLE),
mPreviousState(State::IDLE) {
mStates.insert(State::SELECT, new BoardEditorState_Select(context));
mStates.insert(State::ADD_HOLE, new BoardEditorState_AddHole(context));
mStates.insert(State::ADD_STROKE_TEXT,
new BoardEditorState_AddStrokeText(context));
mStates.insert(State::ADD_VIA, new BoardEditorState_AddVia(context));
mStates.insert(State::ADD_THT_PAD,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::THT,
Pad::Function::StandardPad));
mStates.insert(State::ADD_SMT_PAD_STANDARD,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::StandardPad));
mStates.insert(State::ADD_SMT_PAD_THERMAL,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::ThermalPad));
mStates.insert(State::ADD_SMT_PAD_BGA,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::BgaPad));
mStates.insert(State::ADD_SMT_PAD_EDGE_CONNECTOR,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::EdgeConnectorPad));
mStates.insert(State::ADD_SMT_PAD_TEST,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::TestPad));
mStates.insert(State::ADD_SMT_PAD_LOCAL_FIDUCIAL,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::LocalFiducial));
mStates.insert(State::ADD_SMT_PAD_GLOBAL_FIDUCIAL,
new BoardEditorState_AddPad(
context, BoardEditorState_AddPad::PadType::SMT,
Pad::Function::GlobalFiducial));
mStates.insert(State::ADD_DEVICE, new BoardEditorState_AddDevice(context));
mStates.insert(State::DRAW_POLYGON,
new BoardEditorState_DrawPolygon(context));
mStates.insert(State::DRAW_PLANE, new BoardEditorState_DrawPlane(context));
mStates.insert(State::DRAW_ZONE, new BoardEditorState_DrawZone(context));
mStates.insert(State::DRAW_TRACE, new BoardEditorState_DrawTrace(context));
mStates.insert(State::MEASURE, new BoardEditorState_Measure(context));
enterNextState(State::SELECT);
// Connect the requestLeavingState() signal of all states to the
// processSelect() method to leave the state. Using a queued connection to
// avoid complex nested call stacks of two different states at the same time.
foreach (BoardEditorState* state, mStates) {
connect(state, &BoardEditorState::requestLeavingState, this,
&BoardEditorFsm::processSelect, Qt::QueuedConnection);
}
}
BoardEditorFsm::~BoardEditorFsm() noexcept {
leaveCurrentState();
qDeleteAll(mStates);
mStates.clear();
}
/*******************************************************************************
* Event Handlers
******************************************************************************/
bool BoardEditorFsm::processSelect() noexcept {
return setNextState(State::SELECT);
}
bool BoardEditorFsm::processAddHole() noexcept {
return setNextState(State::ADD_HOLE);
}
bool BoardEditorFsm::processAddStrokeText() noexcept {
return setNextState(State::ADD_STROKE_TEXT);
}
bool BoardEditorFsm::processAddVia() noexcept {
return setNextState(State::ADD_VIA);
}
bool BoardEditorFsm::processAddThtPad() noexcept {
return setNextState(State::ADD_THT_PAD);
}
bool BoardEditorFsm::processAddSmtPad(Pad::Function function) noexcept {
switch (function) {
case Pad::Function::ThermalPad:
return setNextState(State::ADD_SMT_PAD_THERMAL);
case Pad::Function::BgaPad:
return setNextState(State::ADD_SMT_PAD_BGA);
case Pad::Function::EdgeConnectorPad:
return setNextState(State::ADD_SMT_PAD_EDGE_CONNECTOR);
case Pad::Function::TestPad:
return setNextState(State::ADD_SMT_PAD_TEST);
case Pad::Function::LocalFiducial:
return setNextState(State::ADD_SMT_PAD_LOCAL_FIDUCIAL);
case Pad::Function::GlobalFiducial:
return setNextState(State::ADD_SMT_PAD_GLOBAL_FIDUCIAL);
default:
return setNextState(State::ADD_SMT_PAD_STANDARD);
}
}
bool BoardEditorFsm::processAddDevice(ComponentInstance& component,
const Uuid& device,
const Uuid& footprint) noexcept {
State oldState = mCurrentState;
if (!setNextState(State::ADD_DEVICE)) {
return false;
}
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processAddDevice(component, device, footprint)) {
return true;
}
}
setNextState(oldState); // restore previous state
return false;
}
bool BoardEditorFsm::processDrawPolygon() noexcept {
return setNextState(State::DRAW_POLYGON);
}
bool BoardEditorFsm::processDrawPlane() noexcept {
return setNextState(State::DRAW_PLANE);
}
bool BoardEditorFsm::processDrawZone() noexcept {
return setNextState(State::DRAW_ZONE);
}
bool BoardEditorFsm::processDrawTrace() noexcept {
return setNextState(State::DRAW_TRACE);
}
bool BoardEditorFsm::processImportDxf() noexcept {
setNextState(State::SELECT);
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processImportDxf()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processMeasure() noexcept {
return setNextState(State::MEASURE);
}
bool BoardEditorFsm::processAbortCommand() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processAbortCommand()) {
return true;
}
}
// Go to the select state
return setNextState(State::SELECT);
}
bool BoardEditorFsm::processSelectAll() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processSelectAll()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processCut() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processCut()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processCopy() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processCopy()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processPaste() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processPaste()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processMove(const Point& delta) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processMove(delta)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processRotate(const Angle& rotation) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processRotate(rotation)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processFlip(Qt::Orientation orientation) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processFlip(orientation)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processSnapToGrid() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processSnapToGrid()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processSetLocked(bool locked) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processSetLocked(locked)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processChangeLineWidth(int step) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processChangeLineWidth(step)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processResetAllTexts() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processResetAllTexts()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processRemove() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processRemove()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processEditProperties() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processEditProperties()) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processKeyPressed(
const GraphicsSceneKeyEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processKeyPressed(e)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processKeyReleased(
const GraphicsSceneKeyEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processKeyReleased(e)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processGraphicsSceneMouseMoved(
const GraphicsSceneMouseEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneMouseMoved(e)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processGraphicsSceneLeftMouseButtonPressed(
const GraphicsSceneMouseEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneLeftMouseButtonPressed(e)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processGraphicsSceneLeftMouseButtonReleased(
const GraphicsSceneMouseEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneLeftMouseButtonReleased(e)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processGraphicsSceneLeftMouseButtonDoubleClicked(
const GraphicsSceneMouseEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneLeftMouseButtonDoubleClicked(e)) {
return true;
}
}
return false;
}
bool BoardEditorFsm::processGraphicsSceneRightMouseButtonReleased(
const GraphicsSceneMouseEvent& e) noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneRightMouseButtonReleased(e)) {
return true;
} else if (mCurrentState != State::SELECT) {
// If right click is not handled, abort current command.
return processAbortCommand();
} else {
// In select state, switch back to last state.
return switchToPreviousState();
}
}
return false;
}
bool BoardEditorFsm::processChangedSelection() noexcept {
if (BoardEditorState* state = getCurrentStateObj()) {
if (state->processChangedSelection()) {
return true;
}
}
return false;
}
/*******************************************************************************
* Private Methods
******************************************************************************/
BoardEditorState* BoardEditorFsm::getCurrentStateObj() const noexcept {
return mStates.value(mCurrentState, nullptr);
}
bool BoardEditorFsm::setNextState(State state) noexcept {
if (state == mCurrentState) {
return true;
}
if (!leaveCurrentState()) {
return false;
}
return enterNextState(state);
}
bool BoardEditorFsm::leaveCurrentState() noexcept {
if ((getCurrentStateObj()) && (!getCurrentStateObj()->exit())) {
return false;
}
switch (mCurrentState) {
case State::SELECT:
// Only memorize states other than SELECT.
break;
case State::ADD_DEVICE:
// The "add device" state does not make much sense to restore with
// rightclick, thus not memorizing it.
break;
default:
mPreviousState = mCurrentState;
break;
}
mCurrentState = State::IDLE;
return true;
}
bool BoardEditorFsm::enterNextState(State state) noexcept {
Q_ASSERT(mCurrentState == State::IDLE);
BoardEditorState* nextState = mStates.value(state, nullptr);
if ((nextState) && (!nextState->entry())) {
return false;
}
mCurrentState = state;
return true;
}
bool BoardEditorFsm::switchToPreviousState() noexcept {
State nextState = mPreviousState;
if ((nextState == mCurrentState) || (nextState == State::IDLE)) {
nextState = State::SELECT;
}
return setNextState(nextState);
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb