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