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 "schematiceditorfsm.h"
#include "schematiceditorstate_addcomponent.h"
#include "schematiceditorstate_addimage.h"
#include "schematiceditorstate_addlabel.h"
#include "schematiceditorstate_addtext.h"
#include "schematiceditorstate_drawbus.h"
#include "schematiceditorstate_drawpolygon.h"
#include "schematiceditorstate_drawwire.h"
#include "schematiceditorstate_measure.h"
#include "schematiceditorstate_select.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
SchematicEditorFsm::SchematicEditorFsm(const Context& context,
QObject* parent) noexcept
: QObject(parent),
mStates(),
mCurrentState(State::IDLE),
mPreviousState(State::IDLE) {
mStates.insert(State::SELECT, new SchematicEditorState_Select(context));
mStates.insert(State::DRAW_WIRE, new SchematicEditorState_DrawWire(context));
mStates.insert(State::DRAW_BUS, new SchematicEditorState_DrawBus(context));
mStates.insert(State::ADD_LABEL, new SchematicEditorState_AddLabel(context));
mStates.insert(State::ADD_COMPONENT,
new SchematicEditorState_AddComponent(context));
mStates.insert(State::DRAW_POLYGON,
new SchematicEditorState_DrawPolygon(context));
mStates.insert(State::ADD_TEXT, new SchematicEditorState_AddText(context));
mStates.insert(State::ADD_IMAGE, new SchematicEditorState_AddImage(context));
mStates.insert(State::MEASURE, new SchematicEditorState_Measure(context));
// 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 (SchematicEditorState* state, mStates) {
connect(state, &SchematicEditorState::requestLeavingState, this,
&SchematicEditorFsm::processSelect, Qt::QueuedConnection);
connect(state, &SchematicEditorState::requestAddRemainingGates, this,
&SchematicEditorFsm::processAddRemainingGates,
Qt::QueuedConnection);
}
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 (SchematicEditorState* state, mStates) {
connect(state, &SchematicEditorState::requestLeavingState, this,
&SchematicEditorFsm::processSelect, Qt::QueuedConnection);
}
}
SchematicEditorFsm::~SchematicEditorFsm() noexcept {
leaveCurrentState();
qDeleteAll(mStates);
mStates.clear();
}
/*******************************************************************************
* Event Handlers
******************************************************************************/
bool SchematicEditorFsm::processSelect() noexcept {
return setNextState(State::SELECT);
}
bool SchematicEditorFsm::processAddComponent(
const QString& searchTerm) noexcept {
State oldState = mCurrentState;
if (!setNextState(State::ADD_COMPONENT)) {
return false;
}
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processAddComponent(searchTerm)) {
return true;
}
}
setNextState(oldState); // restore previous state
return false;
}
bool SchematicEditorFsm::processAddComponent(const Uuid& cmp,
const Uuid& symbVar) noexcept {
State oldState = mCurrentState;
if (!setNextState(State::ADD_COMPONENT)) {
return false;
}
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processAddComponent(cmp, symbVar)) {
return true;
}
}
setNextState(oldState); // restore previous state
return false;
}
bool SchematicEditorFsm::processAddRemainingGates(
const Uuid& cmp, const std::optional<Uuid>& gate) noexcept {
State oldState = mCurrentState;
if (!setNextState(State::ADD_COMPONENT)) {
return false;
}
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processAddRemainingGates(cmp, gate)) {
return true;
}
}
setNextState(oldState); // restore previous state
return false;
}
bool SchematicEditorFsm::processAddNetLabel() noexcept {
return setNextState(State::ADD_LABEL);
}
bool SchematicEditorFsm::processDrawPolygon() noexcept {
return setNextState(State::DRAW_POLYGON);
}
bool SchematicEditorFsm::processAddText() noexcept {
return setNextState(State::ADD_TEXT);
}
bool SchematicEditorFsm::processAddImage(const QByteArray& data,
const QString& format,
const QString& basename) noexcept {
State oldState = mCurrentState;
if (!setNextState(State::ADD_IMAGE)) {
return false;
}
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processAddImage(data, format, basename)) {
return true;
}
}
setNextState(oldState); // restore previous state
return false;
}
bool SchematicEditorFsm::processDrawWire() noexcept {
return setNextState(State::DRAW_WIRE);
}
bool SchematicEditorFsm::processDrawBus(std::optional<Uuid> busUuid) noexcept {
if (setNextState(State::DRAW_BUS)) {
if (SchematicEditorState_DrawBus* s =
dynamic_cast<SchematicEditorState_DrawBus*>(
mStates.value(mCurrentState))) {
s->selectBus(busUuid);
}
return true;
}
return false;
}
bool SchematicEditorFsm::processMeasure() noexcept {
return setNextState(State::MEASURE);
}
bool SchematicEditorFsm::processAbortCommand() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processAbortCommand()) {
return true;
}
}
// Go to the select state
return setNextState(State::SELECT);
}
bool SchematicEditorFsm::processSelectAll() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processSelectAll()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processCut() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processCut()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processCopy() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processCopy()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processPaste() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processPaste()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processMove(const Point& delta) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processMove(delta)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processRotate(const Angle& rotation) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processRotate(rotation)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processMirror(Qt::Orientation orientation) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processMirror(orientation)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processSnapToGrid() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processSnapToGrid()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processResetAllTexts() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processResetAllTexts()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processRemove() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processRemove()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processEditProperties() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processEditProperties()) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processKeyPressed(
const GraphicsSceneKeyEvent& e) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processKeyPressed(e)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processKeyReleased(
const GraphicsSceneKeyEvent& e) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processKeyReleased(e)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processGraphicsSceneMouseMoved(
const GraphicsSceneMouseEvent& e) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneMouseMoved(e)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processGraphicsSceneLeftMouseButtonPressed(
const GraphicsSceneMouseEvent& e) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneLeftMouseButtonPressed(e)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processGraphicsSceneLeftMouseButtonReleased(
const GraphicsSceneMouseEvent& e) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneLeftMouseButtonReleased(e)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processGraphicsSceneLeftMouseButtonDoubleClicked(
const GraphicsSceneMouseEvent& e) noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processGraphicsSceneLeftMouseButtonDoubleClicked(e)) {
return true;
}
}
return false;
}
bool SchematicEditorFsm::processGraphicsSceneRightMouseButtonReleased(
const GraphicsSceneMouseEvent& e) noexcept {
if (SchematicEditorState* 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 SchematicEditorFsm::processChangedSelection() noexcept {
if (SchematicEditorState* state = getCurrentStateObj()) {
if (state->processChangedSelection()) {
return true;
}
}
return false;
}
/*******************************************************************************
* Private Methods
******************************************************************************/
SchematicEditorState* SchematicEditorFsm::getCurrentStateObj() const noexcept {
return mStates.value(mCurrentState, nullptr);
}
bool SchematicEditorFsm::setNextState(State state) noexcept {
if (state == mCurrentState) {
return true;
}
if (!leaveCurrentState()) {
return false;
}
if (!enterNextState(state)) {
enterNextState(State::SELECT);
return false;
}
return true;
}
bool SchematicEditorFsm::leaveCurrentState() noexcept {
if ((getCurrentStateObj()) && (!getCurrentStateObj()->exit())) {
return false;
}
switch (mCurrentState) {
case State::SELECT:
// Only memorize states other than SELECT.
break;
case State::ADD_COMPONENT:
// The "add component" 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 SchematicEditorFsm::enterNextState(State state) noexcept {
Q_ASSERT(mCurrentState == State::IDLE);
SchematicEditorState* nextState = mStates.value(state, nullptr);
if ((nextState) && (!nextState->entry())) {
return false;
}
mCurrentState = state;
return true;
}
bool SchematicEditorFsm::switchToPreviousState() noexcept {
State nextState = mPreviousState;
if ((nextState == mCurrentState) || (nextState == State::IDLE)) {
nextState = State::SELECT;
}
return setNextState(nextState);
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb