Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
f187d7f67This 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