int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
{
    // Main loop: keep receiving events
    while( OPT_TOOL_EVENT evt = Wait() )
    {
        // Should selected items be added to the current selection or
        // become the new selection (discarding previously selected items)
        m_additive = evt->Modifier( MD_SHIFT );

        // single click? Select single object
        if( evt->IsClick( BUT_LEFT ) )
        {
            if( evt->Modifier( MD_CTRL ) && !m_editModules )
            {
                highlightNet( evt->Position() );
            }
            else
            {
                if( !m_additive )
                    clearSelection();

                selectCursor( evt->Position() );
            }
        }

        // right click? if there is any object - show the context menu
        else if( evt->IsClick( BUT_RIGHT ) )
        {
            bool emptySelection = m_selection.Empty();

            if( emptySelection )
                selectCursor( evt->Position() );

            CONTEXT_MENU& contextMenu = m_menu.Generate( m_selection );

            if( contextMenu.GetMenuItemCount() > 0 )
                SetContextMenu( &contextMenu, CMENU_NOW );

            m_preliminary = emptySelection;
        }

        // double click? Display the properties window
        else if( evt->IsDblClick( BUT_LEFT ) )
        {
            if( m_selection.Empty() )
                selectCursor( evt->Position() );

            m_toolMgr->RunAction( COMMON_ACTIONS::properties );
        }

        // drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
        else if( evt->IsDrag( BUT_LEFT ) )
        {
            if( m_additive )
            {
                m_preliminary = false;

                selectMultiple();
            }
            else if( m_selection.Empty() )
            {
                m_preliminary = false;

                // There is nothing selected, so try to select something
                if( !selectCursor( getView()->ToWorld( getViewControls()->GetMousePosition() ), false ) )
                {
                    // If nothings has been selected or user wants to select more
                    // draw the selection box
                    selectMultiple();
                }
                else
                {
                    m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
                }
            }

            else
            {
                // Check if dragging has started within any of selected items bounding box
                if( selectionContains( evt->Position() ) )
                {
                    // Yes -> run the move tool and wait till it finishes
                    m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
                }
                else
                {
                    // No -> clear the selection list
                    clearSelection();
                }
            }
        }

        else if( evt->IsAction( &COMMON_ACTIONS::selectionCursor ) )
        {
            // GetMousePosition() is used, as it is independent of snapping settings
            selectCursor( getView()->ToWorld( getViewControls()->GetMousePosition() ) );
        }

        else if( evt->IsAction( &COMMON_ACTIONS::find ) )
        {
            find( *evt );
        }

        else if( evt->IsAction( &COMMON_ACTIONS::findMove ) )
        {
            findMove( *evt );
        }

        else if( evt->IsAction( &COMMON_ACTIONS::selectItem ) )
        {
            SelectItem( *evt );
        }

        else if( evt->IsAction( &COMMON_ACTIONS::unselectItem ) )
        {
            UnselectItem( *evt );
        }

        else if( evt->IsCancel() || evt->Action() == TA_UNDO_REDO ||
                 evt->IsAction( &COMMON_ACTIONS::selectionClear ) )
        {
            clearSelection();
        }

        else if( evt->IsAction( &COMMON_ACTIONS::selectConnection ) )
        {
            selectConnection( *evt );
        }

        else if( evt->IsAction( &COMMON_ACTIONS::selectCopper ) )
        {
            selectCopper( *evt );
        }

        else if( evt->IsAction( &COMMON_ACTIONS::selectNet ) )
        {
            selectNet( *evt );
        }

        else if( evt->Action() == TA_CONTEXT_MENU_CLOSED )
        {
            if( m_preliminary )
                clearSelection();
        }
    }

    // This tool is supposed to be active forever
    assert( false );

    return 0;
}
Beispiel #2
0
/*
 * TODO:
 * ====
 * - display value of a port (nice to have)
 * - triangle port (nice to have)
 * - mouse over (over a node or port) (nice to have)
 * - round shaped nodes (nice to have)
*/

