1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5 
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ********************************************************************
20 
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24 
25 ********************************************************************/
26 
27 
28 #include "pegraphicsitem.h"
29 #include "../debugdialog.h"
30 #include "../sketch/infographicsview.h"
31 #include "../items/itembase.h"
32 
33 #include <QBrush>
34 #include <QColor>
35 #include <QGraphicsScene>
36 #include <QPainter>
37 
38 
39 static QVector<qreal> Dashes;
40 static const int DashLength = 3;
41 
42 static bool ShiftDown = false;
43 static QPointF OriginalShiftPos;
44 static bool ShiftX = false;
45 static bool ShiftY = false;
46 static bool SpaceBarWasPressed = false;
47 static const double MinMouseMove = 2;
48 static const QColor NormalColor(0, 0, 255);
49 static const QColor PickColor(255, 0, 255);
50 
51 
52 ////////////////////////////////////////////////
53 
54 
PEGraphicsItem(double x,double y,double w,double h,ItemBase * itemBase)55 PEGraphicsItem::PEGraphicsItem(double x, double y, double w, double h, ItemBase * itemBase) : QGraphicsRectItem(x, y, w, h) {
56     if (Dashes.isEmpty()) {
57         Dashes << DashLength << DashLength;
58     }
59 
60     m_itemBase = itemBase;
61     m_pick = m_flash = false;
62 
63     m_terminalPoint = QPointF(w / 2, h / 2);
64     m_dragTerminalPoint = m_showMarquee = m_showTerminalPoint = false;
65 	setAcceptedMouseButtons(Qt::LeftButton);
66 	setAcceptHoverEvents(true);
67     //setFlag(QGraphicsItem::ItemIsSelectable, true );
68     setHighlighted(false);
69     setBrush(QBrush(NormalColor));
70 }
71 
~PEGraphicsItem()72 PEGraphicsItem::~PEGraphicsItem() {
73     m_element.clear();
74 }
75 
hoverEnterEvent(QGraphicsSceneHoverEvent *)76 void PEGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *) {
77     m_wheelAccum = 0;
78 	setHighlighted(true);
79 }
80 
hoverLeaveEvent(QGraphicsSceneHoverEvent *)81 void PEGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
82 	setHighlighted(false);
83 }
84 
wheelEvent(QGraphicsSceneWheelEvent * event)85 void PEGraphicsItem::wheelEvent(QGraphicsSceneWheelEvent * event) {
86     //DebugDialog::debug(QString("wheel %1 %2").arg(event->delta()).arg(event->orientation()));
87 
88 #ifndef Q_OS_MAC
89     // qt 4.8.3: mac: event orientation is messed up at this point
90     if (event->orientation() == Qt::Horizontal) return;
91 #endif
92     if (event->delta() == 0) return;
93     if ((event->modifiers() & Qt::ShiftModifier) == 0) return;
94 
95 
96     // delta one click forward = 120; delta one click backward = -120
97 
98     int magDelta = qAbs(event->delta());
99     int sign = event->delta() / magDelta;
100     int delta = sign * qMin(magDelta, 120);
101     m_wheelAccum += delta;
102     if (qAbs(m_wheelAccum) < 120) return;
103 
104     m_wheelAccum = 0;
105 
106     QList<PEGraphicsItem *> items;
107     foreach (QGraphicsItem * item, scene()->items(event->scenePos())) {
108         PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
109         if (pegi) items.append(pegi);
110     }
111 
112     if (items.count() < 2) return;
113 
114     int ix = -1;
115     int mix = -1;
116     for (int i = 0; i < items.count(); i++) {
117         if (items.at(i)->highlighted()) {
118             ix = i;
119             break;
120         }
121         if (items.at(i) == this) {
122             mix = i;
123         }
124     }
125 
126     if (ix == -1) ix = mix;
127     if (ix == -1) {
128         // shouldn't happen
129         return;
130     }
131 
132     ix += sign;
133 
134     // wrap
135     //while (ix < 0) {
136     //    ix += items.count();
137     //}
138     //ix = ix % items.count();
139 
140     // don't wrap
141     if (ix < 0) ix = 0;
142     else if (ix >= items.count()) ix = items.count() - 1;
143 
144     PEGraphicsItem * theItem = items.at(ix);
145     if (theItem->highlighted()) {
146         theItem->flash();
147     }
148     else {
149         theItem->setHighlighted(true);
150     }
151 }
152 
setHighlighted(bool highlighted)153 void PEGraphicsItem::setHighlighted(bool highlighted) {
154     if (highlighted) {
155         m_highlighted = true;
156         setOpacity(0.4);
157         foreach (QGraphicsItem * item, scene()->items()) {
158             PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
159             if (pegi == NULL) continue;
160             if (pegi == this) continue;
161             if (!pegi->highlighted()) continue;
162 
163             pegi->setHighlighted(false);
164         }
165         emit highlightSignal(this);
166     }
167     else {
168         m_highlighted = false;
169         setOpacity(0.001);
170     }
171     update();
172 }
173 
highlighted()174 bool PEGraphicsItem::highlighted() {
175     return m_highlighted;
176 }
177 
setElement(QDomElement & el)178 void PEGraphicsItem::setElement(QDomElement & el)
179 {
180     m_element = el;
181 }
182 
element()183 QDomElement & PEGraphicsItem::element() {
184     return m_element;
185 }
186 
setOffset(QPointF p)187 void PEGraphicsItem::setOffset(QPointF p) {
188     m_offset = p;
189 }
190 
offset()191 QPointF PEGraphicsItem::offset() {
192     return m_offset;
193 }
194 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)195 void PEGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
196 {
197 	QGraphicsRectItem::paint(painter, option, widget);
198 
199     if (m_flash) {
200         m_flash = false;
201         QTimer::singleShot(40, this, SLOT(restoreColor()));
202     }
203 
204 	bool save = m_showTerminalPoint || m_showMarquee;
205 
206 	if (save) painter->save();
207 
208     if (m_showTerminalPoint) {
209         QRectF r = rect();
210         QLineF l1(0, m_terminalPoint.y(), r.width(), m_terminalPoint.y());
211         QLineF l2(m_terminalPoint.x(), 0, m_terminalPoint.x(), r.height());
212 
213         painter->setOpacity(1.0);
214         painter->setPen(QPen(QColor(0, 0, 0), 0, Qt::SolidLine));
215         painter->setBrush(Qt::NoBrush);
216         painter->drawLine(l1);
217         painter->drawLine(l2);
218 
219 	    painter->setPen(QPen(QColor(255, 255, 255), 0, Qt::DashLine));
220         painter->setBrush(Qt::NoBrush);
221         painter->drawLine(l1);
222         painter->drawLine(l2);
223     }
224 
225 	if (m_showMarquee) {
226         QRectF r = rect();
227 		double d = qMin(r.width(), r.height()) / 16;
228 		r.adjust(d, d, -d, -d);
229 
230         painter->setOpacity(1.0);
231         painter->setPen(QPen(QColor(0, 0, 0), 0, Qt::SolidLine));
232         painter->setBrush(Qt::NoBrush);
233         painter->drawRect(r);
234 
235 	    painter->setPen(QPen(QColor(255, 255, 255), 0, Qt::DashLine));
236         painter->setBrush(Qt::NoBrush);
237         painter->drawRect(r);
238 	}
239 
240 	if (save) painter->restore();
241 }
242 
showTerminalPoint(bool show)243 void PEGraphicsItem::showTerminalPoint(bool show)
244 {
245     m_showTerminalPoint = show;
246     update();
247 }
248 
249 
showingTerminalPoint()250 bool PEGraphicsItem::showingTerminalPoint() {
251     return m_showTerminalPoint;
252 }
253 
showMarquee(bool show)254 void PEGraphicsItem::showMarquee(bool show) {
255     if (show) {
256         m_showMarquee = true;
257         foreach (QGraphicsItem * item, scene()->items()) {
258             PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
259             if (pegi == NULL) continue;
260             if (pegi == this) continue;
261             if (!pegi->showingMarquee()) continue;
262 
263             pegi->showMarquee(false);
264 		}
265     }
266     else {
267         m_showMarquee = false;
268     }
269     update();
270 }
271 
showingMarquee()272 bool PEGraphicsItem::showingMarquee() {
273     return m_showMarquee;
274 }
275 
setTerminalPoint(QPointF p)276 void PEGraphicsItem::setTerminalPoint(QPointF p) {
277     m_pendingTerminalPoint = m_terminalPoint = p;
278 }
279 
terminalPoint()280 QPointF PEGraphicsItem::terminalPoint() {
281     return m_terminalPoint;
282 }
283 
setPendingTerminalPoint(QPointF p)284 void PEGraphicsItem::setPendingTerminalPoint(QPointF p) {
285     m_pendingTerminalPoint = p;
286 }
287 
pendingTerminalPoint()288 QPointF PEGraphicsItem::pendingTerminalPoint() {
289     return m_pendingTerminalPoint;
290 }
291 
mousePressEvent(QGraphicsSceneMouseEvent * event)292 void PEGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent * event) {
293     m_dragTerminalPoint = false;
294 
295 	if (!m_highlighted) {
296 		// allows to click through to next layer
297 		event->ignore();
298 		return;
299     }
300 
301 	if (!event->buttons() && Qt::LeftButton) {
302 		event->ignore();
303 		return;
304 	}
305 
306 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
307 	if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) {
308 		event->ignore();
309 		return;
310 	}
311 
312 	bool ignore;
313 	emit mousePressedSignal(this, ignore);
314 	if (ignore) {
315 		event->ignore();
316 		return;
317 	}
318 
319     if (m_showMarquee) {
320         QPointF p = event->pos();
321 	    if (event->modifiers() & Qt::ShiftModifier) {
322 		    ShiftDown = true;
323             ShiftX = ShiftY = false;
324 		    OriginalShiftPos = p;
325 	    }
326 
327         if (qAbs(p.x() - m_terminalPoint.x()) <= MinMouseMove && qAbs(p.y() - m_terminalPoint.y()) <= MinMouseMove) {
328             m_dragTerminalPoint = true;
329             m_terminalPointOrigin = m_terminalPoint;
330             m_dragTerminalOrigin = event->pos();
331         }
332 
333 		return;
334     }
335 
336     event->ignore();
337 }
338 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)339 void PEGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
340     if (!m_dragTerminalPoint) return;
341 
342     if (ShiftDown && !(event->modifiers() & Qt::ShiftModifier)) {
343 		ShiftDown = false;
344 	}
345 	QPointF p = event->pos();
346 	if (ShiftDown) {
347 		if (ShiftX) {
348 			// moving along x, constrain y
349 			p.setY(OriginalShiftPos.y());
350 		}
351 		else if (ShiftY) {
352 			// moving along y, constrain x
353 			p.setX(OriginalShiftPos.x());
354 		}
355         else {
356             double dx = qAbs(p.x() - OriginalShiftPos.x());
357             double dy = qAbs(p.y() - OriginalShiftPos.y());
358             if (dx - dy > MinMouseMove) {
359                 ShiftX = true;
360             }
361             else if (dy - dx > MinMouseMove) {
362                 ShiftY = true;
363             }
364         }
365 	}
366 	else if (event->modifiers() & Qt::ShiftModifier) {
367 		ShiftDown = true;
368         ShiftX = ShiftY = false;
369         OriginalShiftPos = event->pos();
370 	}
371 
372 
373     QPointF newTerminalPoint = m_terminalPointOrigin + p - m_dragTerminalOrigin;
374     if (newTerminalPoint.x() < 0) newTerminalPoint.setX(0);
375     else if (newTerminalPoint.x() > rect().width()) newTerminalPoint.setX(rect().width());
376     if (newTerminalPoint.y() < 0) newTerminalPoint.setY(0);
377     else if (newTerminalPoint.y() > rect().height()) newTerminalPoint.setY(rect().height());
378     m_terminalPoint = newTerminalPoint;
379     emit terminalPointMoved(this, newTerminalPoint);
380     update();
381 }
382 
383 
mouseReleaseEvent(QGraphicsSceneMouseEvent *)384 void PEGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) {
385     if (m_dragTerminalPoint) {
386         m_dragTerminalPoint = false;
387         if (m_terminalPointOrigin != m_terminalPoint) {
388             emit terminalPointChanged(this, m_terminalPointOrigin, m_terminalPoint);
389         }
390     }
391     else {
392 		// relocate the connector
393         emit mouseReleasedSignal(this);
394     }
395 }
396 
setPickAppearance(bool pick)397 void PEGraphicsItem::setPickAppearance(bool pick) {
398     m_pick = pick;
399 	setBrush(pick ? PickColor : NormalColor);
400 }
401 
flash()402 void PEGraphicsItem::flash() {
403     m_savedOpacity = opacity();
404     m_flash = true;
405     setBrush(QColor(255, 255, 255));
406     update();
407 }
408 
restoreColor()409 void PEGraphicsItem::restoreColor() {
410     setBrush(m_pick ? PickColor : NormalColor);
411     setOpacity(m_savedOpacity);
412     update();
413 }
414 
itemBase()415 ItemBase * PEGraphicsItem::itemBase() {
416     return m_itemBase;
417 }
418