82a6d74d创建于 2025年4月30日历史提交
/***************************************************************************
                              mouseactions.cpp
                             ------------------
    begin                : Thu Aug 28 2003
    copyright            : (C) 2003 by Michael Margraf
    email                : michael.margraf@alumni.tu-berlin.de
 ***************************************************************************/

/* Copyright (C) 2014 Guilherme Brondani Torri <guitorri@gmail.com>        */

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include "mouseactions.h"
#include "components/component.h"
#include "components/componentdialog.h"
#include "components/optimizedialog.h"
#include "components/spicedialog.h"
#include "components/spicefile.h"
#include "components/vacomponent.h"
#include "diagrams/diagramdialog.h"
#include "diagrams/markerdialog.h"
#include "diagrams/tabdiagram.h"
#include "diagrams/timingdiagram.h"
#include "dialogs/labeldialog.h"
#include "dialogs/textboxdialog.h"
#include "dialogs/tuner.h"
#include "extsimkernels/customsimdialog.h"
#include "extsimkernels/spicelibcompdialog.h"
#include "main.h"
#include "module.h"
#include "node.h"
#include "qucs.h"
#include "schematic.h"
#include "spicecomponents/sp_customsim.h"

#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QDebug>
#include <QEvent>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QTextStream>
#include <qt3_compat/q3ptrlist.h>

#include <climits>
#include <cstdlib>

#define MIN_SELECT_SIZE 5.0

QAction *formerAction; // remember action before drag n'drop etc.

MouseActions::MouseActions(QucsApp *App_)
{
    App = App_;          // pointer to main app
    if(selElem != nullptr){
      selElem = nullptr;         // no component/diagram is selected
    }
    isMoveEqual = false; // mouse cursor move x and y the same way
    focusElement = 0;    //element being interacted with mouse

    // ...............................................................
    // initialize menu appearing by right mouse button click on component
    ComponentMenu = new QMenu(QucsMain);
    focusMEvent = new QMouseEvent(QEvent::MouseButtonPress,
                                  QPointF(0, 0),
                                  QPointF(0, 0),
                                  Qt::NoButton,
                                  Qt::NoButton,
                                  Qt::NoModifier);
}

MouseActions::~MouseActions()
{
    delete ComponentMenu;
    delete focusMEvent;
}

// -----------------------------------------------------------
void MouseActions::setPainter(Schematic *Doc)
{
    // contents to viewport transformation

    Doc->PostPaintEvent(_Translate, -Doc->contentsX(), -Doc->contentsY());
    Doc->PostPaintEvent(_Scale, Doc->getScale(), Doc->getScale());
    Doc->PostPaintEvent(_Translate, -Doc->getViewX1(), -Doc->getViewY1());
    Doc->PostPaintEvent(_NotRop);
}

// -----------------------------------------------------------
bool MouseActions::pasteElements(Schematic *Doc)
{
    QClipboard *cb = QApplication::clipboard(); // get system clipboard
    QString s = cb->text(QClipboard::Clipboard);
    QTextStream stream(&s, QIODevice::ReadOnly);
    movingElements.clear();
    if (!Doc->paste(&stream, &movingElements))
        return false;

    int xmax, xmin, ymax, ymin;
    xmin = ymin = INT_MAX;
    xmax = ymax = INT_MIN;
    // First, get the max and min coordinates of all selected elements.
    for (auto* pe : movingElements) {
        if (pe->Type == isWire) {
            if (pe->x1 < xmin)
                xmin = pe->x1;
            if (pe->x2 > xmax)
                xmax = pe->x2;
            if (pe->y1 < ymin)
                ymin = pe->y1;
            if (pe->y2 > ymax)
                ymax = pe->y2;
        } else {
            if (pe->cx < xmin)
                xmin = pe->cx;
            if (pe->cx > xmax)
                xmax = pe->cx;
            if (pe->cy < ymin)
                ymin = pe->cy;
            if (pe->cy > ymax)
                ymax = pe->cy;
        }
    }

    xmin = -((xmax + xmin) >> 1); // calculate midpoint
    ymin = -((ymax + ymin) >> 1);
    Doc->setOnGrid(xmin, ymin);

    // moving with mouse cursor in the midpoint
    for (auto* pe : movingElements)
        if (pe->Type & isLabel) {
            pe->cx += xmin;
            pe->x1 += xmin;
            pe->cy += ymin;
            pe->y1 += ymin;
        } else
            pe->setCenter(xmin, ymin, true);

    return true;
}