namespace Magus
{
    //****************************************************************************/
    QtNodeEditor::QtNodeEditor(QWidget* parent) : QWidget(parent)
    {
        QVBoxLayout* mainLayout = new QVBoxLayout;
        mView = new QGraphicsView(this);
        mScene = new QtNodeGraphicsScene();
        mScene->installEventFilter(this);
        mView->setScene(mScene);
        mView->setRenderHint(QPainter::Antialiasing, true);
        mView->setInteractive(true);
        mView->setMouseTracking(true);
        mainLayout->addWidget(mView);
        mLastRemovedNode = 0;
        mRubberBand = 0;
        mZoom = 1.0f;
        mFisheyeViewEnabled = false;
        mFisheyeMaxZoom = 1.0f;
        mFisheyeSteps = 5;
        mHeaderTitleIcon = NODE_HEADER_COMPOUND_ICON;
        mAction1Icon = NODE_HEADER_ACTION1_ICON;
        mAction2Icon = NODE_HEADER_ACTION2_ICON;
        mCompoundNodeDropped = 0;
        mRubberbandSelection = false;
        mContextMenuEnabled = true;
        mContextMenu = new QMenu(this);
        mContextMenu->addAction(new QAction(NODE_ACTION_DELETE, this));
        mContextMenu->addAction(new QAction(NODE_ACTION_CENTER, this));
        mZoomSubMenu = mContextMenu->addMenu(NODE_ACTION_ZOOM);
        QAction* action;
        QActionGroup actionGroupZoom(mZoomSubMenu);
        actionGroupZoom.setExclusive(true);
        action = new QAction(NODE_ACTION_ZOOM_10, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_25, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_50, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_75, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_90, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_100, this);
        action->setCheckable(true);
        action->setChecked(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_150, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_200, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_250, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        action = new QAction(NODE_ACTION_ZOOM_300, this);
        action->setCheckable(true);
        actionGroupZoom.addAction(action);
        mZoomSubMenu->addActions(actionGroupZoom.actions());

        mFisheyeViewSubMenu = mContextMenu->addMenu(NODE_ACTION_FISHEY_VIEW);
        QActionGroup actionGroupFisheye(mFisheyeViewSubMenu);
        actionGroupFisheye.setExclusive(true);
        action = new QAction(NODE_ACTION_FISHEYE_DISABLED, this);
        action->setCheckable(true);
        action->setChecked(true);
        actionGroupFisheye.addAction(action);
        action = new QAction(NODE_ACTION_FISHEYE_NORMAL, this);
        action->setCheckable(true);
        actionGroupFisheye.addAction(action);
        action = new QAction(NODE_ACTION_FISHEYE_NORMAL_SUBTLE, this);
        action->setCheckable(true);
        actionGroupFisheye.addAction(action);
        action = new QAction(NODE_ACTION_FISHEYE_LARGE, this);
        action->setCheckable(true);
        actionGroupFisheye.addAction(action);
        action = new QAction(NODE_ACTION_FISHEYE_LARGE_SUBTLE, this);
        action->setCheckable(true);
        actionGroupFisheye.addAction(action);
        mFisheyeViewSubMenu->addActions(actionGroupFisheye.actions());

        mContextMenu->addAction(new QAction(NODE_ACTION_SELECTED_TO_COMPOUND, this));
        mContextMenu->addAction(new QAction(NODE_ACTION_COLLAPSE_ALL, this));
        mContextMenu->addAction(new QAction(NODE_ACTION_EXPAND_ALL, this));
        mContextMenu->addAction(new QAction(NODE_ACTION_EXPAND_COMPOUNDS, this));
        mContextMenu->addAction(new QAction(NODE_ACTION_CENTER, this));

        setMenuZoomEnabled(true);
        setMenuSelectionToCompoundEnabled(true);
        setMenuCollapseExpandEnabled(true);
        setMenuExpandCompoundsEnabled(true);
        setMenuFisheyeViewEnabled(true);
        setContextMenuPolicy(Qt::CustomContextMenu);
        connect(mContextMenu, SIGNAL(triggered(QAction*)), this, SLOT(contextMenuItemSelected(QAction*)));
        setLayout(mainLayout);
    }

    //****************************************************************************/
    QtNodeEditor::~QtNodeEditor(void)
    {
    }

    //****************************************************************************/
    void QtNodeEditor::setContextMenuEnabled(bool enabled)
    {
        mContextMenuEnabled = enabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setCompoundHeaderTitleIcon(const QString& fileNameIcon)
    {
        mHeaderTitleIcon = fileNameIcon;
    }

    //****************************************************************************/
    void QtNodeEditor::setCompoundAction1Icon(const QString& fileNameIcon)
    {
        mAction1Icon = fileNameIcon;
    }

    //****************************************************************************/
    void QtNodeEditor::setCompoundAction2Icon(const QString& fileNameIcon)
    {
        mAction2Icon = fileNameIcon;
    }

    //****************************************************************************/
    bool QtNodeEditor::isContextMenuEnabled(void)
    {
        return mContextMenuEnabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setMenuZoomEnabled(bool enabled)
    {
        mMenuZoomEnabled = enabled;
        QAction* action = getActionFromContextMenu(NODE_ACTION_ZOOM);
        if (action)
            action->setVisible(enabled);
    }

    //****************************************************************************/
    bool QtNodeEditor::isMenuZoomEnabled(void)
    {
        return mMenuZoomEnabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setMenuSelectionToCompoundEnabled(bool enabled)
    {
        mMenuSelectionToCompoundEnabled = enabled;
        QAction* action = getActionFromContextMenu(NODE_ACTION_SELECTED_TO_COMPOUND);
        if (action)
            action->setVisible(enabled);
    }

    //****************************************************************************/
    bool QtNodeEditor::isMenuSelectionToCompoundEnabled(void)
    {
        return mMenuSelectionToCompoundEnabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setMenuCollapseExpandEnabled(bool enabled)
    {
        mMenuCollapseExpandEnabled = enabled;
        QAction* action = getActionFromContextMenu(NODE_ACTION_EXPAND_ALL);
        if (action)
            action->setVisible(enabled);
        action = getActionFromContextMenu(NODE_ACTION_COLLAPSE_ALL);
        if (action)
            action->setVisible(enabled);
    }

    //****************************************************************************/
    bool QtNodeEditor::isMenuCollapseExpandEnabled(void)
    {
        return mMenuCollapseExpandEnabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setMenuExpandCompoundsEnabled(bool enabled)
    {
        mMenuExpandCompoundsEnabled = enabled;
        QAction* action = getActionFromContextMenu(NODE_ACTION_EXPAND_COMPOUNDS);
        if (action)
            action->setVisible(enabled);
    }

    //****************************************************************************/
    bool QtNodeEditor::isMenuExpandCompoundsEnabled(void)
    {
        return mMenuExpandCompoundsEnabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setMenuFisheyeViewEnabled(bool enabled)
    {
        mMenuFisheyeViewEnabled = enabled;
        QAction* action = getActionFromContextMenu(NODE_ACTION_FISHEY_VIEW);
        if (action)
            action->setVisible(enabled);
    }

    //****************************************************************************/
    bool QtNodeEditor::isMenuFisheyeViewEnabled(void)
    {
        return mMenuFisheyeViewEnabled;
    }

    //****************************************************************************/
    void QtNodeEditor::setFisheyeView(bool enabled, qreal maxZoom, unsigned int steps)
    {
        mFisheyeViewEnabled = enabled;
        mFisheyeMaxZoom = maxZoom;
        mFisheyeSteps = steps;
    }

    //****************************************************************************/
    QGraphicsItem* QtNodeEditor::itemAtExceptActiveConnection(const QPointF& pos)
    {
        QList<QGraphicsItem*> items = mScene->items(QRectF(pos - QPointF(1,1), QSize(3,3)));
        bool isActive = isActiveConnection();

        foreach(QGraphicsItem* item, items)
        {
            // If there is an active connection, it is not returned as a selected item
            // Finalized (established) connections are returned
            if (item->isVisible())
            {
                if (isConnection(item))
                {
                    if (!isActive)
                        return item;
                }
                else
                    return item;
            }
        }

        return 0;
    }

    //****************************************************************************/
    QtCompoundNode* QtNodeEditor::nodeOverCompound(QtNode* node)
    {
        if (!node)
            return 0;

        QtCompoundNode* compound;
        QList<QGraphicsItem*> items = mScene->items();
        int subType;
        qreal halfWidth;
        foreach(QGraphicsItem* item, items)
        {
            if (isCompoundNode(item) && item->isVisible())
            {
                compound = static_cast<QtCompoundNode*>(item);
                if (node != compound && !compound->isSelected())
                {
                    halfWidth = 0.5 * compound->getWidth();
                    if (node->scenePos().x() > compound->scenePos().x() - halfWidth &&
                        node->scenePos().x() < compound->scenePos().x() + halfWidth &&
                        node->scenePos().y() > compound->scenePos().y() &&
                        node->scenePos().y() < compound->scenePos().y() + compound->getHeigth())
                    {
                        return compound;
                    }
                }
            }
        }

        return 0;
    }

    //****************************************************************************/
    bool QtNodeEditor::eventFilter(QObject* object, QEvent* event)
    {
        QGraphicsSceneMouseEvent* mouseEvent = (QGraphicsSceneMouseEvent*) event;
        QGraphicsItem* item = 0;
        switch ((int) event->type())
        {
            case QEvent::GraphicsSceneMousePress:
                mouseClickHandler(mouseEvent);
            break;

            case QEvent::GraphicsSceneMouseDoubleClick:
                mouseDoubleClickHandler(mouseEvent);
            break;

            case QEvent::GraphicsSceneMouseMove:
            {
                mouseMoveHandler(mouseEvent);
            }
            break;

            case QEvent::GraphicsSceneMouseRelease:
            {
                mouseReleaseHandler(mouseEvent);
            }
            break;
        }
        return QObject::eventFilter(object, event);
    }

    //****************************************************************************/
    bool QtNodeEditor::mouseClickHandler(QGraphicsSceneMouseEvent* mouseEvent)
    {
        switch ((int) mouseEvent->button())
        {
            case Qt::LeftButton:
            {
                QGraphicsItem* item = itemAtExceptActiveConnection(mouseEvent->scenePos());
                if (!item)
                {
                    // Left-click on the canvas, but no item clicked, so deselect nodes and connections
                    deselectAll();
                    mRubberbandSelection = true;
                    mLastMousePosition.setX(mouseEvent->lastScenePos().x());
                    mLastMousePosition.setY(mouseEvent->lastScenePos().y());
                    return true;
                }
                mRubberbandSelection = false;

                // Delegate to the node; either the node itself is clicked, one of its children or a connection
                QtNode* node;
                int type = 0;
                if (item->data(NODE_KEY_GRAPHIC_ITEM_TYPE).isValid())
                {
                    type = item->data(NODE_KEY_GRAPHIC_ITEM_TYPE).toInt();
                    if (NODE_VALUE_TYPE_CONNECTION == type)
                    {
                        // ======================= Handle selected connection =======================
                        QtConnection* connection = static_cast<QtConnection*>(item);
                        selectConnection(connection);
                    }
                    else if (NODE_VALUE_TYPE_NODE == type)
                    {
                        // ======================= The node itself is clicked =======================
                        node = static_cast<QtNode*>(item);
                        selectNode(node, mouseEvent);
                    }
                    else if (NODE_VALUE_TYPE_HEADER_ICON == type || NODE_VALUE_TYPE_HEADER_TITLE == type)
                    {
                        // ======================= The header title or header icon is clicked =======================
                        node = static_cast<QtNode*>(item->parentItem());
                        selectNode(node, mouseEvent);
                    }
                    else
                    {
                        // A child item of the node is clicked
                        deselectNodes();
                        deselectConnections();
                        node = static_cast<QtNode*>(item->parentItem());

                        if (NODE_VALUE_TYPE_PORT == type)
                        {
                            // ======================= Port is clicked =======================
                            // Either make a connection to another port, or create a new connection
                            QtNode* baseNode = getNodeWithActiveConnection();
                            if (baseNode == 0)
                            {
                                // There is no active connection, so start one
                                node->mouseLeftClickHandler(mouseEvent, item, NODE_ACTION_BASE);
                                setCursor(Qt::ClosedHandCursor);
                            }
                            else if (baseNode != node)
                            {
                                // There is an active connection and the selected port is not part of the baseNode,
                                // so try to establish a connection with the other node
                                if (node->mouseLeftClickHandler(mouseEvent, item, NODE_ACTION_TARGET, baseNode->mActiveConnection))
                                {
                                    // The connection was established, so the active connection on the basenode can be set to 0
                                    baseNode->mActiveConnection = 0;
                                    setCursor(Qt::ArrowCursor);
                                }
                            }
                        }
                        else
                        {
                            node->mouseLeftClickHandler(mouseEvent, item); // Don't do anything with the node after this; it may be deleted
                        }
                    }
                    return true;
                }
            }
            break;

            case Qt::RightButton:
            {
                if (mContextMenuEnabled)
                {
                    QPoint pos;
                    pos.setX(mouseEvent->lastScreenPos().x());
                    pos.setY(mouseEvent->lastScreenPos().y());
                    showContextMenu(pos);
                }
                else
                    deselectAll();

                return true;
            }
            break;
        }

        //mouseEvent->accept();
        return true;
    }