1 
2 
3 #include "toonzqt/schematicnode.h"
4 #include "toonzqt/stageschematicscene.h"
5 #include "toonzqt/fxschematicscene.h"
6 
7 #include <QGraphicsSceneMouseEvent>
8 #include <QStyleOptionGraphicsItem>
9 #include <QKeyEvent>
10 #include <algorithm>
11 #include <QApplication>
12 #include <QTextDocument>
13 #include <QTextCursor>
14 #include <QTextBlock>
15 #include <QMenuBar>
16 #include <QPolygonF>
17 #include <QDesktopWidget>
18 #include "tundo.h"
19 #include "toonzqt/menubarcommand.h"
20 #include "toonzqt/gutil.h"
21 
22 //========================================================
23 //
24 // StageSchematicName
25 //
26 //========================================================
27 
SchematicName(QGraphicsItem * parent,double width,double height)28 SchematicName::SchematicName(QGraphicsItem *parent, double width, double height)
29     : QGraphicsTextItem("", parent), m_width(width), m_height(height) {
30   setFlag(QGraphicsItem::ItemIsSelectable, true);
31   setFlag(QGraphicsItem::ItemIsFocusable, true);
32   setTextInteractionFlags(Qt::TextEditorInteraction);
33 
34   connect(document(), SIGNAL(contentsChanged()), this,
35           SLOT(onContentsChanged()));
36 }
37 
38 //--------------------------------------------------------
39 
~SchematicName()40 SchematicName::~SchematicName() {}
41 
42 //--------------------------------------------------------
43 
setName(const QString & name)44 void SchematicName::setName(const QString &name) { setPlainText(name); }
45 
46 //--------------------------------------------------------
47 
onContentsChanged()48 void SchematicName::onContentsChanged() {
49   QString text       = document()->toPlainText();
50   QTextCursor cursor = textCursor();
51   int position       = cursor.position();
52   if (position > 0 && text.at(position - 1) == '\n') {
53     text.remove("\n");
54     setPlainText(text);
55     ;
56     emit focusOut();
57   }
58 }
59 
60 //--------------------------------------------------------
61 
focusOutEvent(QFocusEvent * fe)62 void SchematicName::focusOutEvent(QFocusEvent *fe) {
63   qApp->removeEventFilter(this);
64   if (fe->reason() == Qt::MouseFocusReason) emit focusOut();
65 }
66 
67 //--------------------------------------------------------
68 
keyPressEvent(QKeyEvent * ke)69 void SchematicName::keyPressEvent(QKeyEvent *ke) {
70   if (ke->key() == Qt::Key_Left || ke->key() == Qt::Key_Right) {
71     QTextCursor cursor = textCursor();
72     int currentPos     = cursor.position();
73     if (ke->key() == Qt::Key_Left)
74       cursor.setPosition(currentPos - 1);
75     else
76       cursor.setPosition(currentPos + 1);
77     setTextCursor(cursor);
78   } else
79     QGraphicsTextItem::keyPressEvent(ke);
80 }
81 
82 //--------------------------------------------------------
83 
eventFilter(QObject * object,QEvent * event)84 bool SchematicName::eventFilter(QObject *object, QEvent *event) {
85   if (event->type() == QEvent::Shortcut ||
86       event->type() == QEvent::ShortcutOverride) {
87     if (!object->inherits("QGraphicsView")) {
88       event->accept();
89       return true;
90     }
91   }
92   return false;
93 }
94 
95 //--------------------------------------------------------
96 
focusInEvent(QFocusEvent * fe)97 void SchematicName::focusInEvent(QFocusEvent *fe) {
98   QGraphicsTextItem::focusInEvent(fe);
99   qApp->installEventFilter(this);
100   QTextDocument *doc = document();
101   QTextCursor cursor(doc->begin());
102   cursor.select(QTextCursor::Document);
103   setTextCursor(cursor);
104 }
105 
106 //========================================================
107 //
108 // class SchematicThumbnailToggle
109 //
110 //========================================================
111 
SchematicThumbnailToggle(SchematicNode * parent,bool isOpened)112 SchematicThumbnailToggle::SchematicThumbnailToggle(SchematicNode *parent,
113                                                    bool isOpened)
114     : QGraphicsItem(parent), m_isDown(!isOpened) {}
115 
116 //--------------------------------------------------------
117 
~SchematicThumbnailToggle()118 SchematicThumbnailToggle::~SchematicThumbnailToggle() {}
119 
120 //--------------------------------------------------------
121 
boundingRect() const122 QRectF SchematicThumbnailToggle::boundingRect() const {
123   return QRectF(0, 0, 14, 14);
124 }
125 
126 //--------------------------------------------------------
127 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)128 void SchematicThumbnailToggle::paint(QPainter *painter,
129                                      const QStyleOptionGraphicsItem *option,
130                                      QWidget *widget) {
131   QRect rect(3, 3, 8, 8);
132   QRect sourceRect = scene()->views()[0]->matrix().mapRect(rect);
133   static QIcon onIcon(":Resources/schematic_thumbtoggle_on.svg");
134   static QIcon offIcon(":Resources/schematic_thumbtoggle_off.svg");
135   QPixmap pixmap;
136   if (m_isDown)
137     pixmap = offIcon.pixmap(sourceRect.size());
138   else
139     pixmap   = onIcon.pixmap(sourceRect.size());
140   sourceRect = QRect(0, 0, sourceRect.width() * getDevPixRatio(),
141                      sourceRect.height() * getDevPixRatio());
142   painter->drawPixmap(rect, pixmap, sourceRect);
143 }
144 
145 //--------------------------------------------------------
146 
setIsDown(bool value)147 void SchematicThumbnailToggle::setIsDown(bool value) {
148   m_isDown = value;
149   emit(toggled(!m_isDown));
150 }
151 
152 //--------------------------------------------------------
153 
mousePressEvent(QGraphicsSceneMouseEvent * me)154 void SchematicThumbnailToggle::mousePressEvent(QGraphicsSceneMouseEvent *me) {
155   m_isDown = !m_isDown;
156   emit(toggled(!m_isDown));
157 }
158 
159 //========================================================
160 //
161 // SchematicToggle
162 //
163 //========================================================
164 
SchematicToggle(SchematicNode * parent,const QIcon & imageOn,QColor colorOn,int flags,bool isNormalIconView)165 SchematicToggle::SchematicToggle(SchematicNode *parent, const QIcon &imageOn,
166                                  QColor colorOn, int flags,
167                                  bool isNormalIconView)
168     : QGraphicsItem(parent)
169     , m_imageOn(imageOn)
170     , m_imageOn2()
171     , m_imageOff()
172     , m_state(0)
173     , m_flags(flags)
174     , m_width(isNormalIconView ? 18 : 30)
175     , m_height(isNormalIconView ? 7 : 5)
176     , m_colorOn(colorOn)
177     , m_colorOff(QColor(0, 0, 0, 0)) {}
178 
179 //--------------------------------------------------------
180 
SchematicToggle(SchematicNode * parent,const QIcon & imageOn,QColor colorOn,const QIcon & imageOff,QColor colorOff,int flags,bool isNormalIconView)181 SchematicToggle::SchematicToggle(SchematicNode *parent, const QIcon &imageOn,
182                                  QColor colorOn, const QIcon &imageOff,
183                                  QColor colorOff, int flags,
184                                  bool isNormalIconView)
185     : QGraphicsItem(parent)
186     , m_imageOn(imageOn)
187     , m_imageOn2()
188     , m_imageOff(imageOff)
189     , m_state(0)
190     , m_flags(flags)
191     , m_width(isNormalIconView ? 18 : 30)
192     , m_height(isNormalIconView ? 7 : 5)
193     , m_colorOn(colorOn)
194     , m_colorOff(colorOff) {}
195 
196 //--------------------------------------------------------
197 
SchematicToggle(SchematicNode * parent,const QIcon & imageOn,const QIcon & imageOn2,QColor colorOn,int flags,bool isNormalIconView)198 SchematicToggle::SchematicToggle(SchematicNode *parent, const QIcon &imageOn,
199                                  const QIcon &imageOn2, QColor colorOn,
200                                  int flags, bool isNormalIconView)
201     : QGraphicsItem(parent)
202     , m_imageOn(imageOn)
203     , m_imageOn2(imageOn2)
204     , m_imageOff()
205     , m_state(0)
206     , m_flags(flags)
207     , m_width(isNormalIconView ? 18 : 30)
208     , m_height(isNormalIconView ? 7 : 5)
209     , m_colorOn(colorOn)
210     , m_colorOff(QColor(0, 0, 0, 0)) {}
211 
212 //--------------------------------------------------------
213 
SchematicToggle(SchematicNode * parent,const QIcon & imageOn,const QIcon & imageOn2,QColor colorOn,const QIcon & imageOff,QColor colorOff,int flags,bool isNormalIconView)214 SchematicToggle::SchematicToggle(SchematicNode *parent, const QIcon &imageOn,
215                                  const QIcon &imageOn2, QColor colorOn,
216                                  const QIcon &imageOff, QColor colorOff,
217                                  int flags, bool isNormalIconView)
218     : QGraphicsItem(parent)
219     , m_imageOn(imageOn)
220     , m_imageOn2(imageOn2)
221     , m_imageOff(imageOff)
222     , m_state(0)
223     , m_flags(flags)
224     , m_width(isNormalIconView ? 18 : 30)
225     , m_height(isNormalIconView ? 7 : 5)
226     , m_colorOn(colorOn)
227     , m_colorOff(colorOff) {}
228 
229 //--------------------------------------------------------
230 
~SchematicToggle()231 SchematicToggle::~SchematicToggle() {}
232 
233 //--------------------------------------------------------
234 
boundingRect() const235 QRectF SchematicToggle::boundingRect() const {
236   return QRectF(0, 0, m_width, m_height);
237 }
238 
239 //--------------------------------------------------------
240 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)241 void SchematicToggle::paint(QPainter *painter,
242                             const QStyleOptionGraphicsItem *option,
243                             QWidget *widget) {
244   int rectHeight = boundingRect().height();
245   int rectWidth  = boundingRect().width();
246   int rectX      = boundingRect().left();
247   int rectY      = boundingRect().top();
248 
249   QRect rect =
250       QRect(0, 0, rectHeight, rectHeight)
251           .translated(rectX + (rectWidth / 2) - (rectHeight / 2), rectY);
252 
253   if (m_state != 0) {
254     QIcon &pix =
255         (m_state == 2 && !m_imageOn2.isNull()) ? m_imageOn2 : m_imageOn;
256     painter->fillRect(boundingRect().toRect(), m_colorOn);
257     QRect sourceRect =
258         scene()->views()[0]->matrix().mapRect(QRect(0, 0, 18, 17));
259     QPixmap redPm = pix.pixmap(sourceRect.size());
260     QRect newRect = QRect(0, 0, sourceRect.width() * getDevPixRatio(),
261                           sourceRect.height() * getDevPixRatio());
262     painter->drawPixmap(rect, redPm, newRect);
263   } else if (!m_imageOff.isNull()) {
264     QPen pen(m_colorOn);
265     pen.setWidthF(0.5);
266     painter->setPen(pen);
267     painter->setBrush(m_colorOff);
268     double d = pen.widthF() / 2.0;
269     painter->drawRect(boundingRect().adjusted(d, d, -d, -d));
270     QRect sourceRect =
271         scene()->views()[0]->matrix().mapRect(QRect(0, 0, 18, 17));
272     QPixmap redPm = m_imageOff.pixmap(sourceRect.size());
273     QRect newRect = QRect(0, 0, sourceRect.width() * getDevPixRatio(),
274                           sourceRect.height() * getDevPixRatio());
275     painter->drawPixmap(rect, redPm, newRect);
276   }
277 }
278 
279 //--------------------------------------------------------
280 
mousePressEvent(QGraphicsSceneMouseEvent * me)281 void SchematicToggle::mousePressEvent(QGraphicsSceneMouseEvent *me) {
282   if (me->button() == Qt::LeftButton) {
283     if (m_imageOn2.isNull()) {
284       m_state = 1 - m_state;
285       emit(toggled(m_state != 0));
286     } else if (m_flags & eEnableNullState) {
287       m_state = (m_state + 1) % 3;
288       emit(stateChanged(m_state));
289     } else {
290       m_state = 3 - m_state;
291       emit(stateChanged(m_state));
292     }
293   }
294   if (me->button() == Qt::RightButton) {
295     SchematicNode *parent = dynamic_cast<SchematicNode *>(this->parentItem());
296     if (parent) parent->onClicked();
297   }
298 }
299 //--------------------------------------------------------
300 
contextMenuEvent(QGraphicsSceneContextMenuEvent * cme)301 void SchematicToggle::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) {
302   if (!(m_flags & eIsParentColumn)) return;
303   if (m_imageOn2.isNull()) {
304     QMenu *menu                = new QMenu(0);
305     CommandManager *cmdManager = CommandManager::instance();
306     menu->addAction(cmdManager->getAction("MI_EnableThisColumnOnly"));
307     menu->addAction(cmdManager->getAction("MI_EnableSelectedColumns"));
308     menu->addAction(cmdManager->getAction("MI_EnableAllColumns"));
309     menu->addAction(cmdManager->getAction("MI_DisableAllColumns"));
310     menu->addAction(cmdManager->getAction("MI_DisableSelectedColumns"));
311     menu->addAction(cmdManager->getAction("MI_SwapEnabledColumns"));
312     QAction *action = menu->exec(cme->screenPos());
313   } else {
314     QMenu *menu                = new QMenu(0);
315     CommandManager *cmdManager = CommandManager::instance();
316     menu->addAction(cmdManager->getAction("MI_ActivateThisColumnOnly"));
317     menu->addAction(cmdManager->getAction("MI_ActivateSelectedColumns"));
318     menu->addAction(cmdManager->getAction("MI_ActivateAllColumns"));
319     menu->addAction(cmdManager->getAction("MI_DeactivateAllColumns"));
320     menu->addAction(cmdManager->getAction("MI_DeactivateSelectedColumns"));
321     menu->addAction(cmdManager->getAction("MI_ToggleColumnsActivation"));
322     QAction *action = menu->exec(cme->screenPos());
323   }
324 }
325 //========================================================
326 
327 //--------------------------------------------------------
328 /*! for Spline Aim and CP toggles
329 */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)330 void SchematicToggle_SplineOptions::paint(
331     QPainter *painter, const QStyleOptionGraphicsItem *option,
332     QWidget *widget) {
333   QRectF rect = boundingRect();
334   painter->fillRect(rect, Qt::white);
335   if (m_state != 0) {
336     QIcon &pix =
337         (m_state == 2 && !m_imageOn2.isNull()) ? m_imageOn2 : m_imageOn;
338     QRect sourceRect = scene()->views()[0]->matrix().mapRect(rect.toRect());
339     QPixmap redPm    = pix.pixmap(sourceRect.size());
340     QRect newRect    = QRect(0, 0, sourceRect.width() * getDevPixRatio(),
341                           sourceRect.height() * getDevPixRatio());
342     painter->drawPixmap(rect, redPm, newRect);
343   }
344   painter->setBrush(Qt::NoBrush);
345   painter->setPen(QColor(180, 180, 180, 255));
346   painter->drawRect(rect);
347 }
348 
349 //--------------------------------------------------------
350 /*! for Spline Aim and CP toggles
351 */
mousePressEvent(QGraphicsSceneMouseEvent * me)352 void SchematicToggle_SplineOptions::mousePressEvent(
353     QGraphicsSceneMouseEvent *me) {
354   SchematicToggle::mousePressEvent(me);
355   update();
356 }
357 
358 //========================================================
359 //
360 // SchematicHandleSpinBox
361 //
362 //========================================================
363 
SchematicHandleSpinBox(QGraphicsItem * parent)364 SchematicHandleSpinBox::SchematicHandleSpinBox(QGraphicsItem *parent)
365     : QGraphicsItem(parent), m_buttonState(Qt::NoButton), m_delta(0) {
366   setFlag(QGraphicsItem::ItemIsSelectable, false);
367   setFlag(QGraphicsItem::ItemIsFocusable, false);
368   m_pixmap = QPixmap(":Resources/schematic_spin_arrows.svg");
369 }
370 
371 //--------------------------------------------------------
372 
~SchematicHandleSpinBox()373 SchematicHandleSpinBox::~SchematicHandleSpinBox() {}
374 
375 //--------------------------------------------------------
376 
boundingRect() const377 QRectF SchematicHandleSpinBox::boundingRect() const {
378   return QRectF(0, 0, 10, 10);
379 }
380 
381 //--------------------------------------------------------
382 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)383 void SchematicHandleSpinBox::paint(QPainter *painter,
384                                    const QStyleOptionGraphicsItem *option,
385                                    QWidget *widget) {
386   QRectF rect = boundingRect();
387   painter->drawPixmap(rect.toRect(), m_pixmap);
388   painter->setBrush(QColor(0, 0, 0, 0));
389   painter->setPen(QColor(128, 128, 128, 255));
390   painter->drawRect(rect);
391 }
392 
393 //--------------------------------------------------------
394 
mouseMoveEvent(QGraphicsSceneMouseEvent * me)395 void SchematicHandleSpinBox::mouseMoveEvent(QGraphicsSceneMouseEvent *me) {
396   if (m_buttonState == Qt::LeftButton) {
397     bool increase           = false;
398     int delta               = me->screenPos().y() - me->lastScreenPos().y();
399     if (delta < 0) increase = true;
400     m_delta += abs(delta);
401     if (m_delta > 5) {
402       if (increase)
403         emit(modifyHandle(1));
404       else
405         emit(modifyHandle(-1));
406       m_delta = 0;
407       emit sceneChanged();
408     }
409   }
410 }
411 
412 //--------------------------------------------------------
413 
mousePressEvent(QGraphicsSceneMouseEvent * me)414 void SchematicHandleSpinBox::mousePressEvent(QGraphicsSceneMouseEvent *me) {
415   m_buttonState = me->button();
416   TUndoManager::manager()->beginBlock();
417 }
418 
419 //--------------------------------------------------------
420 
mouseReleaseEvent(QGraphicsSceneMouseEvent * me)421 void SchematicHandleSpinBox::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) {
422   m_buttonState = Qt::NoButton;
423   m_delta       = 0;
424   TUndoManager::manager()->endBlock();
425   emit handleReleased();
426 }
427 
428 //========================================================
429 //
430 // class SchematicLink
431 //
432 //========================================================
433 
SchematicLink(QGraphicsItem * parent,QGraphicsScene * scene)434 SchematicLink::SchematicLink(QGraphicsItem *parent, QGraphicsScene *scene)
435 #if QT_VERSION >= 0x050000
436     : QGraphicsItem(parent)
437 #else
438     : QGraphicsItem(parent, scene)
439 #endif
440     , m_startPort(0)
441     , m_endPort(0)
442     , m_path()
443     , m_hitPath()
444     , m_lineShaped(false)
445     , m_highlighted(false) {
446 #if QT_VERSION >= 0x050000
447   scene->addItem(this);
448 #endif
449   setFlag(QGraphicsItem::ItemIsMovable, false);
450   setFlag(QGraphicsItem::ItemIsSelectable, true);
451   setFlag(QGraphicsItem::ItemIsFocusable, false);
452   setZValue(0.0);
453 }
454 
455 //--------------------------------------------------------
456 
~SchematicLink()457 SchematicLink::~SchematicLink() { m_startPort = m_endPort = 0; }
458 
459 //--------------------------------------------------------
460 
boundingRect() const461 QRectF SchematicLink::boundingRect() const {
462   return m_hitPath.boundingRect().adjusted(-5, -5, 5, 5);
463 }
464 
465 //--------------------------------------------------------
466 
shape() const467 QPainterPath SchematicLink::shape() const { return m_hitPath; }
468 
469 //--------------------------------------------------------
470 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)471 void SchematicLink::paint(QPainter *painter,
472                           const QStyleOptionGraphicsItem *option,
473                           QWidget *widget) {
474   SchematicViewer *viewer;
475   FxSchematicScene *fxScene = dynamic_cast<FxSchematicScene *>(scene());
476   StageSchematicScene *stageScene =
477       dynamic_cast<StageSchematicScene *>(scene());
478   if (fxScene) {
479     viewer = fxScene->getSchematicViewer();
480   } else if (stageScene) {
481     viewer = stageScene->getSchematicViewer();
482   } else {
483     return;
484   }
485 
486   if (getStartPort() && (getStartPort()->getType() == 100  // eStageSplinePort
487                          || getStartPort()->getType() == 202)) {  // eFxLinkPort
488     if (isSelected() || isHighlighted())
489       painter->setPen(QPen(viewer->getMotionPathSelectedLinkColor()));
490     else
491       painter->setPen(QColor(viewer->getMotionPathLinkColor()));
492   } else if (isSelected() || isHighlighted())
493     painter->setPen(QPen(viewer->getSelectedLinkColor()));
494 
495   else if (!m_lineShaped)
496     painter->setPen(QPen(viewer->getLinkColor()));
497   else
498     painter->setPen(QPen(QColor(170, 170, 10), 0, Qt::DashLine));
499 
500   painter->setRenderHint(QPainter::Antialiasing, true);
501   painter->drawPath(m_path);
502 }
503 
504 //--------------------------------------------------------
505 
updatePath(const QPointF & startPos,const QPointF & endPos)506 void SchematicLink::updatePath(const QPointF &startPos, const QPointF &endPos) {
507   prepareGeometryChange();
508   setPos(startPos);
509   if (!m_lineShaped) {
510     QPointF p0((endPos.x() - startPos.x()) * 0.5, 0);
511     QPointF p1(p0.x(), endPos.y() - startPos.y());
512     QPointF p2(endPos - startPos);
513 
514     m_path = QPainterPath(QPointF(0, 0));
515     m_path.cubicTo(p0, p1, p2);
516 
517     QPointF h(0, 5);
518     QPointF p = h;
519     if (p2.y() > 0)
520       p.setX(p2.x() > 0 ? -5 : 5);
521     else
522       p.setX(p2.x() > 0 ? 5 : -5);
523     m_hitPath = QPainterPath(QPointF(0, 0));
524     m_hitPath.lineTo(h);
525     m_hitPath.cubicTo(p0 + p, p1 + p, p2 + h);
526     m_hitPath.lineTo(p2 - h);
527     m_hitPath.cubicTo(p1 - p, p0 - p, -h);
528     m_hitPath.lineTo(0, 0);
529   } else {
530     m_path = QPainterPath(QPointF(0, 0));
531     m_path.lineTo(endPos - startPos);
532 
533     m_hitPath = QPainterPath(QPointF(0, 0));
534     m_hitPath.lineTo(endPos - startPos);
535   }
536 }
537 
538 //--------------------------------------------------------
539 
updatePath(SchematicPort * startPort,SchematicPort * endPort)540 void SchematicLink::updatePath(SchematicPort *startPort,
541                                SchematicPort *endPort) {
542   updatePath(startPort->getLinkEndPoint(), endPort->getLinkEndPoint());
543 }
544 
545 //--------------------------------------------------------
546 
updateEndPos(const QPointF & endPos)547 void SchematicLink::updateEndPos(const QPointF &endPos) {
548   if (m_startPort) updatePath(m_startPort->getLinkEndPoint(), endPos);
549 }
550 
551 //--------------------------------------------------------
552 
getOtherPort(const SchematicPort * port) const553 SchematicPort *SchematicLink::getOtherPort(const SchematicPort *port) const {
554   if (port == m_startPort)
555     return m_endPort;
556   else if (port == m_endPort)
557     return m_startPort;
558   else
559     return 0;
560 }
561 
562 //--------------------------------------------------------
563 
getOtherNode(const SchematicNode * node) const564 SchematicNode *SchematicLink::getOtherNode(const SchematicNode *node) const {
565   if (node == m_startPort->getNode())
566     return m_endPort->getNode();
567   else if (node == m_endPort->getNode())
568     return m_startPort->getNode();
569   else
570     return 0;
571 }
572 
573 //--------------------------------------------------------
574 
mousePressEvent(QGraphicsSceneMouseEvent * me)575 void SchematicLink::mousePressEvent(QGraphicsSceneMouseEvent *me) {
576   QPointF pos              = me->scenePos();
577   SchematicPort *startPort = getStartPort();
578   SchematicPort *endPort   = getEndPort();
579 
580   if (!startPort || !endPort) {
581     me->ignore();
582     return;
583   }
584 
585   if (startPort && endPort) {
586     QRectF startRect = startPort->boundingRect();
587     startRect.moveTopLeft(startPort->scenePos());
588     QRectF endRect = endPort->boundingRect();
589     endRect.moveTopLeft(endPort->scenePos());
590     if (startRect.contains(pos) || endRect.contains(pos)) {
591       me->ignore();
592       return;
593     }
594   }
595 
596   QMatrix matrix = scene()->views()[0]->matrix();
597 #if QT_VERSION >= 0x050000
598   double scaleFactor = sqrt(matrix.determinant());
599 #else
600   double scaleFactor = sqrt(matrix.det());
601 #endif
602 
603   QPointF startPos = getStartPort()->getLinkEndPoint();
604   QPointF endPos   = getEndPort()->getLinkEndPoint();
605   QPointF p0((endPos.x() - startPos.x()) * 0.5, 0);
606   QPointF p1(p0.x(), endPos.y() - startPos.y());
607   QPointF p2(endPos - startPos);
608   double sensitivity = 5 / scaleFactor;
609   QPointF h(0, sensitivity);
610   QPointF p = h;
611   if (p2.y() > 0)
612     p.setX(p2.x() > 0 ? -sensitivity : sensitivity);
613   else
614     p.setX(p2.x() > 0 ? sensitivity : -sensitivity);
615   QPainterPath path(QPointF(0, 0));
616   path.lineTo(h);
617   path.cubicTo(p0 + p, p1 + p, p2 + h);
618   path.lineTo(p2 - h);
619   path.cubicTo(p1 - p, p0 - p, -h);
620   path.lineTo(0, 0);
621   if (!path.contains(me->scenePos() - scenePos())) {
622     me->ignore();
623     return;
624   }
625 
626   if (!isSelected()) {
627     if (me->modifiers() != Qt::ControlModifier) scene()->clearSelection();
628     if (me->button() == Qt::LeftButton || me->button() == Qt::RightButton)
629       setSelected(true);
630   } else {
631     if (me->modifiers() == Qt::ControlModifier &&
632         me->button() == Qt::LeftButton)
633       setSelected(false);
634   }
635 }
636 
637 //--------------------------------------------------------
638 
mouseReleaseEvent(QGraphicsSceneMouseEvent * me)639 void SchematicLink::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) {
640   if (me->modifiers() != Qt::ControlModifier && me->button() != Qt::RightButton)
641     QGraphicsItem::mouseReleaseEvent(me);
642 }
643 
644 //========================================================
645 //
646 // class SchematicPort
647 //
648 //========================================================
649 
SchematicPort(QGraphicsItem * parent,SchematicNode * node,int type)650 SchematicPort::SchematicPort(QGraphicsItem *parent, SchematicNode *node,
651                              int type)
652     : QGraphicsItem(parent)
653     , m_node(node)
654     , m_buttonState(Qt::NoButton)
655     , m_highlighted(false)
656     , m_linkingTo(0)
657     , m_hook(0, 0)
658     , m_type(type) {
659 #if QT_VERSION >= 0x050000
660   setAcceptHoverEvents(false);
661 #else
662   setAcceptsHoverEvents(false);
663 #endif
664   setFlag(QGraphicsItem::ItemIsSelectable, false);
665   setFlag(QGraphicsItem::ItemIsFocusable, false);
666 }
667 
668 //--------------------------------------------------------
669 
~SchematicPort()670 SchematicPort::~SchematicPort() { m_links.clear(); }
671 
672 //--------------------------------------------------------
673 
mouseMoveEvent(QGraphicsSceneMouseEvent * me)674 void SchematicPort::mouseMoveEvent(QGraphicsSceneMouseEvent *me) {
675   if (m_buttonState != Qt::LeftButton) return;
676   if (m_ghostLinks.isEmpty()) return;
677 
678   // Snapping
679   SchematicPort *linkingTo = searchPort(me->scenePos());
680   if (!linkingTo) {
681     for (SchematicLink *ghostLink : m_ghostLinks) {
682       ghostLink->updateEndPos(me->scenePos());
683       ghostLink->getStartPort()->showSnappedLinks(m_linkingTo);
684     }
685     if (m_linkingTo) {
686       m_linkingTo->highLight(false);
687       m_linkingTo->update();
688       m_linkingTo = nullptr;
689     }
690   }
691   // if to be connected something
692   else if (linkingTo != this) {
693     m_linkingTo = linkingTo;
694     for (SchematicLink *ghostLink : m_ghostLinks) {
695       ghostLink->updatePath(ghostLink->getStartPort(), linkingTo);
696       ghostLink->getStartPort()->hideSnappedLinks(m_linkingTo);
697     }
698   }
699   // autopan
700   QGraphicsView *viewer = scene()->views()[0];
701   viewer->setInteractive(false);
702   viewer->ensureVisible(QRectF(me->scenePos(), QSizeF(1, 1)), 5, 5);
703   viewer->setInteractive(true);
704 }
705 
706 //--------------------------------------------------------
707 
mousePressEvent(QGraphicsSceneMouseEvent * me)708 void SchematicPort::mousePressEvent(QGraphicsSceneMouseEvent *me) {
709   if (!isSelected()) {
710     if (me->modifiers() != Qt::ControlModifier) scene()->clearSelection();
711     if (me->button() == Qt::LeftButton || me->button() == Qt::RightButton)
712       getNode()->setSelected(true);
713   } else {
714     if (me->modifiers() == Qt::ControlModifier &&
715         me->button() == Qt::LeftButton)
716       getNode()->setSelected(false);
717   }
718   getNode()->onClicked();
719   if (me->button() == Qt::LeftButton && getType() != 202  // eFxLinkPort
720       && getType() != 203                                 // eFxGroupedInPort
721       && getType() != 204                                 // eFxGroupedOutPort
722       && getType() != 103   // eStageSplineGroupPort
723       && getType() != 104   // eStageParentGroupPort
724       && getType() != 105)  // eStageChildGroupPort
725   {
726     m_buttonState = Qt::LeftButton;
727     QPointF endPos(me->pos());
728 
729     // Enable to connect multiple links from all selected nodes
730     // only when ( Ctrl + ) dragging from the eStageParentPort.
731     if (getType() == 101) {  // eStageParentPort
732       QList<QGraphicsItem *> items = scene()->selectedItems();
733       if (items.empty()) return;
734       for (auto const &item : items) {
735         SchematicNode *node = dynamic_cast<SchematicNode *>(item);
736         if (node) {
737           SchematicPort *parentPort =
738               node->getPort(0);  // id 0 is for parent port.
739           if (parentPort) {
740             SchematicLink *ghostLink = new SchematicLink(0, scene());
741             ghostLink->setStartPort(parentPort);
742             ghostLink->setZValue(3.0);
743             ghostLink->updateEndPos(me->scenePos());
744             m_ghostLinks.push_back(ghostLink);
745           }
746         }
747       }
748     } else {
749       SchematicLink *ghostLink = new SchematicLink(0, scene());
750       ghostLink->setStartPort(this);
751       ghostLink->setZValue(3.0);
752       ghostLink->updateEndPos(me->scenePos());
753       m_ghostLinks.push_back(ghostLink);
754     }
755     emit(isClicked());
756   }
757 }
758 
759 //--------------------------------------------------------
760 
mouseReleaseEvent(QGraphicsSceneMouseEvent * me)761 void SchematicPort::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) {
762   if (me->modifiers() != Qt::ControlModifier && me->button() != Qt::RightButton)
763     QGraphicsItem::mouseReleaseEvent(me);
764 
765   if (m_buttonState == Qt::LeftButton) emit(isReleased(me->scenePos()));
766 
767   // The link is added to the scene only if the user released the left mouse
768   // button over
769   // a SchematicPort different from SchematicPort of the parent node.
770   bool somethingChanged = false;
771   if (m_buttonState == Qt::LeftButton && m_linkingTo) {
772     TUndoManager::manager()->beginBlock();
773     for (SchematicLink *ghostLink : m_ghostLinks) {
774       SchematicPort *port = ghostLink->getStartPort();
775       if (!port) continue;
776       if (!port->isLinkedTo(m_linkingTo) && port->linkTo(m_linkingTo, true)) {
777         port->linkTo(m_linkingTo);
778         somethingChanged = true;
779       } else
780         port->showSnappedLinks(m_linkingTo);
781     }
782     m_buttonState = Qt::NoButton;
783     m_linkingTo   = 0;
784     TUndoManager::manager()->endBlock();
785   }
786 
787   if (!m_ghostLinks.isEmpty()) {
788     for (SchematicLink *ghostLink : m_ghostLinks)
789       scene()->removeItem(ghostLink);
790     qDeleteAll(m_ghostLinks.begin(), m_ghostLinks.end());
791     m_ghostLinks.clear();
792   }
793 
794   if (somethingChanged) {
795     emit sceneChanged();
796     emit xsheetChanged();
797   }
798 }
799 
800 //--------------------------------------------------------
801 
removeLink(SchematicLink * link)802 void SchematicPort::removeLink(SchematicLink *link) { m_links.removeAll(link); }
803 
804 //--------------------------------------------------------
805 
eraseLink(SchematicLink * link)806 void SchematicPort::eraseLink(SchematicLink *link) {
807   SchematicPort *otherPort = link->getOtherPort(this);
808   if (otherPort) otherPort->removeLink(link);
809   removeLink(link);
810   if (link->scene()) link->scene()->removeItem(link);
811   delete link;
812 }
813 
814 //--------------------------------------------------------
815 
eraseAllLinks()816 void SchematicPort::eraseAllLinks() {
817   while (!m_links.empty()) eraseLink(m_links[0]);
818 }
819 
820 //--------------------------------------------------------
821 
makeLink(SchematicPort * port)822 SchematicLink *SchematicPort::makeLink(SchematicPort *port) {
823   if (isLinkedTo(port) || !port) return 0;
824   SchematicLink *link = new SchematicLink(0, scene());
825   if (getType() == 202 && port->getType() == 202) link->setLineShaped(true);
826 
827   link->setStartPort(this);
828   link->setEndPort(port);
829   addLink(link);
830   port->addLink(link);
831   link->updatePath();
832   return link;
833 }
834 
835 //--------------------------------------------------------
836 
isLinkedTo(SchematicPort * port) const837 bool SchematicPort::isLinkedTo(SchematicPort *port) const {
838   if (m_links.size() == 0) return false;
839   int i;
840   for (i = 0; i < m_links.size(); i++) {
841     SchematicLink *link = m_links[i];
842     if (((link->getStartPort() == this && link->getEndPort() == port) ||
843          (link->getEndPort() == this && link->getStartPort() == port)) &&
844         link->isVisible())
845       return true;
846   }
847   return false;
848 }
849 
850 //--------------------------------------------------------
851 
updateLinksGeometry()852 void SchematicPort::updateLinksGeometry() {
853   int linkCount = getLinkCount();
854   int i;
855   for (i = 0; i < linkCount; i++) {
856     SchematicLink *link      = getLink(i);
857     SchematicPort *startPort = link->getStartPort();
858     SchematicPort *endPort   = link->getEndPort();
859     if (startPort && endPort) {
860       link->updatePath(startPort, endPort);
861       link->setPos(startPort->getLinkEndPoint());
862     }
863   }
864 }
865 
866 //--------------------------------------------------------
867 
getLinkEndPoint() const868 QPointF SchematicPort::getLinkEndPoint() const { return scenePos() + m_hook; }
869 
870 //========================================================
871 //
872 // class SchematicNode
873 //
874 //========================================================
875 
876 /*! \class SchematicNode schematicnode.h "../inlcude/toonzqt/schematicnode.h"
877         \brief The class provides methods to draw and handle a node item in the
878    SchematicScene.
879 */
880 
881 /*! \fn SchematicPort *SchematicNode::getInputPort() const
882         Returns the input SchematicPort of the node.
883 */
884 
885 /*! \fn SchematicPort *SchematicNode::getOutputPort() const
886         Returns the output SchematicPort of the node.
887 */
888 
889 /*! \fn SchematicScene* SchematicNode::getScene() const
890         Returns the scene where the node is placed.
891 */
892 
SchematicNode(SchematicScene * scene)893 SchematicNode::SchematicNode(SchematicScene *scene)
894 #if QT_VERSION >= 0x050000
895     : QGraphicsItem(0)
896 #else
897     : QGraphicsItem(0, scene)
898 #endif
899     , m_scene(scene)
900     , m_width(0)
901     , m_height(0)
902     , m_buttonState(Qt::NoButton) {
903 #if QT_VERSION >= 0x050000
904   scene->addItem(this);
905 #endif
906   setFlag(QGraphicsItem::ItemIsMovable, false);
907   setFlag(QGraphicsItem::ItemIsSelectable, true);
908   setFlag(QGraphicsItem::ItemIsFocusable, false);
909   setZValue(1.0);
910 }
911 
912 //--------------------------------------------------------
913 
~SchematicNode()914 SchematicNode::~SchematicNode() {}
915 
916 //--------------------------------------------------------
917 
918 /*!Reimplements the pure virtual QGraphicsItem::boundingRect() method.
919 */
boundingRect() const920 QRectF SchematicNode::boundingRect() const { return QRectF(0, 0, 1, 1); }
921 
922 //--------------------------------------------------------
923 
924 /*! Reimplements the pure virtual QGraphicsItem::paint() method.
925 */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)926 void SchematicNode::paint(QPainter *painter,
927                           const QStyleOptionGraphicsItem *option,
928                           QWidget *widget) {
929   SchematicViewer *viewer;
930   FxSchematicScene *fxScene = dynamic_cast<FxSchematicScene *>(scene());
931   StageSchematicScene *stageScene =
932       dynamic_cast<StageSchematicScene *>(scene());
933   if (fxScene) {
934     viewer = fxScene->getSchematicViewer();
935   } else if (stageScene) {
936     viewer = stageScene->getSchematicViewer();
937   } else {
938     return;
939   }
940 
941   QPen pen;
942   if (isSelected()) {
943     painter->setBrush(QColor(0, 0, 0, 0));
944     pen.setColor(QColor(viewer->getSelectedBorderColor()));
945 
946     pen.setWidth(4.0);
947     pen.setJoinStyle(Qt::RoundJoin);
948     painter->setPen(pen);
949     painter->drawRect(-2, -2, m_width + 4, m_height + 4);
950   }
951   pen.setColor(QColor(0, 0, 0, 255));
952   pen.setWidth(0);
953   painter->setPen(pen);
954 }
955 
956 //--------------------------------------------------------
957 
958 /*! Reimplements the QGraphicsItem::mouseMoveEvent() method.
959 */
mouseMoveEvent(QGraphicsSceneMouseEvent * me)960 void SchematicNode::mouseMoveEvent(QGraphicsSceneMouseEvent *me) {
961   QList<QGraphicsItem *> items = scene()->selectedItems();
962   if (items.empty()) return;
963   QPointF delta         = me->scenePos() - me->lastScenePos();
964   QGraphicsView *viewer = scene()->views()[0];
965   for (auto const &item : items) {
966     SchematicNode *node = dynamic_cast<SchematicNode *>(item);
967     if (node) {
968       node->setPosition(node->scenePos() + delta);
969       node->setSchematicNodePos(node->scenePos());
970       node->updateLinksGeometry();
971     }
972   }
973   viewer->setInteractive(false);
974   viewer->ensureVisible(QRectF(me->scenePos(), QSizeF(1, 1)), 5, 5);
975   viewer->setInteractive(true);
976 }
977 
978 //--------------------------------------------------------
979 
mousePressEvent(QGraphicsSceneMouseEvent * me)980 void SchematicNode::mousePressEvent(QGraphicsSceneMouseEvent *me) {
981   if (!isSelected()) {
982     if (me->modifiers() != Qt::ControlModifier) scene()->clearSelection();
983     if (me->button() == Qt::LeftButton || me->button() == Qt::RightButton)
984       setSelected(true);
985   } else {
986     if (me->modifiers() == Qt::ControlModifier &&
987         me->button() == Qt::LeftButton)
988       setSelected(false);
989   }
990   onClicked();
991 }
992 
993 //--------------------------------------------------------
994 
mouseReleaseEvent(QGraphicsSceneMouseEvent * me)995 void SchematicNode::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) {
996   if (me->modifiers() != Qt::ControlModifier && me->button() != Qt::RightButton)
997     QGraphicsItem::mouseReleaseEvent(me);
998 }
999 
1000 //--------------------------------------------------------
1001 /* Add a pair (portId, SchematicPort*port) in the mapping
1002 */
addPort(int portId,SchematicPort * port)1003 SchematicPort *SchematicNode::addPort(int portId, SchematicPort *port) {
1004   QMap<int, SchematicPort *>::iterator it;
1005   it = m_ports.find(portId);
1006   if (it != m_ports.end() && m_ports[portId] != port) {
1007     SchematicPort *oldPort = m_ports[portId];
1008     m_ports.erase(it);
1009     scene()->removeItem(oldPort);
1010     delete oldPort;
1011   }
1012   m_ports[portId] = port;
1013   return port;
1014 }
1015 
1016 //--------------------------------------------------------
1017 
1018 /*!Erase the pair (portId, SchematicPort*port) from the mapping*/
erasePort(int portId)1019 void SchematicNode::erasePort(int portId) {
1020   QMap<int, SchematicPort *>::iterator it = m_ports.find(portId);
1021   if (it != m_ports.end()) {
1022     delete it.value();
1023     m_ports.erase(it);
1024   }
1025 }
1026 
1027 //--------------------------------------------------------
1028 
1029 /*! Returns a pointer to the SchematicPort mapped from \b portId.\n
1030     Returns 0 if \b portId doesn't map no SchematicPort.
1031 */
getPort(int portId) const1032 SchematicPort *SchematicNode::getPort(int portId) const {
1033   QMap<int, SchematicPort *>::const_iterator it = m_ports.find(portId);
1034   if (it != m_ports.end()) return it.value();
1035   return 0;
1036 }
1037 
1038 //--------------------------------------------------------
1039 
1040 /*! Returns a list of all node connected by links to a SchematicPort identified
1041  * by \b portId.
1042 */
getLinkedNodes(int portId) const1043 QList<SchematicNode *> SchematicNode::getLinkedNodes(int portId) const {
1044   QList<SchematicNode *> list;
1045   SchematicPort *port = getPort(portId);
1046   if (port) {
1047     int linkCount = port->getLinkCount();
1048     int i;
1049     for (i = 0; i < linkCount; i++) list.push_back(port->getLinkedNode(i));
1050   }
1051   return list;
1052 }
1053 
1054 //--------------------------------------------------------
1055 
updateLinksGeometry()1056 void SchematicNode::updateLinksGeometry() {
1057   QMap<int, SchematicPort *>::iterator it;
1058   for (it = m_ports.begin(); it != m_ports.end(); ++it)
1059     it.value()->updateLinksGeometry();
1060 }
1061