1 /***************************************************************************
2 ** **
3 ** Polyphone, a soundfont editor **
4 ** Copyright (C) 2013-2020 Davy Triponney **
5 ** **
6 ** This program 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 ** This program 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 this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Davy Triponney **
21 ** Website/Contact: https://www.polyphone-soundfonts.com **
22 ** Date: 01.01.2013 **
23 ***************************************************************************/
24
25 #include "graphicsviewrange.h"
26 #include "contextmanager.h"
27 #include "graphicssimpletextitem.h"
28 #include "graphicsrectangleitem.h"
29 #include "graphicslegenditem.h"
30 #include "graphicslegenditem2.h"
31 #include "graphicszoomline.h"
32 #include "graphicskey.h"
33 #include <QScrollBar>
34 #include <QMouseEvent>
35 #include <QApplication>
36
37 const double GraphicsViewRange::WIDTH = 128.0;
38 const double GraphicsViewRange::MARGIN = 0.5;
39 const double GraphicsViewRange::OFFSET = -0.5;
40
41 // Z values (all GraphicsItem being at the top level)
42 // 0: grid
43 // 50: non selected rectangles
44 // 51: selected rectangles
45 // 80: play marker
46 // 100: axis values
47 // 120: legends
48 // 150: zoom line
49
GraphicsViewRange(QWidget * parent)50 GraphicsViewRange::GraphicsViewRange(QWidget *parent) : QGraphicsView(parent),
51 _sf2(SoundfontManager::getInstance()),
52 _scene(new QGraphicsScene(OFFSET, OFFSET, WIDTH, WIDTH)),
53 _legendItem(nullptr),
54 _legendItem2(nullptr),
55 _zoomLine(nullptr),
56 _dontRememberScroll(false),
57 _keyTriggered(-1),
58 _keepIndexOnRelease(false),
59 _editing(false),
60 _buttonPressed(Qt::NoButton),
61 _moveOccured(false),
62 _zoomX(1.),
63 _zoomY(1.),
64 _posX(0.5),
65 _posY(0.5),
66 _displayedRect(OFFSET, OFFSET, WIDTH, WIDTH),
67 _shiftRectangles(2, nullptr)
68 {
69 // Colors
70 QColor color = this->palette().color(QPalette::Text);
71 color.setAlpha(180);
72 _textColor = color;
73 color.setAlpha(40);
74 _lineColor = color;
75
76 // Configuration
77 this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
78 this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
79 this->setRenderHint(QPainter::Antialiasing, true);
80 this->setMouseTracking(true);
81
82 // Preparation of the graphics
83 this->setScene(_scene);
84 this->initItems();
85 }
86
~GraphicsViewRange()87 GraphicsViewRange::~GraphicsViewRange()
88 {
89 while (!_rectangles.isEmpty())
90 delete _rectangles.takeFirst();
91 while (!_leftLabels.isEmpty())
92 delete _leftLabels.takeFirst();
93 while (!_bottomLabels.isEmpty())
94 delete _bottomLabels.takeFirst();
95 delete _legendItem;
96 delete _legendItem2;
97 delete _zoomLine;
98 while (!_keyLines.isEmpty())
99 delete _keyLines.takeFirst();
100 while (!_mapGraphicsKeys.isEmpty())
101 delete _mapGraphicsKeys.take(_mapGraphicsKeys.keys().first());
102 delete _scene;
103 }
104
initItems()105 void GraphicsViewRange::initItems()
106 {
107 // Vertical lines
108 QPen penVerticalLines(_lineColor, 1);
109 penVerticalLines.setCosmetic(true);
110 for (quint32 note = 12; note <= 120; note += 12)
111 {
112 QGraphicsLineItem * line = new QGraphicsLineItem(note, OFFSET - MARGIN, note, OFFSET + WIDTH + MARGIN);
113 _scene->addItem(line);
114 line->setPen(penVerticalLines);
115 line->setZValue(0);
116 GraphicsSimpleTextItem * text = new GraphicsSimpleTextItem(Qt::AlignHCenter + Qt::AlignBottom);
117 _scene->addItem(text);
118 text->setZValue(100);
119 text->setBrush(_textColor);
120 text->setText(ContextManager::keyName()->getKeyName(note));
121 text->setPos(note, OFFSET + WIDTH);
122 _bottomLabels << text;
123 _keyLines << line;
124 }
125
126 // Horizontal lines
127 QPen penHorizontalLines(_lineColor, 1, Qt::DotLine);
128 penHorizontalLines.setCosmetic(true);
129 for (int vel = 10; vel <= 120; vel += 10)
130 {
131 QGraphicsLineItem * line = new QGraphicsLineItem(OFFSET - MARGIN, 127 - vel, OFFSET + WIDTH + MARGIN, 127 - vel);
132 _scene->addItem(line);
133 line->setPen(penHorizontalLines);
134 line->setZValue(0);
135 GraphicsSimpleTextItem * text = new GraphicsSimpleTextItem(Qt::AlignLeft + Qt::AlignVCenter);
136 _scene->addItem(text);
137 text->setZValue(100);
138 text->setBrush(_textColor);
139 text->setText(QString::number(vel));
140 text->setPos(OFFSET, OFFSET + WIDTH - vel);
141 _leftLabels << text;
142 _keyLines << line;
143 }
144
145 // Legends
146 _legendItem = new GraphicsLegendItem(this->font().family());
147 _scene->addItem(_legendItem);
148 _legendItem->setZValue(120);
149 _legendItem2 = new GraphicsLegendItem2(this->font().family());
150 _scene->addItem(_legendItem2);
151 _legendItem2->setZValue(120);
152
153 // Zoomline
154 _zoomLine = new GraphicsZoomLine();
155 _scene->addItem(_zoomLine);
156 _zoomLine->setZValue(150);
157 }
158
updateLabelPosition()159 void GraphicsViewRange::updateLabelPosition()
160 {
161 // Current rect
162 QRectF rect = getCurrentRect();
163
164 // Update the position of the axis labels (they stay to the left and bottom)
165 foreach (GraphicsSimpleTextItem * label, _leftLabels)
166 label->setX(qMax(OFFSET, rect.x()));
167 foreach (GraphicsSimpleTextItem * label, _bottomLabels)
168 label->setY(qMin(WIDTH + OFFSET, rect.y() + rect.height()));
169
170 // Update the position of the legend (it stays in a corner)
171 if (_legendItem->isLeft())
172 {
173 double posX = 35. * rect.width() / this->width() + qMax(OFFSET, rect.x());
174 _legendItem->setX(posX);
175 _legendItem2->setX(posX);
176 }
177 else
178 {
179 double posX = qMin(WIDTH + OFFSET, rect.x() + rect.width()) - 5. * rect.width() / this->width();
180 _legendItem->setX(posX);
181 _legendItem2->setX(posX);
182 }
183 _legendItem->setY(5. * rect.height() / this->height() + qMax(OFFSET, rect.y()));
184 _legendItem2->setY(13. * rect.height() / this->height() + qMax(OFFSET, rect.y()));
185 }
186
updateHover(QPoint mousePos)187 void GraphicsViewRange::updateHover(QPoint mousePos)
188 {
189 // Rectangles under the mouse
190 QList<QList<GraphicsRectangleItem *> > pairs = getRectanglesUnderMouse(mousePos);
191
192 // Highlighted rectangles below the mouse, priority for the selected rectangles
193 QList<GraphicsRectangleItem*> hoveredRectangles;
194 int selectionNumber = pairs.count();
195 int selectionIndex = 0;
196 if (selectionNumber > 0)
197 {
198 for (int i = 0; i < pairs.count(); i++)
199 {
200 bool ok = !pairs[i].isEmpty();
201 foreach (GraphicsRectangleItem * item, pairs[i])
202 ok &= item->isSelected();
203 if (ok)
204 {
205 selectionIndex = i;
206 break;
207 }
208 }
209 hoveredRectangles = pairs[selectionIndex];
210 }
211
212 // Update the hover, get the type of editing in the same time
213 GraphicsRectangleItem::EditingMode editingMode = GraphicsRectangleItem::NONE;
214 foreach (GraphicsRectangleItem * item, _rectangles)
215 {
216 if (hoveredRectangles.contains(item))
217 editingMode = item->setHover(true, mousePos);
218 else
219 item->setHover(false);
220 }
221
222 // Adapt the cursor depending on the way we can edit the rectangle
223 switch (editingMode)
224 {
225 case GraphicsRectangleItem::NONE:
226 this->setCursor(Qt::ArrowCursor);
227 break;
228 case GraphicsRectangleItem::MOVE_ALL:
229 this->setCursor(Qt::SizeAllCursor);
230 break;
231 case GraphicsRectangleItem::MOVE_RIGHT: case GraphicsRectangleItem::MOVE_LEFT:
232 this->setCursor(Qt::SizeHorCursor);
233 break;
234 case GraphicsRectangleItem::MOVE_TOP: case GraphicsRectangleItem::MOVE_BOTTOM:
235 this->setCursor(Qt::SizeVerCursor);
236 break;
237 }
238
239 // Update the content of the first legend
240 QList<EltID> ids;
241 QList<int> selectedIds;
242 int count = 0;
243 for (int i = 0; i < pairs.count(); i++)
244 {
245 foreach (GraphicsRectangleItem * item, pairs[i])
246 {
247 ids << item->getID();
248 if (i == selectionIndex)
249 selectedIds << count;
250 count++;
251 }
252 }
253 _legendItem->setIds(ids, selectedIds, selectionIndex, pairs.count());
254
255 // Offset and location of the second legend
256 _legendItem2->setOffsetY(_legendItem->boundingRect().bottom());
257 }
258
display(IdList ids,bool justSelection)259 void GraphicsViewRange::display(IdList ids, bool justSelection)
260 {
261 if (!justSelection)
262 {
263 // Clear previous rectangles
264 while (!_rectangles.isEmpty())
265 {
266 _scene->removeItem(_rectangles.first());
267 delete _rectangles.takeFirst();
268 }
269
270 // Add new ones
271 _defaultID = ids[0];
272 EltID idDiv = ids[0];
273 switch (_defaultID.typeElement)
274 {
275 case elementInst: case elementInstSmpl:
276 _defaultID.typeElement = elementInst;
277 idDiv.typeElement = elementInstSmpl;
278 break;
279 case elementPrst: case elementPrstInst:
280 _defaultID.typeElement = elementPrst;
281 idDiv.typeElement = elementPrstInst;
282 break;
283 default:
284 return;
285 }
286
287 foreach (int i, _sf2->getSiblings(idDiv))
288 {
289 idDiv.indexElt2 = i;
290 GraphicsRectangleItem * item = new GraphicsRectangleItem(idDiv);
291 item->setZValue(50);
292 _scene->addItem(item);
293 _rectangles << item;
294 }
295
296 // Reset the shiftpoints
297 _shiftRectangles.fill(nullptr, 2);
298
299 updateLabelPosition();
300 }
301
302 // Selection
303 foreach (GraphicsRectangleItem * item, _rectangles)
304 {
305 if (ids.contains(item->getID()))
306 {
307 item->setSelected(true);
308 _shiftRectangles[1] = item;
309 }
310 else {
311 item->setSelected(false);
312 }
313 }
314
315 viewport()->update();
316 }
317
resizeEvent(QResizeEvent * event)318 void GraphicsViewRange::resizeEvent(QResizeEvent * event)
319 {
320 _dontRememberScroll = true;
321 QGraphicsView::resizeEvent(event);
322 fitInView(_displayedRect);
323 _dontRememberScroll = false;
324 }
325
mousePressEvent(QMouseEvent * event)326 void GraphicsViewRange::mousePressEvent(QMouseEvent *event)
327 {
328 // Return immediately if a button is already pressed
329 if (_buttonPressed != Qt::NoButton)
330 return;
331
332 if (event->button() == Qt::MiddleButton)
333 {
334 QPointF p = this->mapToScene(event->pos());
335 int key = qRound(p.x());
336 int velocity = 127 - qRound(p.y());
337 if (velocity > 0)
338 {
339 ContextManager::midi()->processKeyOn(key, velocity, true);
340 _keyTriggered = key;
341 }
342 }
343 else if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton)
344 {
345 // Update current position
346 double deltaX = WIDTH - _displayedRect.width();
347 if (deltaX < 0.5)
348 _posX = 0.5;
349 else
350 _posX = (_displayedRect.left() - OFFSET) / deltaX;
351 double deltaY = WIDTH - _displayedRect.height();
352 if (deltaY < 0.5)
353 _posY = 0.5;
354 else
355 _posY = (_displayedRect.top() - OFFSET) / deltaY;
356
357 // Remember situation
358 _xInit = normalizeX(event->x());
359 _yInit = normalizeY(event->y());
360 _zoomXinit = _zoomX;
361 _zoomYinit = _zoomY;
362 _posXinit = _posX;
363 _posYinit = _posY;
364
365 // Rectangles below the mouse?
366 QList<QList<GraphicsRectangleItem*> > pairs = getRectanglesUnderMouse(event->pos());
367 if (event->button() == Qt::LeftButton && !pairs.empty())
368 {
369 _editing = true;
370 GraphicsRectangleItem::syncHover(true);
371
372 // Store the highlighted rectangle for the shift selection
373 _shiftRectangles[0] = _shiftRectangles[1];
374 _shiftRectangles[1] = pairs[0][0]; // By default
375 for (int i = 0; i < pairs.count(); i++)
376 foreach (GraphicsRectangleItem * item, pairs[i])
377 if (item->isHovered())
378 _shiftRectangles[1] = item;
379
380 // Other properties
381 Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
382 bool hasSelection = false;
383 for (int i = 0; i < pairs.count(); i++)
384 foreach (GraphicsRectangleItem * item, pairs[i])
385 hasSelection |= item->isSelected();
386
387 _keepIndexOnRelease = !hasSelection;
388 if (modifiers == Qt::ShiftModifier)
389 {
390 // Select all rectangles between the previous rectangle and the new one
391 if (_shiftRectangles[0] != nullptr && _shiftRectangles[1] != nullptr)
392 {
393 QRectF shiftZone = _shiftRectangles[0]->rect().united(_shiftRectangles[1]->rect());
394 foreach (GraphicsRectangleItem * item, _rectangles)
395 item->setSelected(shiftZone.intersects(item->rect()));
396 }
397 else if (!hasSelection)
398 {
399 // Select the first pair
400 foreach (GraphicsRectangleItem * item, pairs[0])
401 item->setSelected(true);
402 }
403 }
404 else if (modifiers == Qt::ControlModifier)
405 {
406 if (!hasSelection)
407 {
408 // Select the first pair
409 foreach (GraphicsRectangleItem * item, pairs[0])
410 item->setSelected(true);
411 }
412 }
413 else // No modifiers
414 {
415 if (!hasSelection)
416 {
417 // Deselect everything
418 foreach (GraphicsRectangleItem * item, _rectangles)
419 item->setSelected(false);
420
421 // Select the first pair
422 foreach (GraphicsRectangleItem * item, pairs[0])
423 item->setSelected(true);
424 }
425 }
426
427 // Update the selection outside the range editor
428 triggerDivisionSelected();
429 }
430 }
431
432 _moveOccured = false;
433 _buttonPressed = event->button();
434 updateHover(event->pos());
435 viewport()->update();
436 }
437
mouseReleaseEvent(QMouseEvent * event)438 void GraphicsViewRange::mouseReleaseEvent(QMouseEvent *event)
439 {
440 // Only continue if the button released if the one that initiated the move
441 if (_buttonPressed != event->button())
442 return;
443
444 if (event->button() == Qt::MiddleButton)
445 {
446 if (_keyTriggered != -1)
447 {
448 ContextManager::midi()->processKeyOff(_keyTriggered, true);
449 _keyTriggered = -1;
450 }
451 }
452 else if (event->button() == Qt::RightButton)
453 {
454 // Stop zooming
455 this->setZoomLine(-1, 0, 0, 0);
456 }
457 else if (event->button() == Qt::LeftButton)
458 {
459 if (_moveOccured)
460 {
461 // Save the changes
462 bool withChanges = false;
463 foreach (GraphicsRectangleItem * item, _rectangles)
464 if (item->isSelected())
465 withChanges |= item->saveChanges();
466
467 if (withChanges)
468 {
469 _sf2->endEditing("rangeEditor");
470 updateKeyboard();
471 }
472 }
473 else
474 {
475 QList<QList<GraphicsRectangleItem*> > pairs = getRectanglesUnderMouse(event->pos());
476 if (pairs.empty())
477 {
478 if (QApplication::keyboardModifiers() != Qt::ControlModifier)
479 {
480 // Remove the selection
481 foreach (GraphicsRectangleItem * item, _rectangles)
482 item->setSelected(false);
483 }
484 }
485 else
486 {
487 if (QApplication::keyboardModifiers() == Qt::ControlModifier && !_keepIndexOnRelease)
488 {
489 // Something more to select?
490 int indexToSelect = -1;
491 for (int i = 0; i < pairs.count(); i++)
492 {
493 for (int j = 0; j < pairs[i].count(); j++)
494 {
495 if (!pairs[i][j]->isSelected())
496 {
497 pairs[i][j]->setSelected(false);
498 indexToSelect = i;
499 break;
500 }
501 }
502 if (indexToSelect != -1)
503 break;
504 }
505
506 if (indexToSelect != -1)
507 {
508 foreach (GraphicsRectangleItem * item, pairs[indexToSelect])
509 item->setSelected(true);
510 }
511 else
512 {
513 // Deselect everything below the mouse
514 for (int i = 0; i < pairs.count(); i++)
515 foreach (GraphicsRectangleItem * item, pairs[i])
516 item->setSelected(false);
517 }
518 }
519 else if (QApplication::keyboardModifiers() == Qt::NoModifier)
520 {
521 // Current index of the selected pair (maximum index)
522 int index = -1;
523 for (int i = 0; i < pairs.count(); i++)
524 {
525 for (int j = 0; j < pairs[i].count(); j++)
526 {
527 if (pairs[i][j]->isSelected())
528 {
529 index = i;
530 break;
531 }
532 }
533 }
534
535 if (index == -1)
536 {
537 // Deselect everything
538 foreach (GraphicsRectangleItem * item, _rectangles)
539 item->setSelected(false);
540
541 // Select the first pair if possible
542 if (!pairs.empty())
543 for (int j = 0; j < pairs[0].count(); j++)
544 pairs[0][j]->setSelected(true);
545 }
546 else if (!_keepIndexOnRelease)
547 {
548 // Deselect everything
549 foreach (GraphicsRectangleItem * item, _rectangles)
550 item->setSelected(false);
551
552 // Next index is the new selection
553 index = (index + 1) % pairs.count();
554 foreach (GraphicsRectangleItem * item, pairs[index])
555 item->setSelected(true);
556 }
557 }
558 }
559
560 // Update the selection outside the range editor
561 triggerDivisionSelected();
562 }
563 }
564
565 GraphicsRectangleItem::syncHover(false);
566 _legendItem2->setNewValues(-1, 0, 0, 0);
567 _editing = false;
568 _buttonPressed = Qt::NoButton;
569 updateHover(event->pos());
570 viewport()->update();
571 }
572
mouseMoveEvent(QMouseEvent * event)573 void GraphicsViewRange::mouseMoveEvent(QMouseEvent *event)
574 {
575 _moveOccured = true;
576
577 switch (_buttonPressed)
578 {
579 case Qt::LeftButton: {
580 this->setCursor(Qt::ClosedHandCursor);
581 if (_editing)
582 {
583 // Try to move rectangles
584 GraphicsRectangleItem * highlightedRectangle = nullptr;
585 foreach (GraphicsRectangleItem * item, _rectangles)
586 {
587 if (item->isSelected())
588 {
589 QPointF pointInit = this->mapToScene(
590 static_cast<int>(_xInit * this->width()), static_cast<int>(_yInit * this->height()));
591 QPointF pointFinal = this->mapToScene(event->pos());
592 item->computeNewRange(pointInit, pointFinal);
593
594 if (item->isHovered())
595 highlightedRectangle = item;
596 }
597 }
598
599 if (highlightedRectangle != nullptr)
600 {
601 // Update the modification legend
602 _legendItem2->setNewValues(highlightedRectangle->currentMinKey(), highlightedRectangle->currentMaxKey(),
603 highlightedRectangle->currentMinVel(), highlightedRectangle->currentMaxVel());
604 }
605 }
606 else
607 {
608 // Drag
609 this->drag(event->pos());
610 }
611 } break;
612 case Qt::RightButton:
613 this->setCursor(Qt::SizeAllCursor);
614 this->setZoomLine(_xInit, _yInit, normalizeX(event->x()), normalizeY(event->y()));
615 this->zoom(event->pos());
616 break;
617 case Qt::NoButton: default: {
618 // Section of the legend
619 bool isLeft = (event->pos().x() > this->width() / 2);
620 if (_legendItem->isLeft() != isLeft)
621 {
622 _legendItem->setLeft(isLeft);
623 _legendItem2->setLeft(isLeft);
624 updateLabelPosition();
625 }
626
627 // Update legend content
628 updateHover(event->pos());
629 }
630 }
631
632 viewport()->update();
633 }
634
wheelEvent(QWheelEvent * event)635 void GraphicsViewRange::wheelEvent(QWheelEvent * event)
636 {
637 if (_buttonPressed == Qt::NoButton)
638 QGraphicsView::wheelEvent(event);
639 }
640
scrollContentsBy(int dx,int dy)641 void GraphicsViewRange::scrollContentsBy(int dx, int dy)
642 {
643 QGraphicsView::scrollContentsBy(dx, dy);
644 if (_dontRememberScroll)
645 return;
646
647 // Update the displayed rect
648 _displayedRect = getCurrentRect();
649
650 // Limits
651 if (_displayedRect.left() < OFFSET)
652 _displayedRect.setLeft(OFFSET);
653 if (_displayedRect.right() > WIDTH + OFFSET)
654 _displayedRect.setRight(WIDTH + OFFSET);
655 if (_displayedRect.top() < OFFSET)
656 _displayedRect.setTop(OFFSET);
657 if (_displayedRect.bottom() > WIDTH + OFFSET)
658 _displayedRect.setBottom(WIDTH + OFFSET);
659
660 updateLabelPosition();
661 }
662
drag(QPoint point)663 void GraphicsViewRange::drag(QPoint point)
664 {
665 // Décalage
666 double decX = normalizeX(point.x()) - _xInit;
667 double decY = normalizeY(point.y()) - _yInit;
668
669 // Modification posX et posY
670 if (_zoomXinit > 1)
671 _posX = _posXinit - decX / (_zoomXinit - 1);
672 if (_zoomYinit > 1)
673 _posY = _posYinit - decY / (_zoomYinit - 1);
674
675 // Mise à jour
676 this->zoomDrag();
677 }
678
zoom(QPoint point)679 void GraphicsViewRange::zoom(QPoint point)
680 {
681 // Décalage
682 double decX = normalizeX(point.x()) - _xInit;
683 double decY = _yInit - normalizeY(point.y());
684
685 // Modification zoom & drag
686 _zoomX = _zoomXinit * pow(2, 10.0 * decX);
687 _zoomY = _zoomYinit * pow(2, 10.0 * decY);
688
689 // Ajustement posX et posY
690 if (_zoomX > 1)
691 _posX = (_zoomX * _posXinit * (_zoomXinit - 1) +
692 _xInit * (_zoomX - _zoomXinit)) / (_zoomXinit * (_zoomX - 1));
693 if (_zoomY > 1)
694 _posY = (_zoomY * _posYinit * (_zoomYinit - 1) +
695 _yInit * (_zoomY - _zoomYinit)) / (_zoomYinit * (_zoomY - 1));
696
697 // Mise à jour
698 this->zoomDrag();
699 }
700
zoomDrag()701 void GraphicsViewRange::zoomDrag()
702 {
703 // Bornes des paramètres d'affichage
704 if (_zoomX < 1)
705 _zoomX = 1;
706 else if (_zoomX > 8)
707 _zoomX = 8;
708 if (_zoomY < 1)
709 _zoomY = 1;
710 else if (_zoomY > 8)
711 _zoomY = 8;
712 if (_posX < 0)
713 _posX = 0;
714 else if (_posX > 1)
715 _posX = 1;
716 if (_posY < 0)
717 _posY = 0;
718 else if (_posY > 1)
719 _posY = 1;
720
721 // Application du drag et zoom
722 double etendueX = WIDTH / _zoomX;
723 double offsetX = (WIDTH - etendueX) * _posX + OFFSET;
724 double etendueY = WIDTH / _zoomY;
725 double offsetY = (WIDTH - etendueY) * _posY + OFFSET;
726 _displayedRect.setRect(offsetX, offsetY, etendueX, etendueY);
727
728 // Mise à jour
729 _dontRememberScroll = true;
730 this->fitInView(_displayedRect);
731 _dontRememberScroll = false;
732 updateLabelPosition();
733 }
734
setZoomLine(double x1Norm,double y1Norm,double x2Norm,double y2Norm)735 void GraphicsViewRange::setZoomLine(double x1Norm, double y1Norm, double x2Norm, double y2Norm)
736 {
737 if (x1Norm < 0)
738 _zoomLine->setSize(0, 0);
739 else
740 {
741 QRectF rect = getCurrentRect();
742
743 // Initial position
744 _zoomLine->setPos(rect.left() + x1Norm * rect.width(),
745 rect.top() + y1Norm * rect.height());
746
747 // Size
748 _zoomLine->setSize((x2Norm - x1Norm) * this->width(),
749 (y2Norm - y1Norm) * this->height());
750 }
751 }
752
getRectanglesUnderMouse(QPoint mousePos)753 QList<QList<GraphicsRectangleItem*> > GraphicsViewRange::getRectanglesUnderMouse(QPoint mousePos)
754 {
755 // Rectangles under the mouse
756 QList<GraphicsRectangleItem *> rectanglesUnderMouse;
757 foreach (GraphicsRectangleItem * item, _rectangles)
758 if (item->contains(this->mapToScene(mousePos)))
759 rectanglesUnderMouse << item;
760
761 // Sort them by pairs
762 QList<QList<GraphicsRectangleItem*> > pairs;
763 while (!rectanglesUnderMouse.isEmpty())
764 {
765 QList<GraphicsRectangleItem*> listTmp;
766 listTmp << rectanglesUnderMouse.takeFirst();
767 EltID idBrother = listTmp[0]->findBrother();
768 foreach (GraphicsRectangleItem* item, rectanglesUnderMouse)
769 {
770 if (*item == idBrother)
771 {
772 listTmp << item;
773 rectanglesUnderMouse.removeOne(item);
774 break;
775 }
776 }
777 pairs << listTmp;
778 }
779
780 return pairs;
781 }
782
normalizeX(int xPixel)783 double GraphicsViewRange::normalizeX(int xPixel)
784 {
785 return static_cast<double>(xPixel) / this->width();
786 }
787
normalizeY(int yPixel)788 double GraphicsViewRange::normalizeY(int yPixel)
789 {
790 return static_cast<double>(yPixel) / this->height();
791 }
792
getCurrentRect()793 QRectF GraphicsViewRange::getCurrentRect()
794 {
795 QPointF tl(horizontalScrollBar()->value(), verticalScrollBar()->value());
796 QPointF br = tl + viewport()->rect().bottomRight();
797 QMatrix mat = matrix().inverted();
798 return mat.mapRect(QRectF(tl,br));
799 }
800
playKey(int key,int velocity)801 void GraphicsViewRange::playKey(int key, int velocity)
802 {
803 if (velocity == 0 && _mapGraphicsKeys[key] != nullptr)
804 {
805 // A key is removed
806 delete _mapGraphicsKeys.take(key);
807 }
808 else if (velocity > 0 && _mapGraphicsKeys[key] == nullptr)
809 {
810 // A key is added
811 _mapGraphicsKeys[key] = new GraphicsKey();
812 _scene->addItem(_mapGraphicsKeys[key]);
813 _mapGraphicsKeys[key]->setPos(QPoint(key, 127 - velocity));
814 _mapGraphicsKeys[key]->setZValue(80);
815 }
816 }
817
triggerDivisionSelected()818 void GraphicsViewRange::triggerDivisionSelected()
819 {
820 IdList ids;
821 foreach (GraphicsRectangleItem * item, _rectangles)
822 if (item->isSelected())
823 ids << item->getID();
824 if (ids.empty())
825 ids << _defaultID;
826 divisionsSelected(ids);
827 updateKeyboard();
828 }
829