// -----------------------------------------------------------
void MouseActions::editLabel(Schematic *Doc, WireLabel *pl)
{
    LabelDialog *Dia = new LabelDialog(pl, Doc);
    int Result = Dia->exec();
    if (Result == 0)
        return;

    QString Name = Dia->NodeName->text();
    QString Value = Dia->InitValue->text();
    delete Dia;

    if (Name.isEmpty() && Value.isEmpty()) { // if nothing entered, delete label
        pl->pOwner->Label = 0;               // delete name of wire
        delete pl;
    } else {
        /*    Name.replace(' ', '_');	// label must not contain spaces
    while(Name.at(0) == '_') Name.remove(0,1);  // must not start with '_'
    if(Name.isEmpty()) return;
    if(Name == pl->Name) return;*/
        if (Result == 1)
            return; // nothing changed

        int old_x2 = pl->x2;
        pl->setName(Name); // set new name
        pl->initValue = Value;
        if (pl->cx > (pl->x1 + (pl->x2 >> 1)))
            pl->x1 -= pl->x2 - old_x2; // don't change position due to text width
    }

    Doc->updateAllBoundingRect();
    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
// Reinserts all elements (moved by the user) back into the schematic.
void MouseActions::endElementMoving(Schematic *Doc,
                                    QList<Element*> *movElements) {
  for (auto* pe : *movElements) {
    //    pe->isSelected = false;  // deselect first (maybe afterwards pe ==
    //    NULL)
    switch (pe->Type) { // FIXME: use casts.
    case isWire:
      if (pe->x1 == pe->x2)
        if (pe->y1 == pe->y2) {
          // Delete wires with zero length, but preserve label.
          if (((Wire *)pe)->Label) {
            Doc->insertNodeLabel((WireLabel *)((Wire *)pe)->Label);
            ((Wire *)pe)->Label = nullptr;
          }
          delete (Wire *)pe;
          break;
        }

      Doc->insertWire((Wire *)pe);
      break;
    case isDiagram:
      Doc->a_Diagrams->append((Diagram *)pe);
      break;
    case isPainting:
      Doc->a_Paintings->append((Painting *)pe);
      break;
    case isComponent:
    case isAnalogComponent:
    case isDigitalComponent:
      Doc->insertRawComponent((Component *)pe, false);
      break;
    case isMovingLabel:
    case isHMovingLabel:
    case isVMovingLabel:
      Doc->insertNodeLabel((WireLabel *)pe);
      break;
    case isMarker:
      assert(dynamic_cast<Marker *>(pe));
      break;
    }
  }

  movElements->clear();

  if ((MAx3 != 0) || (MAy3 != 0)) // moved or put at the same place ?
    Doc->setChanged(true, true);

  // Position of some elements of schematic has changed. This change
  // is "external" to schematic and its state must be updated to
  // take the changes into account
  auto totalBounds = Doc->allBoundingRect();
  Doc->enlargeView(totalBounds.left(), totalBounds.top(), totalBounds.right(), totalBounds.bottom());

  Doc->viewport()->update();
}

// -----------------------------------------------------------
// Moves elements in "movElements" by x/y
void MouseActions::moveElements(QList<Element*> *movElements, int x, int y)
{
    Wire *pw;
    for (auto* pe : *movElements) {
        if (pe->Type == isWire) {
            pw = (Wire *) pe; // connected wires are not moved completely

            if (((uintptr_t) pw->Port1) > 3) {
                pw->x1 += x;
                pw->y1 += y;
                if (pw->Label) {
                    pw->Label->cx += x;
                    pw->Label->cy += y;
                }
            } else {
                if ((uintptr_t) (pw->Port1) & 1) {
                    pw->x1 += x;
                }
                if ((uintptr_t) (pw->Port1) & 2) {
                    pw->y1 += y;
                }
            }

            if (((uintptr_t) pw->Port2) > 3) {
                pw->x2 += x;
                pw->y2 += y;
            } else {
                if ((uintptr_t) (pw->Port2) & 1)
                    pw->x2 += x;
                if ((uintptr_t) (pw->Port2) & 2)
                    pw->y2 += y;
            }

            if (pw->Label) { // root of node label must lie on wire
                if (pw->Label->cx < pw->x1)
                    pw->Label->cx = pw->x1;
                if (pw->Label->cy < pw->y1)
                    pw->Label->cy = pw->y1;
                if (pw->Label->cx > pw->x2)
                    pw->Label->cx = pw->x2;
                if (pw->Label->cy > pw->y2)
                    pw->Label->cy = pw->y2;
            }

        } else
            pe->setCenter(x, y, true);
    }
}

// ***********************************************************************
// **********                                                   **********
// **********       Functions for serving mouse moving          **********
// **********                                                   **********
// ***********************************************************************
void MouseActions::MMoveElement(Schematic *Doc, QMouseEvent *Event)
{
    if (selElem == nullptr)
        return;

    //  qDebug() << "MMoveElement got selElem";

    QPoint contentsCoordinates = Event->pos();
    QPoint modelCoordinates = Doc->contentsToModel(contentsCoordinates);

    int x = contentsCoordinates.x();
    int y = contentsCoordinates.y();
    int fx = modelCoordinates.x();
    int fy = modelCoordinates.y();
    int gx = fx;
    int gy = fy;
    Doc->setOnGrid(gx, gy);

    //QPainter painter(Doc->viewport());
    setPainter(Doc);

    if (selElem->Type == isPainting) {
        Doc->PostPaintEvent(_NotRop, 0, 0, 0, 0);
        ((Painting *) selElem)->MouseMoving(Doc, x, y, gx, gy, Doc, fx, fy);
        Doc->viewport()->update();
        return;
    } // of "if(isPainting)"

    //  Component *comp = (Component*)selElem;
    //qDebug() << "desc" << comp->Description << "gx" << gx << "gy" << gy;

    selElem->setCenter(gx, gy);
    selElem->paintScheme(Doc); // paint scheme at new position
    Doc->viewport()->update();
}

/**
 * @brief draws wire aiming cross
 * @param Doc - pointer to Schematics object
 * @param aimX  - model x-coordinate of aim
 * @param aimY  - model y-coordinate of aim
 */
static void paintAim(Schematic *Doc, int aimX, int aimY) {
    // What we want to do here is to draw a cross centered at (aimX, aimY)
    // which lines don't touch the bounds of visible part of schematic,
    // i.e. there should be a little gap between the line ends and picture
    // bounds:
    //
    //       Bad                Good
    // +---------+---+    +--------------+
    // |         |   |    |         |    |
    // +---------+---+    | --------+--- |
    // |         |   |    |         |    |
    // |         |   |    |         |    |
    // +---------+---+    +--------------+
    //
    // The cross is drawn using Schematic 'PostPaintEvent' subsystem.
    // This is not a requirement, it's just an inherited implementation.
    // Maybe there is a better way to do it, but until it's found let's
    // stick to the way things already work.
    //
    // PostPaintEvent subsystem operates in *model* coordinates – the coordinates
    // used to locate components, wires, etc.
    //
    // To draw the cross we want we have to follow these steps:
    //   1. Find the size of viewport (visible part of schematic)
    //   2. Shrink it a bit. The resulting rectangle is the *bounding* for the cross
    //   3. Transform bounding rectangle coordinates to model coordinate system.
    //   4. Using resulting 'in-model' coordinates of bounding rectangle, post
    //      two 'paint line' events

    const QRect viewportAimBounds =
        QRect{0, 0, Doc->viewport()->width(), Doc->viewport()->height()}
        .marginsRemoved(QMargins{2, 2, 2, 2});

    const QRect aimBounds{
        Doc->viewportToModel(viewportAimBounds.topLeft()),
        Doc->viewportToModel(viewportAimBounds.bottomRight())
    };

    // Horizontal line of cross
    Doc->PostPaintEvent(_Line, aimBounds.left(), aimY, aimBounds.right(), aimY);
    // Vertical line of cross
    Doc->PostPaintEvent(_Line, aimX, aimBounds.top(), aimX, aimBounds.bottom());
}

//paint ghost line - horizontal
static void paintGhostLineH(Schematic *Doc, int fx, int fy, int fxx)
{
    Doc->PostPaintEvent(_Line, fx, fy - 1, fxx, fy - 1);
    Doc->PostPaintEvent(_Line, fx, fy + 1, fxx, fy + 1);
}

//paint ghost line - vertical
static void paintGhostLineV(Schematic *Doc, int fx, int fy, int fyy)
{
    Doc->PostPaintEvent(_Line, fx - 1, fy, fx - 1, fyy);
    Doc->PostPaintEvent(_Line, fx + 1, fy, fx + 1, fyy);
}

// -----------------------------------------------------------
/**
 * @brief MouseActions::MMoveWire2 Paint wire as it is being drawn with mouse.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveWire2(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx2 = inModel.x();
    MAy2 = inModel.y();
    Doc->setOnGrid(MAx2, MAy2);
    paintAim(Doc, MAx2, MAy2); //let we paint aim cross

    Doc->showEphemeralWire({MAx3, MAy3}, {MAx2, MAy2});

    QucsMain->MouseDoubleClickAction = &MouseActions::MDoubleClickWire2;
    Doc->viewport()->update();
}

/**
 * @brief MouseActions::MMoveWire1 Paint hair cross for "insert wire" mode
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveWire1(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();
    Doc->setOnGrid(MAx3, MAy3);
    paintAim(Doc, MAx3, MAy3);

    inModel = Doc->contentsToModel(
        QPoint{
            Doc->contentsX() + Doc->viewport()->width() - 1 - 2,
            Doc->contentsY() + Doc->viewport()->height() - 1 - 2
        }
    );
    MAx2 = inModel.x();
    MAx2 = inModel.y();
    Doc->viewport()->update();
}

/**
 * @brief MouseActions::MMoveSelect Paints a rectangle for select area.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveSelect(Schematic *Doc, QMouseEvent *Event)
{
    //qDebug() << "MMoveSelect " << "select area";
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx2 = inModel.x() - MAx1;
    MAy2 = inModel.y() - MAy1;
    if (isMoveEqual) { // x and y size must be equal ?
        if (abs(MAx2) > abs(MAy2)) {
            if (MAx2 < 0)
                MAx2 = -abs(MAy2);
            else
                MAx2 = abs(MAy2);
        } else {
            if (MAy2 < 0)
                MAy2 = -abs(MAx2);
            else
                MAy2 = abs(MAx2);
        }
    }

    Doc->PostPaintEvent(_SelectionRect, MAx1, MAy1, MAx2, MAy2);
}

// -----------------------------------------------------------
void MouseActions::MMoveResizePainting(Schematic *Doc, QMouseEvent *Event)
{
    setPainter(Doc);

    auto inModel = Doc->contentsToModel(Event->pos());
    MAx1 = inModel.x();
    MAy1 = inModel.y();
    Doc->setOnGrid(MAx1, MAy1);
    ((Painting *) focusElement)->MouseResizeMoving(MAx1, MAy1, Doc);
}

// -----------------------------------------------------------
// Moves components by keeping the mouse button pressed.
void MouseActions::MMoveMoving(Schematic *Doc, QMouseEvent *Event)
{
    setPainter(Doc);

    auto inModel = Doc->contentsToModel(Event->pos());
    MAx2 = inModel.x();
    MAy2 = inModel.y();

    Doc->setOnGrid(MAx2, MAy2);
    MAx3 = MAx1 = MAx2 - MAx1;
    MAy3 = MAy1 = MAy2 - MAy1;

    movingElements.clear();
    {
        // Q3PtrList is long time deprecated and has to be replaced with another
        // container type, which is not always easy. Here it's simpler to use it
        // once and go back to QList, because copySelectedElements() uses API
        // unique to Q3PtrList and to refactor it is a piece of work
        Q3PtrList<Element> temp_buffer;
        temp_buffer.setAutoDelete(false);
        Doc->copySelectedElements(&temp_buffer);
        for (auto* e : temp_buffer) { movingElements.append(e); }
    }
    Doc->viewport()->repaint();

    Wire *pw;
    // Changes the position of all moving elements by dx/dy
    for (Element *pe : movingElements) {
        if (pe->Type == isWire) {
            pw = (Wire *) pe; // connecting wires are not moved completely

            if (((uintptr_t) pw->Port1) > 3) {
                pw->x1 += MAx1;
                pw->y1 += MAy1;
            } else {
                if ((uintptr_t) (pw->Port1) & 1) {
                    pw->x1 += MAx1;
                }
                if ((uintptr_t) (pw->Port1) & 2) {
                    pw->y1 += MAy1;
                }
            }

            if (((uintptr_t) pw->Port2) > 3) {
                pw->x2 += MAx1;
                pw->y2 += MAy1;
            } else {
                if ((uintptr_t) (pw->Port2) & 1)
                    pw->x2 += MAx1;
                if ((uintptr_t) (pw->Port2) & 2)
                    pw->y2 += MAy1;
            }

            if (pw->Label) { // root of node label must lie on wire
                if (pw->Label->cx < pw->x1)
                    pw->Label->cx = pw->x1;
                if (pw->Label->cy < pw->y1)
                    pw->Label->cy = pw->y1;
                if (pw->Label->cx > pw->x2)
                    pw->Label->cx = pw->x2;
                if (pw->Label->cy > pw->y2)
                    pw->Label->cy = pw->y2;
            }

        } else
            pe->setCenter(MAx1, MAy1, true);

        pe->paintScheme(Doc);
    }

    MAx1 = MAx2;
    MAy1 = MAy2;
    QucsMain->MouseMoveAction = &MouseActions::MMoveMoving2;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseMoving;
    QucsMain->editRotate->blockSignals(true);
    QucsMain->insLabel->blockSignals(true);
    QucsMain->setMarker->blockSignals(true);
}

// -----------------------------------------------------------
// Moves components by keeping the mouse button pressed.
void MouseActions::MMoveMoving2(Schematic *Doc, QMouseEvent *Event)
{
  setPainter(Doc);

  auto inModel = Doc->contentsToModel(Event->pos());
  MAx2 = inModel.x();
  MAy2 = inModel.y();


  if ((Event->modifiers().testFlag(Qt::ControlModifier)) == 0)
    Doc->setOnGrid(MAx2, MAy2); // use grid only if CTRL key not pressed
  MAx1 = MAx2 - MAx1;
  MAy1 = MAy2 - MAy1;
  MAx3 += MAx1;
  MAy3 += MAy1; // keep track of the complete movement

  moveElements(&movingElements, MAx1, MAy1); // moves elements by MAx1/MAy1

  // paint afterwards to avoid conflict between wire and label painting
  for (auto* pe : movingElements)
    pe->paintScheme(Doc);
  //    if(pe->Type == isWire)  if(((Wire*)pe)->Label)
  //      if(!((Wire*)pe)->Label->isSelected)
  //        ((Wire*)pe)->Label->paintScheme(&painter);

  MAx1 = MAx2;
  MAy1 = MAy2;
}

/**
 * @brief MouseActions::MMovePaste Moves components after paste from clipboard.
 * @param Doc
 * @param Event
 */
void MouseActions::MMovePaste(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx1 = inModel.x();
    MAy1 = inModel.y();
    moveElements(Doc, MAx1, MAy1);
    paintElementsScheme(Doc);

    QucsMain->MouseMoveAction = &MouseActions::MMoveMoving2;
    QucsMain->MouseReleaseAction = &MouseActions::MReleasePaste;
}

// -----------------------------------------------------------
// Moves scroll bar of diagram (e.g. tabular) according the mouse cursor.
void MouseActions::MMoveScrollBar(Schematic *Doc, QMouseEvent *Event)
{
    TabDiagram *d = (TabDiagram *) focusElement;
    auto inModel = Doc->contentsToModel(Event->pos());
    int x = inModel.x();
    int y = inModel.y();

    if (d->scrollTo(MAx2, x - MAx1, y - MAy1)) {
        Doc->setChanged(true, true, 'm'); // 'm' = only the first time

        // FIXME #warning QPainter p(Doc->viewport());
        // FIXME #warning ViewPainter Painter;
        // FIXME #warning Painter.init(&p, Doc->a_Scale, -Doc->getViewX1(), -Doc->getViewY1(),
        // FIXME #warning                  Doc->contentsX(), Doc->contentsY());
        // FIXME #warning     Painter.fillRect(d->cx-d->x1, d->cy-d->y2, d->x2+d->x1, d->y2+d->y1,
        // FIXME #warning                      QucsSettings.BGColor);
        // FIXME #warning     d->paint(&Painter);
    }
}

/**
* @brief MouseActions::MMoveDelete
*   Paints a cross under the mouse cursor to show the delete mode.
* @param Doc Schematic document
* @param Event
*/
void MouseActions::MMoveDelete(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    // cannot draw on the viewport, it is displaced by the size of dock and toolbar
    Doc->PostPaintEvent(_Line, MAx3 - 15, MAy3 - 15, MAx3 + 15, MAy3 + 15, 0, 0, false);
    Doc->PostPaintEvent(_Line, MAx3 - 15, MAy3 + 15, MAx3 + 15, MAy3 - 15, 0, 0, false);
}

/**
 * @brief MouseActions::MMoveLabel Paints a label above the mouse cursor for "set wire label".
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveLabel(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    // paint marker
    Doc->PostPaintEvent(_Line, MAx3, MAy3, MAx3 + 10, MAy3 - 10);
    Doc->PostPaintEvent(_Line, MAx3 + 10, MAy3 - 10, MAx3 + 20, MAy3 - 10);
    Doc->PostPaintEvent(_Line, MAx3 + 10, MAy3 - 10, MAx3 + 10, MAy3 - 17);

    // paint A
    Doc->PostPaintEvent(_Line, MAx3 + 12, MAy3 - 12, MAx3 + 15, MAy3 - 23);
    Doc->PostPaintEvent(_Line, MAx3 + 14, MAy3 - 17, MAx3 + 17, MAy3 - 17);
    Doc->PostPaintEvent(_Line, MAx3 + 19, MAy3 - 12, MAx3 + 16, MAy3 - 23);
}

/**
 * @brief MouseActions::MMoveMarker Paints a triangle above the mouse for "set marker on graph"
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveMarker(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3, MAy3 - 2, MAx3 - 8, MAy3 - 10);
    Doc->PostPaintEvent(_Line, MAx3 + 1, MAy3 - 3, MAx3 + 8, MAy3 - 10);
    Doc->PostPaintEvent(_Line, MAx3 - 7, MAy3 - 10, MAx3 + 7, MAy3 - 10);
}

/**
 * @brief MouseActions::MMoveSetLimits Sets the cursor to a magnifying glass with a wave.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveSetLimits(Schematic *Doc, QMouseEvent *Event)
{
    // TODO: Refactor to a QRectF for easy normalisation etc.
    // Update the second point of the selection rectangle.
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();
}

/**
 * @brief MouseActions::MMoveMirrorX Paints rounded "mirror about y axis" mouse cursor
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveMirrorY(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3 - 11, MAy3 - 4, MAx3 - 9, MAy3 - 9);
    Doc->PostPaintEvent(_Line, MAx3 - 11, MAy3 - 3, MAx3 - 6, MAy3 - 3);
    Doc->PostPaintEvent(_Line, MAx3 + 11, MAy3 - 4, MAx3 + 9, MAy3 - 9);
    Doc->PostPaintEvent(_Line, MAx3 + 11, MAy3 - 3, MAx3 + 6, MAy3 - 3);
    Doc->PostPaintEvent(_Arc, MAx3 - 10, MAy3 - 8, 21, 10, 16 * 20, 16 * 140, false);
}

/**
 * @brief MouseActions::MMoveMirrorX Paints rounded "mirror about x axis" mouse cursor
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveMirrorX(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3 - 4, MAy3 - 11, MAx3 - 9, MAy3 - 9);
    Doc->PostPaintEvent(_Line, MAx3 - 3, MAy3 - 11, MAx3 - 3, MAy3 - 6);
    Doc->PostPaintEvent(_Line, MAx3 - 4, MAy3 + 11, MAx3 - 9, MAy3 + 9);
    Doc->PostPaintEvent(_Line, MAx3 - 3, MAy3 + 11, MAx3 - 3, MAy3 + 6);
    Doc->PostPaintEvent(_Arc, MAx3 - 8, MAy3 - 10, 10, 21, 16 * 110, 16 * 140, false);
}

/**
 * @brief MouseActions::MMoveMirrorX Paints "rotate" mouse cursor
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveRotate(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3 - 6, MAy3 + 8, MAx3 - 6, MAy3 + 1);
    Doc->PostPaintEvent(_Line, MAx3 - 7, MAy3 + 8, MAx3 - 12, MAy3 + 8);
    Doc->PostPaintEvent(_Arc, MAx3 - 10, MAy3 - 10, 21, 21, -16 * 20, 16 * 240, false);
}

/**
 * @brief MouseActions::MMoveActivate Paints a crossed box mouse cursor to "(de)activate" components.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveActivate(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Rect, MAx3, MAy3 - 9, 14, 10);
    Doc->PostPaintEvent(_Line, MAx3, MAy3 - 9, MAx3 + 13, MAy3);
    Doc->PostPaintEvent(_Line, MAx3, MAy3, MAx3 + 13, MAy3 - 9);
}

/**
 * @brief MouseActions::MMoveOnGrid Paints a grid beside the mouse cursor, put "on grid" mode.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveOnGrid(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3 + 10, MAy3 + 3, MAx3 + 25, MAy3 + 3);
    Doc->PostPaintEvent(_Line, MAx3 + 10, MAy3 + 7, MAx3 + 25, MAy3 + 7);
    Doc->PostPaintEvent(_Line, MAx3 + 10, MAy3 + 11, MAx3 + 25, MAy3 + 11);
    Doc->PostPaintEvent(_Line, MAx3 + 13, MAy3, MAx3 + 13, MAy3 + 15);
    Doc->PostPaintEvent(_Line, MAx3 + 17, MAy3, MAx3 + 17, MAy3 + 15);
    Doc->PostPaintEvent(_Line, MAx3 + 21, MAy3, MAx3 + 21, MAy3 + 15);
}

/**
 * @brief MouseActions::MMoveMoveTextB Paints mouse symbol for "move component text" mode.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveMoveTextB(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3 + 14, MAy3, MAx3 + 16, MAy3);
    Doc->PostPaintEvent(_Line, MAx3 + 23, MAy3, MAx3 + 25, MAy3);
    Doc->PostPaintEvent(_Line, MAx3 + 13, MAy3, MAx3 + 13, MAy3 + 3);
    Doc->PostPaintEvent(_Line, MAx3 + 13, MAy3 + 7, MAx3 + 13, MAy3 + 10);
    Doc->PostPaintEvent(_Line, MAx3 + 14, MAy3 + 10, MAx3 + 16, MAy3 + 10);
    Doc->PostPaintEvent(_Line, MAx3 + 23, MAy3 + 10, MAx3 + 25, MAy3 + 10);
    Doc->PostPaintEvent(_Line, MAx3 + 26, MAy3, MAx3 + 26, MAy3 + 3);
    Doc->PostPaintEvent(_Line, MAx3 + 26, MAy3 + 7, MAx3 + 26, MAy3 + 10);
}

/**
 * @brief MouseActions::MMoveMoveText Paint rectangle around component text being mouse moved
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveMoveText(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    int newX = inModel.x();
    int newY = inModel.y();
    MAx1 += newX - MAx3;
    MAy1 += newY - MAy3;
    MAx3 = newX;
    MAy3 = newY;

    Doc->PostPaintEvent(_Rect, MAx1, MAy1, MAx2, MAy2);
}

/**
 * @brief MouseActions::MMoveZoomIn Paints symbol beside the mouse to show the "Zoom in" modus.
 * @param Doc
 * @param Event
 */
void MouseActions::MMoveZoomIn(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx3 = inModel.x();
    MAy3 = inModel.y();

    Doc->PostPaintEvent(_Line, MAx3 + 14, MAy3, MAx3 + 22, MAy3);
    Doc->PostPaintEvent(_Line, MAx3 + 18, MAy3 - 4, MAx3 + 18, MAy3 + 4);
    Doc->PostPaintEvent(_Ellipse, MAx3 + 12, MAy3 - 6, 13, 13, 0, 0, false);
    Doc->viewport()->update();
}

// ************************************************************************
// **********                                                    **********
// **********    Functions for serving mouse button clicking     **********
// **********                                                    **********
// ************************************************************************

// Is called from several MousePress functions to show right button menu.
void MouseActions::rightPressMenu(Schematic *Doc, QMouseEvent *Event, float fX, float fY)
{
    MAx1 = int(fX);
    MAy1 = int(fY);
    focusElement = Doc->selectElement(fX, fY, false);

    if (focusElement) // remove special function (4 least significant bits)
        focusElement->Type &= isSpecialMask;

    // define menu
    ComponentMenu->clear();
    while (true) {
        if (focusElement) {
            focusElement->isSelected = true;
            QAction *editProp = new QAction(QObject::tr("Edit Properties"), QucsMain);
            QObject::connect(editProp, SIGNAL(triggered(bool)), QucsMain, SLOT(slotEditElement()));
            ComponentMenu->addAction(editProp);

            if ((focusElement->Type & isComponent) == 0)
                break;
        } else {
            /// \todo "exchange like this"
            //ComponentMenu->addAction(QucsMain->symEdit);
            //to QucsMain->symEdit->addTo(ComponentMenu);
            // see http://qt-project.org/doc/qt-4.8/qaction-qt3.html#addTo
            ComponentMenu->addAction(QucsMain->symEdit);
            ComponentMenu->addAction(QucsMain->fileSettings);
        }
        if (!QucsMain->moveText->isChecked())
            ComponentMenu->addAction(QucsMain->moveText);
        break;
    }
    while (true) {
        if (focusElement)
            if (focusElement->Type == isGraph)
                break;
        if (!QucsMain->onGrid->isChecked())
            ComponentMenu->addAction(QucsMain->onGrid);
        ComponentMenu->addAction(QucsMain->editCopy);
        if (!QucsMain->editPaste->isChecked())
            ComponentMenu->addAction(QucsMain->editPaste);
        break;
    }

    while (true) {
        if (focusElement) {
            if (focusElement->Type == isDiagram) {
                Diagram* diagram = static_cast<Diagram*>(focusElement);

                // Only show reset limits action if one or more axis is not autoscaled.
                if (diagram->Name == "Rect" &&
                    (!diagram->xAxis.autoScale || !diagram->yAxis.autoScale || !diagram->zAxis.autoScale)) {
                    ComponentMenu->addAction(QucsMain->resetDiagramLimits);
                }

                // TODO: This should probably be in qucs_init::initActions.
                QAction *actExport = new QAction(QObject::tr("Export as image"), QucsMain);
                QObject::connect(actExport,
                                 SIGNAL(triggered(bool)),
                                 QucsMain,
                                 SLOT(slotSaveDiagramToGraphicsFile()));
                ComponentMenu->addAction(actExport);
            }
        }
        break;
    }

    if (!QucsMain->editDelete->isChecked())
        ComponentMenu->addAction(QucsMain->editDelete);
    if (focusElement)
        if (focusElement->Type == isMarker) {
            ComponentMenu->addSeparator();
            QString s = QObject::tr("power matching");
            if (((Marker *) focusElement)->pGraph->Var == "Sopt")
                s = QObject::tr("noise matching");
            QAction *actPwrMatching = new QAction(s, QucsMain);
            QObject::connect(actPwrMatching,
                             SIGNAL(triggered(bool)),
                             QucsMain,
                             SLOT(slotPowerMatching()));
            ComponentMenu->addAction(actPwrMatching);
            if (((Marker *) focusElement)->pGraph->Var.left(2) == "S[") {
                QAction *act2PortMatching = new QAction(QObject::tr("2-port matching"), QucsMain);
                QObject::connect(act2PortMatching,
                                 SIGNAL(triggered(bool)),
                                 QucsMain,
                                 SLOT(slot2PortMatching()));
                ComponentMenu->addAction(act2PortMatching);
            }
        }
    do {
        if (focusElement) {
            if (focusElement->Type == isDiagram)
                break;
            if (focusElement->Type == isGraph) {
                ComponentMenu->addAction(QucsMain->graph2csv);
                break;
            }
        }
        ComponentMenu->addSeparator();
        if (focusElement)
            if (focusElement->Type & isComponent)
                if (!QucsMain->editActivate->isChecked())
                    ComponentMenu->addAction(QucsMain->editActivate);
        if (!QucsMain->editRotate->isChecked())
            ComponentMenu->addAction(QucsMain->editRotate);
        if (!QucsMain->editMirror->isChecked())
            ComponentMenu->addAction(QucsMain->editMirror);
        if (!QucsMain->editMirrorY->isChecked())
            ComponentMenu->addAction(QucsMain->editMirrorY);

        // right-click menu to go into hierarchy
        if (focusElement) {
            if (focusElement->Type & isComponent)
                if (((Component *) focusElement)->Model == "Sub")
                    if (!QucsMain->intoH->isChecked())
                        ComponentMenu->addAction(QucsMain->intoH);
        }
        // right-click menu to pop out of hierarchy
        if (!focusElement)
            if (!QucsMain->popH->isChecked())
                ComponentMenu->addAction(QucsMain->popH);
    } while (false);

    //*focusMEvent = *Event;  // remember event for "edit component" action
#if QT_VERSION >= 0x060000
    ComponentMenu->popup(Event->globalPosition().toPoint());
#else
    ComponentMenu->popup(Event->globalPos());
#endif
    Doc->viewport()->update();
}

// -----------------------------------------------------------
void MouseActions::MPressLabel(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    int x = int(fX), y = int(fY);
    Wire *pw = 0;
    WireLabel *pl = 0;
    Node *pn = Doc->selectedNode(x, y);
    if (!pn) {
        pw = Doc->selectedWire(x, y);
        if (!pw)
            return;
    }

    QString Name, Value;
    Element *pe = 0;
    // is wire line already labeled ?
    if (pw)
        pe = Doc->getWireLabel(pw->Port1);
    else
        pe = Doc->getWireLabel(pn);
    if (pe) {
        if (pe->Type & isComponent) {
            QMessageBox::information(0,
                                     QObject::tr("Info"),
                                     QObject::tr("The ground potential cannot be labeled!"));
            return;
        }
        pl = ((Conductor *) pe)->Label;
    }

    LabelDialog *Dia = new LabelDialog(pl, Doc);
    if (Dia->exec() == 0)
        return;

    Name = Dia->NodeName->text();
    Value = Dia->InitValue->text();
    delete Dia;

    if (Name.isEmpty() && Value.isEmpty()) { // if nothing entered, delete name
        if (pe) {
            if (((Conductor *) pe)->Label)
                delete ((Conductor *) pe)->Label; // delete old name
            ((Conductor *) pe)->Label = 0;
        } else {
            if (pw)
                pw->setName("", ""); // delete name of wire
            else
                pn->setName("", "");
        }
    } else {
        /*    Name.replace(' ', '_');	// label must not contain spaces
    while(Name.at(0) == '_') Name.remove(0,1);  // must not start with '_'
    if(Name.isEmpty()) return;
*/
        if (pe) {
            delete ((Conductor *) pe)->Label; // delete old name
            ((Conductor *) pe)->Label = nullptr;
        }

        int xl = x + 30;
        int yl = y - 30;
        Doc->setOnGrid(xl, yl);
        // set new name
        if (pw)
            pw->setName(Name, Value, x - pw->x1 + y - pw->y1, xl, yl);
        else
            pn->setName(Name, Value, xl, yl);
    }

    Doc->updateAllBoundingRect();
    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
void MouseActions::MPressSelect(Schematic *Doc, QMouseEvent *Event, float fX, float fY)
{
    bool Ctrl;
    if (Event->modifiers().testFlag(Qt::ControlModifier))
        Ctrl = true;
    else
        Ctrl = false;

    int No = 0;
    MAx1 = int(fX);
    MAy1 = int(fY);
    focusElement = Doc->selectElement(fX, fY, Ctrl, &No);
    isMoveEqual = false; // moving not necessarily square

    if (focusElement)
        // print define value in hex, see element.h
        qDebug() << "MPressSelect: focusElement->Type"
                 << QStringLiteral("0x%1").arg(focusElement->Type, 0, 16);
    else
        qDebug() << "MPressSelect";

    if (focusElement)
        switch (focusElement->Type) {
        case isPaintingResize: // resize painting ?
            focusElement->Type = isPainting;
            QucsMain->MouseReleaseAction = &MouseActions::MReleaseResizePainting;
            QucsMain->MouseMoveAction = &MouseActions::MMoveResizePainting;
            QucsMain->MousePressAction = 0;
            QucsMain->MouseDoubleClickAction = 0;
            Doc->grabKeyboard(); // no keyboard inputs during move actions
            // Update matching wire label highlighting
            Doc->highlightWireLabels();
            return;

        case isDiagramResize: // resize diagram ?
            if (((Diagram *) focusElement)->Name.left(4) != "Rect")
                if (((Diagram *) focusElement)->Name.at(0) != 'T')
                    if (((Diagram *) focusElement)->Name != "Curve")
                        isMoveEqual = true; // diagram must be square

            focusElement->Type = isDiagram;
            MAx1 = focusElement->cx;
            MAx2 = focusElement->x2;
            if (((Diagram *) focusElement)->State & 1) {
                MAx1 += MAx2;
                MAx2 *= -1;
            }
            MAy1 = focusElement->cy;
            MAy2 = -focusElement->y2;
            if (((Diagram *) focusElement)->State & 2) {
                MAy1 += MAy2;
                MAy2 *= -1;
            }

            QucsMain->MouseReleaseAction = &MouseActions::MReleaseResizeDiagram;
            QucsMain->MouseMoveAction = &MouseActions::MMoveSelect;
            QucsMain->MousePressAction = 0;
            QucsMain->MouseDoubleClickAction = 0;
            Doc->grabKeyboard(); // no keyboard inputs during move actions
            // Update matching wire label highlighting
            Doc->highlightWireLabels();
            return;

        case isDiagramHScroll: // scroll in tabular ?
        case isDiagramVScroll:
            if (focusElement->Type == isDiagramHScroll)
            {
                MAy1 = MAx1;
            }

            focusElement->Type = isDiagram;

            No = ((TabDiagram *) focusElement)->scroll(MAy1);

            switch (No) {
            case 1:
                Doc->setChanged(true, true, 'm'); // 'm' = only the first time
                break;
            case 2: // move scroll bar with mouse cursor
                QucsMain->MouseMoveAction = &MouseActions::MMoveScrollBar;
                QucsMain->MousePressAction = 0;
                QucsMain->MouseDoubleClickAction = 0;
                Doc->grabKeyboard(); // no keyboard inputs during move actions

                // Remember initial scroll bar position.
                MAx2 = int(((TabDiagram *) focusElement)->xAxis.limit_min);
                // Update matching wire label highlighting
                Doc->highlightWireLabels();
                return;
            }
            // Update matching wire label highlighting
            Doc->highlightWireLabels();
            Doc->viewport()->update();
            return;

        case isComponentText: // property text of component ?
            focusElement->Type &= (~isComponentText) | isComponent;

            MAx3 = No;
            QucsMain->slotApplyCompText();
            // Update matching wire label highlighting
            Doc->highlightWireLabels();
            return;

        case isNode:
            if (QucsSettings.NodeWiring) {
                MAx1 = 0;                // paint wire corner first up, then left/right
                MAx3 = focusElement->cx; // works even if node is not on grid
                MAy3 = focusElement->cy;
                QucsMain->MouseMoveAction = &MouseActions::MMoveWire2;
                QucsMain->MousePressAction = &MouseActions::MPressWire2;
                QucsMain->MouseReleaseAction = 0; // if function is called from elsewhere
                QucsMain->MouseDoubleClickAction = 0;

                formerAction = QucsMain->select; // to restore action afterwards
                QucsMain->activeAction = QucsMain->insWire;

                QucsMain->select->blockSignals(true);
                QucsMain->select->setChecked(false);
                QucsMain->select->blockSignals(false);

                QucsMain->insWire->blockSignals(true);
                QucsMain->insWire->setChecked(true);
                QucsMain->insWire->blockSignals(false);
                // Update matching wire label highlighting
                Doc->highlightWireLabels();
                return;
            }
        }

    QucsMain->MousePressAction = 0;
    QucsMain->MouseDoubleClickAction = 0;
    Doc->grabKeyboard(); // no keyboard inputs during move actions
    Doc->viewport()->update();

    if (focusElement == 0) {
        MAx2 = 0; // if not clicking on an element => open a rectangle
        MAy2 = 0;
        QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect2;
        QucsMain->MouseMoveAction = &MouseActions::MMoveSelect;
    } else {
        // element could be moved
        if (!Ctrl) {
            if (!focusElement->isSelected)           // Don't move selected elements if clicked
                Doc->deselectElements(focusElement); // element was not selected.
            focusElement->isSelected = true;
        }
        Doc->setOnGrid(MAx1, MAy1);
        QucsMain->MouseMoveAction = &MouseActions::MMoveMoving;
    }
    // Update matching wire label highlighting
    Doc->highlightWireLabels();
}

// -----------------------------------------------------------
void MouseActions::MPressDelete(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    Element *pe = Doc->selectElement(fX, fY, false);
    if (pe) {
        pe->isSelected = true;
        Doc->deleteElements();

        Doc->updateAllBoundingRect();
        Doc->viewport()->update();
    }
}

// -----------------------------------------------------------
void MouseActions::MPressActivate(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    MAx1 = int(fX);
    MAy1 = int(fY);
    if (!Doc->activateSpecifiedComponent(MAx1, MAy1)) {
        //    if(Event->button() != Qt::LeftButton) return;
        MAx2 = 0; // if not clicking on a component => open a rectangle
        MAy2 = 0;
        QucsMain->MousePressAction = 0;
        QucsMain->MouseReleaseAction = &MouseActions::MReleaseActivate;
        QucsMain->MouseMoveAction = &MouseActions::MMoveSelect;
    }
    Doc->viewport()->update();
}

// -----------------------------------------------------------
void MouseActions::MPressMirrorX(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    // no use in mirroring wires or diagrams
    Component *c = Doc->selectedComponent(int(fX), int(fY));
    if (c) {
        if (c->Ports.count() < 1)
            return; // only mirror components with ports
        c->mirrorX();
        Doc->setCompPorts(c);
    } else {
        Painting *p = Doc->selectedPainting(fX, fY);
        if (p == 0)
            return;
        p->mirrorX();
    }

    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
void MouseActions::MPressMirrorY(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    // no use in mirroring wires or diagrams
    Component *c = Doc->selectedComponent(int(fX), int(fY));
    if (c) {
        if (c->Ports.count() < 1)
            return; // only mirror components with ports
        c->mirrorY();
        Doc->setCompPorts(c);
    } else {
        Painting *p = Doc->selectedPainting(fX, fY);
        if (p == 0)
            return;
        p->mirrorY();
    }

    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
void MouseActions::MPressRotate(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    Element *e = Doc->selectElement(int(fX), int(fY), false);
    if (e == 0)
        return;
    e->Type &= isSpecialMask; // remove special functions

    WireLabel *pl;
    int x1, y1, x2, y2;
    //  e->isSelected = false;
    switch (e->Type) {
    case isComponent:
    case isAnalogComponent:
    case isDigitalComponent:
        if (((Component *) e)->Ports.count() < 1)
            break; // do not rotate components without ports
        ((Component *) e)->rotate();
        Doc->setCompPorts((Component *) e);
        // enlarge viewarea if component lies outside the view
        ((Component *) e)->entireBounds(x1, y1, x2, y2);
        Doc->enlargeView(x1, y1, x2, y2);
        break;

    case isWire:
        pl = ((Wire *) e)->Label;
        ((Wire *) e)->Label = 0; // prevent label to be deleted
        Doc->a_Wires->setAutoDelete(false);
        Doc->deleteWire((Wire *) e);
        ((Wire *) e)->Label = pl;
        ((Wire *) e)->rotate();
        Doc->setOnGrid(e->x1, e->y1);
        Doc->setOnGrid(e->x2, e->y2);
        if (pl)
            Doc->setOnGrid(pl->cx, pl->cy);
        Doc->insertWire((Wire *) e);
        Doc->a_Wires->setAutoDelete(true);
        if (Doc->a_Wires->containsRef((Wire *) e))
            Doc->enlargeView(e->x1, e->y1, e->x2, e->y2);
        break;

    case isPainting:
        ((Painting *) e)->rotate(0, 0);
        // enlarge viewarea if component lies outside the view
        ((Painting *) e)->Bounding(x1, y1, x2, y2);
        Doc->enlargeView(x1, y1, x2, y2);
        break;

    default:
        return;
    }
    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
// insert component, diagram, painting into schematic ?!
void MouseActions::MPressElement(Schematic *Doc, QMouseEvent *Event, float, float)
{
    if (selElem == 0)
        return;
    //QPainter painter(Doc->viewport());
    //setPainter(Doc, &painter);

    int x1, y1, x2, y2, rot;
    if (selElem->Type & isComponent) {
        Component *Comp = (Component *) selElem;
        //    qDebug() << "+-+ got to switch:" << Comp->Name;
        QString entryName = Comp->Name;

        switch (Event->button()) {
        case Qt::LeftButton:
            // left mouse button inserts component into the schematic
            // give the component a pointer to the schematic it's a
            // part of
            Comp->setSchematic(Doc);
            Comp->textSize(x1, y1);
            Doc->insertComponent(Comp);
            Comp->textSize(x2, y2);
            if (Comp->tx < Comp->x1)
                Comp->tx -= x2 - x1;

            // Note: insertCopmponents does increment  name1 -> name2
            //    qDebug() << "  +-+ got to insert:" << Comp->Name;

            // enlarge viewarea if component lies outside the view
            Comp->entireBounds(x1, y1, x2, y2);
            Doc->enlargeView(x1, y1, x2, y2);
            //Doc->setOnGrid(Comp->cx,Comp->cy);

            Doc->viewport()->update();
            Doc->setChanged(true, true);
            rot = Comp->rotated;

            // handle static and dynamic components
            //    QucsApp::CompChoose;
            if (Module::vaComponents.contains(entryName)) {
                QString filename = Module::vaComponents[entryName];
                //      qDebug() << "   ===+ recast";
                Comp = dynamic_cast<vacomponent *>(Comp)->newOne(filename); //va component
                qDebug() << "   => recast = Comp;" << Comp->Name << "filename: " << filename;
            } else {
                Comp = Comp->newOne(); // static component is used, so create a new one
            }
            rot -= Comp->rotated;
            rot &= 3;
            while (rot--)
                Comp->rotate(); // keep last rotation for single component
            break;

        case Qt::RightButton: // right mouse button rotates the component
            if (Comp->Ports.count() == 0)
                break;              // do not rotate components without ports
            Comp->paintScheme(Doc); // erase old component scheme
            Doc->viewport()->repaint();
            Comp->rotate();
            Doc->setOnGrid(Comp->cx,Comp->cy);
            Comp->paintScheme(Doc); // paint new component scheme
            break;

        default:; // avoids compiler warnings
        }
        //    qDebug() << "   => selElem = Comp;" << Comp->Name;
        // comp it getting empty
        selElem = Comp;
        return;

    } // of "if(isComponent)"
    else if (selElem->Type == isDiagram) {
        if (Event->button() != Qt::LeftButton)
            return;

        Diagram *Diag = (Diagram *) selElem;
        QFileInfo Info(Doc->getDocName());
        // dialog is Qt::WDestructiveClose !!!
        DiagramDialog *dia = new DiagramDialog(Diag, Doc);
        if (dia->exec() == QDialog::Rejected) { // don't insert if dialog canceled
            Doc->viewport()->update();
            return;
        }

        Doc->a_Diagrams->append(Diag);
        Doc->enlargeView(Diag->cx, Diag->cy - Diag->y2, Diag->cx + Diag->x2, Diag->cy);
        Doc->setChanged(true, true); // document has been changed

        Doc->viewport()->repaint();
        Diag = Diag->newOne(); // the component is used, so create a new one
        Diag->paintScheme(Doc);
        selElem = Diag;
        return;
    } // of "if(isDiagram)"

    // ***********  it is a painting !!!
    if (((Painting *) selElem)->MousePressing(Doc)) {
        Doc->a_Paintings->append((Painting *) selElem);
        ((Painting *) selElem)->Bounding(x1, y1, x2, y2);
        //Doc->enlargeView(x1, y1, x2, y2);
        selElem = ((Painting *) selElem)->newOne();

        Doc->viewport()->update();
        Doc->setChanged(true, true);

        MMoveElement(Doc, Event); // needed before next mouse pressing
    }
}

/**
 * @brief MouseActions::MPressWire1 Is called if starting point of wire is pressed
 * @param Doc
 * @param fX
 * @param fY
 */
void MouseActions::MPressWire1(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    //Doc->PostPaintEvent (_DotLine);
    //Doc->PostPaintEvent (PPENotRop);
    //if(drawn) {
#if 0 //ALYS - it draws some garbage, not deleted because of possible questions
    Doc->PostPaintEvent (_Line, 0, MAy3, MAx2, MAy3); // erase old mouse cross
    Doc->PostPaintEvent (_Line, MAx3, 0, MAx3, MAy2);
#endif
    //}
    //drawn = false;

    MAx1 = 0; // paint wire corner first up, then left/right
    MAx3 = int(fX);
    MAy3 = int(fY);
    Doc->setOnGrid(MAx3, MAy3);

    //ALYS - draw aiming cross
    paintAim(Doc, MAx3, MAy3);
    //#######################

    formerAction = 0; // keep wire action active after first wire finished
    QucsMain->MouseMoveAction = &MouseActions::MMoveWire2;
    QucsMain->MousePressAction = &MouseActions::MPressWire2;
    // Double-click action is set in "MMoveWire2" to not initiate it
    // during "Wire1" actions.
    Doc->viewport()->update();
}

/**
 * @brief MouseActions::MPressWire2 Is called if ending point of wire is pressed
 * @param Doc
 * @param Event
 * @param fX
 * @param fY
 */
void MouseActions::MPressWire2(Schematic *Doc, QMouseEvent *Event, float fX, float fY)
{
    switch (Event->button()) {
    case Qt::LeftButton: {
        auto [hasChanges, lastNode] = Doc->connectWithWire({MAx3, MAy3}, {MAx2, MAy2});

        if (lastNode == nullptr || lastNode->conn_count() > 1) {
            // if last port is connected, then...
            if (formerAction) {
                // ...restore old action
                QucsMain->select->setChecked(true);
            } else {
                // ...start a new wire
                QucsMain->MouseMoveAction = &MouseActions::MMoveWire1;
                QucsMain->MousePressAction = &MouseActions::MPressWire1;
                QucsMain->MouseDoubleClickAction = 0;
            }
        }

        if (hasChanges)
            Doc->setChanged(true, true);
        MAx3 = MAx2;
        MAy3 = MAy2;
        break;
    }
        /// \todo document right mouse button changes the wire corner
    case Qt::RightButton:
        Doc->a_wirePlanner.next();
#if 0
    //ALYS - old code preserved because isn't clear - what it was???
    //looks like deletion via painting.
    //i'll delete it after possible clarification from team
    if(MAx1 == 0) {
      Doc->PostPaintEvent (_Line, MAx3, MAy3, MAx3, MAy2); // erase old
      Doc->PostPaintEvent (_Line, MAx3, MAy2, MAx2, MAy2); // erase old
    }
    else {
      Doc->PostPaintEvent (_Line, MAx3, MAy3, MAx2, MAy3); // erase old
      Doc->PostPaintEvent (_Line, MAx2, MAy3, MAx2, MAy2); // erase old
    }
#endif

        MAx2 = int(fX);
        MAy2 = int(fY);
        Doc->setOnGrid(MAx2, MAy2);

        MAx1 ^= 1; // change the painting direction of wire corner
        Doc->showEphemeralWire({MAx3, MAy3}, {MAx2, MAy2});
        break;

    default:; // avoids compiler warnings
    }

    paintAim(Doc, MAx2, MAy2); //ALYS - added missed aiming cross
    Doc->viewport()->update();
}

// -----------------------------------------------------------
// Is called for setting a marker on a diagram's graph
void MouseActions::MPressMarker(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    MAx1 = int(fX);
    MAy1 = int(fY);
    Marker *pm = Doc->setMarker(MAx1, MAy1);

    if (pm) {
        assert(pm->diag());
        int x0 = pm->diag()->cx;
        int y0 = pm->diag()->cy;
        Doc->enlargeView(x0 + pm->x1, y0 - pm->y1 - pm->y2, x0 + pm->x1 + pm->x2, y0 - pm->y1);
    }
    Doc->viewport()->update();
}

/**
 * @brief MouseActions::MPressSetLimits Sets the start point of the diagram limits.
 * @param Doc
 * @param Event
 */
void MouseActions::MPressSetLimits(Schematic *Doc, QMouseEvent*, float fX, float fY)
{
    // fX, fY are the scaled / adjusted coordinates, equivalent to DOC_X_POS(Event->x()).
    // MAx1, MAy1 are needed to set the start of the selection box.
    MAx1 = int(fX);
    MAy1 = int(fY);

    // TODO: Diagrams is currently a Q3PtrList, but it would be better to refactor
    // this (and many other collections) to be std::vector.
    // Check to see if the mouse is within a diagram using the oddly named "getSelected".
    for (Diagram* diagram = Doc->a_Diagrams->last(); diagram != 0; diagram = Doc->a_Diagrams->prev()) {
        // BUG: Obtaining the diagram type by name is marked as a bug elsewhere (to be solved separately).
        // TODO: Currently only rectangular diagrams are supported.
        if (diagram->getSelected(fX, fY) && diagram->Name == "Rect") {
            qDebug() << "In a rectangular diagram, setting up for area selection.";

            // cx and cy are the adjusted points of the diagram's bottom left hand corner.
            mouseDownPoint = QPointF(fX - diagram->cx, diagram->cy - fY);
            pActiveDiagram = diagram;

            QucsMain->MouseMoveAction = &MouseActions::MMoveSelect;
            QucsMain->MouseReleaseAction = &MouseActions::MReleaseSetLimits;
            Doc->grabKeyboard(); // no keyboard inputs during move actions

            // No need to continue searching;
            break;
        }
    }

    Doc->viewport()->update();
}

// -----------------------------------------------------------
void MouseActions::MPressOnGrid(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    Element *pe = Doc->selectElement(fX, fY, false);
    if (pe) {
        pe->Type &= isSpecialMask; // remove special functions (4 lowest bits)

        // onGrid is toggle action -> no other element can be selected
        pe->isSelected = true;
        Doc->elementsOnGrid();

        Doc->updateAllBoundingRect();
        // Update matching wire label highlighting
        Doc->highlightWireLabels();
        Doc->viewport()->update();
    }
}

// -----------------------------------------------------------
void MouseActions::MPressMoveText(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    MAx1 = int(fX);
    MAy1 = int(fY);
    focusElement = Doc->selectCompText(MAx1, MAy1, MAx2, MAy2);

    if (focusElement) {
        MAx3 = MAx1;
        MAy3 = MAy1;
        MAx1 = ((Component *) focusElement)->cx + ((Component *) focusElement)->tx;
        MAy1 = ((Component *) focusElement)->cy + ((Component *) focusElement)->ty;
        Doc->viewport()->update();
        QucsMain->MouseMoveAction = &MouseActions::MMoveMoveText;
        QucsMain->MouseReleaseAction = &MouseActions::MReleaseMoveText;
        Doc->grabKeyboard(); // no keyboard inputs during move actions
    }
}

// -----------------------------------------------------------
void MouseActions::MPressZoomIn(Schematic *Doc, QMouseEvent *, float fX, float fY)
{
    qDebug() << "zoom into box";
    MAx1 = int(fX);
    MAy1 = int(fY);
    MAx2 = 0; // rectangle size
    MAy2 = 0;

    QucsMain->MouseMoveAction = &MouseActions::MMoveSelect;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseZoomIn;
    Doc->grabKeyboard(); // no keyboard inputs during move actions
    Doc->viewport()->update();
}

// ***********************************************************************
// **********                                                   **********
// **********    Functions for serving mouse button releasing   **********
// **********                                                   **********
// ***********************************************************************
void MouseActions::MReleaseSelect(Schematic *Doc, QMouseEvent *Event)
{
    bool ctrl;
    if (Event->modifiers().testFlag(Qt::ControlModifier))
        ctrl = true;
    else
        ctrl = false;

    if (!ctrl)
        Doc->deselectElements(focusElement);

    if (focusElement)
        if (Event->button() == Qt::LeftButton)
            if (focusElement->Type == isWire) {
                Doc->selectWireLine(focusElement, ((Wire *) focusElement)->Port1, ctrl);
                Doc->selectWireLine(focusElement, ((Wire *) focusElement)->Port2, ctrl);
            }

    Doc->releaseKeyboard(); // allow keyboard inputs again
    QucsMain->MousePressAction = &MouseActions::MPressSelect;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect;
    QucsMain->MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
    QucsMain->MouseMoveAction = 0; // no element moving
    Doc->highlightWireLabels();
    Doc->viewport()->update();
}

// -----------------------------------------------------------
// Is called after the rectangle for selection is released.
void MouseActions::MReleaseSelect2(Schematic *Doc, QMouseEvent *Event)
{
    if (Event->button() != Qt::LeftButton)
        return;

    bool IsCtrl = Event->modifiers().testFlag(Qt::ControlModifier);
    bool IsShift = Event->modifiers().testFlag(Qt::ShiftModifier);

    // selects all elements within the rectangle
    Doc->selectElements(
        QRect{MAx1, MAy1, MAx2, MAy2}.normalized(), IsCtrl, !IsShift);

    Doc->releaseKeyboard(); // allow keyboard inputs again
    QucsMain->MouseMoveAction = 0;
    QucsMain->MousePressAction = &MouseActions::MPressSelect;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect;
    QucsMain->MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
    Doc->highlightWireLabels();
    Doc->clearPostedPaintEvents();
    Doc->viewport()->update();
}

// -----------------------------------------------------------
void MouseActions::MReleaseActivate(Schematic *Doc, QMouseEvent *Event)
{
    if (Event->button() != Qt::LeftButton)
        return;

    // activates all components within the rectangle
    Doc->activateCompsWithinRect(MAx1, MAy1, MAx1 + MAx2, MAy1 + MAy2);

    QucsMain->MouseMoveAction = &MouseActions::MMoveActivate;
    QucsMain->MousePressAction = &MouseActions::MPressActivate;
    QucsMain->MouseReleaseAction = 0;
    QucsMain->MouseDoubleClickAction = 0;
    Doc->highlightWireLabels();
    Doc->viewport()->update();
}

// -----------------------------------------------------------
// Is called after moving selected elements.
void MouseActions::MReleaseMoving(Schematic *Doc, QMouseEvent *)
{
    // Allow all mouse buttons, because for others than the left one,
    // a menu has already created.
    endElementMoving(Doc, &movingElements);
    Doc->releaseKeyboard(); // allow keyboard inputs again

    QucsMain->MouseMoveAction = nullptr;
    QucsMain->MousePressAction = &MouseActions::MPressSelect;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect;
    QucsMain->MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
    QucsMain->editRotate->setChecked(false);
    QucsMain->editRotate->blockSignals(false);
    QucsMain->insLabel->blockSignals(false);
    QucsMain->setMarker->blockSignals(false);
}

// -----------------------------------------------------------
void MouseActions::MReleaseResizeDiagram(Schematic *Doc, QMouseEvent *Event)
{
    if (Event->button() != Qt::LeftButton)
        return;

    MAx3 = focusElement->cx;
    MAy3 = focusElement->cy;
    if (MAx2 < 0) { // resize diagram
        if (MAx2 > -10)
            MAx2 = -10; // not smaller than 10 pixels
        focusElement->x2 = -MAx2;
        focusElement->cx = MAx1 + MAx2;
    } else {
        if (MAx2 < 10)
            MAx2 = 10;
        focusElement->x2 = MAx2;
        focusElement->cx = MAx1;
    }
    if (MAy2 < 0) {
        if (MAy2 > -10)
            MAy2 = -10;
        focusElement->y2 = -MAy2;
        focusElement->cy = MAy1;
    } else {
        if (MAy2 < 10)
            MAy2 = 10;
        focusElement->y2 = MAy2;
        focusElement->cy = MAy1 + MAy2;
    }
    MAx3 -= focusElement->cx;
    MAy3 -= focusElement->cy;

    Diagram *pd = (Diagram *) focusElement;
    pd->updateGraphData();
    for (Graph *pg : pd->Graphs)
        for (Marker *pm : pg->Markers) {
            pm->x1 += MAx3; // correct changes due to move of diagram corner
            pm->y1 += MAy3;
        }

    int x1, x2, y1, y2;
    pd->Bounding(x1, x2, y1, y2);
    Doc->enlargeView(x1, x2, y1, y2);

    QucsMain->MouseMoveAction = nullptr;
    QucsMain->MousePressAction = &MouseActions::MPressSelect;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect;
    QucsMain->MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
    Doc->releaseKeyboard(); // allow keyboard inputs again

    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
void MouseActions::MReleaseResizePainting(Schematic *Doc, QMouseEvent *Event)
{
    if (Event->button() != Qt::LeftButton)
        return;

    QucsMain->MouseMoveAction = nullptr;
    QucsMain->MousePressAction = &MouseActions::MPressSelect;
    QucsMain->MouseReleaseAction = &MouseActions::MReleaseSelect;
    QucsMain->MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
    Doc->releaseKeyboard(); // allow keyboard inputs again

    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
void MouseActions::MReleaseSetLimits(Schematic *Doc, QMouseEvent *Event)
{
    Doc->releaseKeyboard();

    // TODO: Make a point version of DOC_n_POS.
    auto inModel = Doc->contentsToModel(Event->pos());
    MAx2 = inModel.x();
    MAy2 = inModel.y();

    qDebug() << "Mouse released after setting limits.";
    // Check to see if the mouse is within a diagram using the oddly named "getSelected".
    for (Diagram* diagram = Doc->a_Diagrams->last(); diagram != 0; diagram = Doc->a_Diagrams->prev()) {

        // Only process the selection if it ends in a diagram, and is the same diagram as start.
        if (diagram->getSelected(MAx2, MAy2) && diagram == pActiveDiagram) {
            qDebug() << "In the same diagram, setting limits";

            mouseUpPoint = QPointF(MAx2 - diagram->cx, diagram->cy - MAy2);

            // Normalise the selection in case user starts at bottom and/or right.
            QRectF select = QRectF(mouseDownPoint, mouseUpPoint).normalized();

            // Only process if there is a valid selection box.
            if (select.width() < MIN_SELECT_SIZE || select.height() < MIN_SELECT_SIZE)
                break;

            diagram->setLimitsBySelectionRect(select);

            diagram->updateGraphData();

            Doc->setChanged(true, true);

            // No need to keep searching.
            break;
        }
    }

    // Stay in set limits and allow user to choose a new start point.
    QucsMain->MouseMoveAction = &MouseActions::MMoveSetLimits;
    QucsMain->MouseReleaseAction = nullptr;
    Doc->viewport()->update();
}

// -----------------------------------------------------------
void MouseActions::paintElementsScheme(Schematic *p)
{
    for (auto* pe : movingElements)
        pe->paintScheme(p);
}

// -----------------------------------------------------------
void MouseActions::moveElements(Schematic *Doc, int &x1, int &y1)
{
    Doc->setOnGrid(x1, y1);

    for (auto* pe : movingElements) {
        if (pe->Type & isLabel) {
            pe->cx += x1;
            pe->x1 += x1;
            pe->cy += y1;
            pe->y1 += y1;
        } else
            pe->setCenter(x1, y1, true);
    }
}

// -----------------------------------------------------------
void MouseActions::rotateElements(Schematic *Doc, int &x1, int &y1)
{
    int x2;
    Doc->setOnGrid(x1, y1);

    for (auto* pe : movingElements) {
        switch (pe->Type) {
        case isComponent:
        case isAnalogComponent:
        case isDigitalComponent:
            ((Component *) pe)->rotate(); // rotate !before! rotating the center
            x2 = x1 - pe->cx;
            pe->setCenter(pe->cy - y1 + x1, x2 + y1);
            break;
        case isWire:
            x2 = pe->x1;
            pe->x1 = pe->y1 - y1 + x1;
            pe->y1 = x1 - x2 + y1;
            x2 = pe->x2;
            pe->x2 = pe->y2 - y1 + x1;
            pe->y2 = x1 - x2 + y1;
            break;
        case isPainting:
            ((Painting *) pe)->rotate(x1, y1); // rotate around the center x1 y1
            break;
        default:
            x2 = x1 - pe->cx; // if diagram -> only rotate cx/cy
            pe->setCenter(pe->cy - y1 + x1, x2 + y1);
            break;
        }
    }
}

// -----------------------------------------------------------
void MouseActions::MReleasePaste(Schematic *Doc, QMouseEvent *Event)
{
    int x1, y1, x2, y2, rot;
    QFileInfo Info(Doc->getDocName());
    //QPainter painter(Doc->viewport());

    switch (Event->button()) {
    case Qt::LeftButton:
        // insert all moved elements into document
        for (auto* pe : movingElements) {
            pe->isSelected = false;
            switch (pe->Type) {
            case isWire:
                if (pe->x1 == pe->x2)
                    if (pe->y1 == pe->y2)
                        break;
                Doc->insertWire((Wire *) pe);
                if (Doc->a_Wires->containsRef((Wire *) pe))
                    Doc->enlargeView(pe->x1, pe->y1, pe->x2, pe->y2);
                else
                    pe = NULL;
                break;
            case isDiagram:
                Doc->a_Diagrams->append((Diagram *) pe);
                ((Diagram *) pe)
                    ->loadGraphData(Info.absolutePath() + QDir::separator() + Doc->getDataSet());
                Doc->enlargeView(pe->cx, pe->cy - pe->y2, pe->cx + pe->x2, pe->cy);
                break;
            case isPainting:
                Doc->a_Paintings->append((Painting *) pe);
                ((Painting *) pe)->Bounding(x1, y1, x2, y2);
                Doc->enlargeView(x1, y1, x2, y2);
                break;
            case isMovingLabel:
                pe->Type = isNodeLabel;
                Doc->placeNodeLabel((WireLabel *) pe);
                break;
            case isComponent:
            case isAnalogComponent:
            case isDigitalComponent:
                Doc->insertComponent((Component *) pe);
                ((Component *) pe)->entireBounds(x1, y1, x2, y2);
                Doc->enlargeView(x1, y1, x2, y2);
                break;
            }
        }

        pasteElements(Doc);
        // keep rotation sticky for pasted elements
        rot = movingRotated;
        x1 = y1 = 0;
        while (rot--)
            rotateElements(Doc, x1, y1);

        QucsMain->MouseMoveAction = &MouseActions::MMovePaste;
        QucsMain->MousePressAction = 0;
        QucsMain->MouseReleaseAction = 0;
        QucsMain->MouseDoubleClickAction = 0;

        Doc->viewport()->update();
        Doc->setChanged(true, true);
        break;

    // ............................................................
    case Qt::RightButton: {// right button rotates the elements
        //setPainter(Doc, &painter);

        auto inModel = Doc->contentsToModel(Event->pos());
        x1 = inModel.x();
        y1 = inModel.y();
        rotateElements(Doc, x1, y1);
        paintElementsScheme(Doc);
        // save rotation
        movingRotated++;
        movingRotated &= 3;
        break;
    }

    default:; // avoids compiler warnings
    }
}

// -----------------------------------------------------------
void MouseActions::MReleaseMoveText(Schematic *Doc, QMouseEvent *Event)
{
    if (Event->button() != Qt::LeftButton)
        return;

    QucsMain->MouseMoveAction = &MouseActions::MMoveMoveTextB;
    QucsMain->MouseReleaseAction = 0;
    Doc->releaseKeyboard(); // allow keyboard inputs again

    ((Component *) focusElement)->tx = MAx1 - ((Component *) focusElement)->cx;
    ((Component *) focusElement)->ty = MAy1 - ((Component *) focusElement)->cy;
    Doc->viewport()->update();
    Doc->setChanged(true, true);
}

// -----------------------------------------------------------
void MouseActions::MReleaseZoomIn(Schematic *Doc, QMouseEvent *Event)
{
    if (Event->button() != Qt::LeftButton)
        return;

    MAx1 = Event->pos().x();
    MAy1 = Event->pos().y();


    const QPoint click{Event->pos().x() , Event->pos().y()};
    // "false" = "coordinates are not relative to viewport"
    Doc->zoomAroundPoint(1.5, click, false);

    QucsMain->MouseMoveAction = &MouseActions::MMoveZoomIn;
    QucsMain->MouseReleaseAction = 0;
    Doc->releaseKeyboard(); // allow keyboard inputs again
}

// ***********************************************************************
// **********                                                   **********
// **********    Functions for mouse button double clicking     **********
// **********                                                   **********
// ***********************************************************************
void MouseActions::editElement(Schematic *Doc, QMouseEvent *Event)
{
    //    qDebug() << "+double click, editElement";

    if (focusElement == 0)
        return;

    //  qDebug() << "+focusElement->Type" << focusElement->Type;

    Graph *pg;
    Component *c;
    Diagram *dia;
    DiagramDialog *ddia;
    MarkerDialog *mdia;
    int x1, y1, x2, y2;

    QFileInfo Info(Doc->getDocName());
    auto inModel = Doc->contentsToModel(Event->pos());
    float fX = static_cast<float>(inModel.x());
    float fY = static_cast<float>(inModel.y());

    switch (focusElement->Type) {
    case isComponent:
    case isAnalogComponent:
    case isDigitalComponent:
        c = (Component *) focusElement;
        //         qDebug() << "cast focusElement into" << c->Name;
        if (c->Model == "GND")
            return;

        if (c->Model == "SpLib") {
          SpiceLibCompDialog *sld = new SpiceLibCompDialog(c, Doc);
          if (sld->exec() != -1) {
            break;
          }
        } else if ((c->Model == ".CUSTOMSIM") || (c->Model == ".XYCESCR") || (c->Model == "INCLSCR")) {
            CustomSimDialog *sd = new CustomSimDialog((SpiceCustomSim *) c, Doc);
            if (sd->exec() != 1)
                break; // dialog is WDestructiveClose
        } else if (c->Model == "SPICE") {
            SpiceDialog *sd = new SpiceDialog(App, (SpiceFile *) c, Doc);
            if (sd->exec() != 1)
                break; // dialog is WDestructiveClose
        } else if (c->Model == ".Opt") {
            OptimizeDialog *od = new OptimizeDialog((Optimize_Sim *) c, Doc);
            if (od->exec() != 1)
                break; // dialog is WDestructiveClose
        } else if (c->Model == "SPICEINIT") {
            TextBoxDialog *od = new TextBoxDialog("Edit .spiceinit configuration", c, Doc);
            if (od->exec() != 1)
                break; // dialog is WDestructiveClose
        } else {
            ComponentDialog *cd = new ComponentDialog(c, Doc);
            if (cd->exec() != 1)
                break; // dialog is WDestructiveClose

            Doc->a_Components->findRef(c);
            Doc->a_Components->take();
            Doc->setComponentNumber(c); // for ports/power sources
            Doc->a_Components->append(c);
        }

        Doc->setChanged(true, true);
        c->entireBounds(x1, y1, x2, y2);
        Doc->enlargeView(x1, y1, x2, y2);
        break;

    case isDiagram:
        dia = (Diagram *) focusElement;
        if (dia->Name.at(0) == 'T' // check only on double click
            && Event->type() == QMouseEvent::MouseButtonDblClick) { // don't open dialog on scrollbar
            if (dia->Name == "Time") {
                if (dia->cy < int(fY)) {
                    if (((TimingDiagram *) focusElement)->scroll(MAx1))
                        Doc->setChanged(true, true, 'm'); // 'm' = only the first time
                    break;
                }
            } else {
                if (dia->cx > int(fX)) {
                    if (((TabDiagram *) focusElement)->scroll(MAy1))
                        Doc->setChanged(true, true, 'm'); // 'm' = only the first time
                    break;
                }
            }
        }

        ddia = new DiagramDialog(dia, Doc);
        if (ddia->exec() != QDialog::Rejected) // is WDestructiveClose
            Doc->setChanged(true, true);

        dia->Bounding(x1, x2, y1, y2);
        Doc->enlargeView(x1, x2, y1, y2);
        break;

    case isGraph:
        pg = (Graph *) focusElement;
        // searching diagram for this graph
        for (dia = Doc->a_Diagrams->last(); dia != 0; dia = Doc->a_Diagrams->prev())
            if (dia->Graphs.indexOf(pg) >= 0)
                break;
        if (!dia)
            break;

        ddia = new DiagramDialog(dia, Doc, pg);
        if (ddia->exec() != QDialog::Rejected) // is WDestructiveClose
            Doc->setChanged(true, true);
        break;

    case isWire:
        MPressLabel(Doc, Event, fX, fY);
        break;

    case isNodeLabel:
    case isHWireLabel:
    case isVWireLabel:
        editLabel(Doc, (WireLabel *) focusElement);
        // update highlighting, labels may have changed
        Doc->highlightWireLabels();
        break;

    case isPainting:
        if (((Painting *) focusElement)->Dialog(Doc))
            Doc->setChanged(true, true);
        break;

    case isMarker:
        mdia = new MarkerDialog((Marker *) focusElement, Doc);
        if (mdia->exec() > 1)
            Doc->setChanged(true, true);
        break;
    }

    // Very strange: Now an open VHDL editor gets all the keyboard input !?!
    // I don't know why it only happens here, nor am I sure whether it only
    // happens here. Anyway, I hope the best and give the focus back to the
    // current document.
    Doc->setFocus();

    Doc->viewport()->update();
}

// -----------------------------------------------------------
void MouseActions::MDoubleClickSelect(Schematic *Doc, QMouseEvent *Event)
{
    Doc->releaseKeyboard(); // allow keyboard inputs again
    QucsMain->editText->setHidden(true);
    editElement(Doc, Event);
}

/**
 * @brief MouseActions::MDoubleClickWire2  Double click terminates wire insertion.
 * @param Doc
 * @param Event
 */
void MouseActions::MDoubleClickWire2(Schematic *Doc, QMouseEvent *Event)
{
    auto inModel = Doc->contentsToModel(Event->pos());
    MPressWire2(Doc, Event, static_cast<float>(inModel.x()), static_cast<float>(inModel.y()));

    if (formerAction)
        QucsMain->select->setChecked(true); // restore old action
    else {
        QucsMain->MouseMoveAction = &MouseActions::MMoveWire1;
        QucsMain->MousePressAction = &MouseActions::MPressWire1;
        QucsMain->MouseDoubleClickAction = 0;
    }
}

void MouseActions::MPressTune(Schematic *Doc, QMouseEvent *Event, float fX, float fY)
{
    Q_UNUSED(Event);
    int No = 0;
    MAx1 = int(fX);
    MAy1 = int(fY);
    focusElement = Doc->selectElement(fX, fY, false, &No);
    isMoveEqual = false; // moving not neccessarily square

    if (focusElement)
        // print define value in hex, see element.h
        qDebug() << "MPressTune: focusElement->Type"
                 << QStringLiteral("0x%1").arg(focusElement->Type, 0, 16);
    else
        qDebug() << "MPressTune";

    if (focusElement && App->TuningMode) {
        Component *pc = nullptr;
        Property *pp = nullptr;
        switch (focusElement->Type) {
        case isComponent: // pick first property if device is clicked (i.e. R,C)
        case isAnalogComponent:
            pc = (Component *) focusElement;
            if (!pc)
                return;
            if (pc->Props.isEmpty())
                return;
            pp = pc->Props.at(0);
            break;
        case isComponentText: // property text of component ?
            focusElement->Type &= (~isComponentText) | isComponent;
            pc = (Component *) focusElement;
            pp = 0;
            if (!pc)
                return; // should never happen

            // current property
            if (No > 0) {
                No--; // No counts also the on/screen component Name, so subtract 1 to get the actual property number
                pp = pc->Props.at(No);
            }
            if (!pp)
                return; // should not happen
            break;
        default:
            return;
        }
        if (pc == nullptr || pp == nullptr)
            return;
        if (!App->tunerDia->containsProperty(pp)) {
            if (isPropertyTunable(pc, pp)) {
                tunerElement *tune = new tunerElement(App->tunerDia, pc, pp, No);
                tune->schematicName = Doc->getDocName();
                if (tune != NULL)
                    App->tunerDia->addTunerElement(tune); //Tunable property
            } else {
                QMessageBox::warning(nullptr,
                                     "Property not correct",
                                     "You selected a non-tunable property",
                                     QMessageBox::Ok);
                return;
            }
        }
    }
}

// vim:ts=8:sw=2:noet