1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18 #define RG_MODULE_STRING "[ControlRuler]"
19
20 #include "ControlRuler.h"
21
22 #include "base/Event.h"
23 #include "misc/Debug.h"
24 #include "base/RulerScale.h"
25 #include "document/Command.h"
26 #include "commands/notation/NormalizeRestsCommand.h"
27 #include "gui/editors/notation/NotationStaff.h"
28 #include "ControlMouseEvent.h"
29 #include "ControlItem.h"
30 #include "ControlSelector.h"
31 #include "ControlTool.h"
32 #include "ControlToolBox.h"
33 #include "ControlChangeCommand.h"
34 #include "DefaultVelocityColour.h"
35 #include "document/CommandHistory.h"
36 #include "base/ViewSegment.h"
37
38 #include <algorithm>
39 #include <cfloat>
40
41 #include <QMainWindow>
42 #include <QColor>
43 #include <QPoint>
44 #include <QPolygonF>
45 #include <QPolygon>
46 #include <QMenu>
47 #include <QString>
48 #include <QWidget>
49 #include <QMouseEvent>
50 #include <QContextMenuEvent>
51 #include <QPainter>
52 #include <QBrush>
53 #include <QPen>
54
55 namespace Rosegarden
56 {
57
ControlRuler(ViewSegment *,RulerScale * rulerScale,QWidget * parent)58 ControlRuler::ControlRuler(ViewSegment * /*viewsegment*/,
59 RulerScale* rulerScale,
60 QWidget* parent) :
61 QWidget(parent),
62 m_rulerScale(rulerScale),
63 m_eventSelection(nullptr),
64 m_viewSegment(nullptr),
65 m_notationStaff(nullptr),
66 m_segment(nullptr),
67 m_controlItemMap(),
68 m_firstVisibleItem(m_controlItemMap.end()),
69 m_lastVisibleItem(m_controlItemMap.end()),
70 m_nextItemLeft(m_controlItemMap.end()),
71 m_selectedItems(),
72 m_visibleItems(),
73 m_currentIndex(nullptr),
74 m_currentTool(nullptr),
75 m_currentToolName(),
76 m_pannedRect(),
77 m_xScale(1.0),
78 m_yScale(1.0),
79 m_maxItemValue(127),
80 m_minItemValue(0),
81 m_viewSegmentOffset(0),
82 m_xOffset(0),
83 m_currentX(0.0),
84 m_lastEventPos(),
85 m_itemMoved(false),
86 m_selecting(false),
87 m_selector(nullptr),
88 m_selectionRect(nullptr),
89 m_menuName(),
90 m_menu(nullptr),
91 //m_hposUpdatePending(false),
92 m_selectedEvents()
93 {
94
95 setFixedHeight(sizeHint().height());
96 setMouseTracking(true);
97
98 m_toolBox = new ControlToolBox(this);
99 connect(m_toolBox, &BaseToolBox::showContextHelp,
100 this, &ControlRuler::showContextHelp);
101 }
102
~ControlRuler()103 ControlRuler::~ControlRuler()
104 {
105 delete m_eventSelection;
106 m_eventSelection = nullptr;
107 }
108
setSegment(Segment * segment)109 void ControlRuler::setSegment(Segment *segment)
110 {
111 m_segment = segment;
112
113 if (m_eventSelection) delete m_eventSelection;
114
115 m_eventSelection = new EventSelection(*segment);
116 }
117
setViewSegment(ViewSegment * viewSegment)118 void ControlRuler::setViewSegment(ViewSegment *viewSegment)
119 {
120 m_viewSegment = viewSegment;
121
122 // If this ruler is connected to a NotationStaff then this will return a valid pointer
123 // otherwise, it will return zero. This can be used to check whether we're connected
124 // to a matrix or notation editor later
125 m_notationStaff = dynamic_cast <NotationStaff *> (viewSegment);
126
127 setSegment(&m_viewSegment->getSegment());
128 }
129
findControlItem(float x)130 ControlItemMap::iterator ControlRuler::findControlItem(float x)
131 {
132 ControlItemMap::iterator it;
133 it = m_controlItemMap.upper_bound(x);
134 return it;
135 }
136
findControlItem(const Event * event)137 ControlItemMap::iterator ControlRuler::findControlItem(const Event *event)
138 {
139 // double xstart = getRulerScale()->getXForTime(event->getAbsoluteTime());
140
141 ControlItemMap::iterator it;
142 std::pair <ControlItemMap::iterator,ControlItemMap::iterator> ret;
143
144 for (it = m_controlItemMap.begin(); it != m_controlItemMap.end(); ++it) {
145 if (it->second->getEvent() == event) break;
146 }
147
148 ///@TODO equal_range (above) is not behaving as expected - sort it out
149 // if (it != m_controlItemMap.end() && it->second->getEvent() != event) {
150 // it = m_controlItemMap.end();
151 // }
152
153 return it;
154 }
155
findControlItem(const ControlItem * item)156 ControlItemMap::iterator ControlRuler::findControlItem(const ControlItem* item)
157 {
158 // Basic loop through until I get the equal_range thing working properly
159 ControlItemMap::iterator it;
160 for (it = m_controlItemMap.begin(); it != m_controlItemMap.end(); ++it) {
161 if (it->second == item) break;
162 }
163 return it;
164 }
165
addControlItem(QSharedPointer<ControlItem> item)166 void ControlRuler::addControlItem(QSharedPointer<ControlItem> item)
167 {
168 // Add a ControlItem to the ruler
169
170 //RG_DEBUG << "addControlItem(): ControlItem added: " << hex << (long)item;
171
172 // ControlItem may not have an assigned event but must have x position
173 ControlItemMap::iterator it =
174 m_controlItemMap.insert(
175 ControlItemMap::value_type(item->xStart(), item));
176
177 addCheckVisibleLimits(it);
178
179 if (it->second->isSelected())
180 m_selectedItems.push_back(it->second);
181
182 }
183
addCheckVisibleLimits(ControlItemMap::iterator it)184 void ControlRuler::addCheckVisibleLimits(ControlItemMap::iterator it)
185 {
186 // Referenced item is has just been added to m_controlItemMap
187 // If it is visible, add it to the list and correct first/last
188 // visible item iterators
189 QSharedPointer<ControlItem> item = it->second;
190
191 // If this new item is visible
192 if (visiblePosition(item)==0) {
193 // put it in the visible list
194 m_visibleItems.push_back(item);
195 // If there is no first visible item or this one is further left
196 if (m_firstVisibleItem == m_controlItemMap.end() ||
197 item->xStart() < m_firstVisibleItem->second->xStart()) {
198 // make it the first visible item
199 m_firstVisibleItem = it;
200 }
201
202 // If there is no last visible item or this is further right
203 if (m_lastVisibleItem == m_controlItemMap.end() ||
204 item->xStart() > m_lastVisibleItem->second->xStart()) {
205 // make it the last visible item
206 m_lastVisibleItem = it;
207 }
208 }
209
210 // If the new item is invisible to the left
211 if (visiblePosition(item) == -1) {
212 if (m_nextItemLeft == m_controlItemMap.end() ||
213 item->xStart() > m_nextItemLeft->second->xStart()) {
214 // make it the next item to the left
215 m_nextItemLeft = it;
216 }
217 }
218 }
219
removeControlItem(ControlItem * item)220 void ControlRuler::removeControlItem(ControlItem* item)
221 {
222 // Remove control item by item pointer
223 // No search by Value provided for std::multimap so find items with the requested item's
224 // xstart position and sweep these for the correct entry
225 ControlItemMap::iterator it = findControlItem(item);
226
227 if (it != m_controlItemMap.end()) removeControlItem(it);
228 }
229
removeControlItem(const Event * event)230 void ControlRuler::removeControlItem(const Event *event)
231 {
232 // Remove the ControlItem matching the received event if one exists
233 ControlItemMap::iterator it = findControlItem(event);
234
235 if (it != m_controlItemMap.end()) {
236 RG_DEBUG << "removeControlItem(): at x = " << it->first;
237 removeControlItem(it);
238 }
239 }
240
removeControlItem(const ControlItemMap::iterator & it)241 void ControlRuler::removeControlItem(const ControlItemMap::iterator &it)
242 {
243 //RG_DEBUG << "removeControlItem(): iterator->item: " << hex << (long) it->second;
244 //RG_DEBUG << " m_selectedItems.front(): " << hex << (long) m_selectedItems.front();
245
246 if (it->second->isSelected()) m_selectedItems.remove(it->second);
247 removeCheckVisibleLimits(it);
248 m_controlItemMap.erase(it);
249 }
250
removeCheckVisibleLimits(const ControlItemMap::iterator & it)251 void ControlRuler::removeCheckVisibleLimits(const ControlItemMap::iterator &it)
252 {
253 // Referenced item is being removed from m_controlItemMap
254 // If it was visible, remove it from the list and correct first/last
255 // visible item iterators
256 // Note, we can't check if it _was_ visible. It may have just become invisible
257 // Try to remove from list and check iterators.
258 m_visibleItems.remove(it->second);
259
260 // If necessary, correct the first and lastVisibleItem iterators
261 // If this was the first visible item
262 if (it == m_firstVisibleItem) {
263 // Check the next item to the right
264 ++m_firstVisibleItem;
265 // If the next item to the right is invisible, there are no visible items
266 // Note we have to check .end() before we dereference ->second
267 if (m_firstVisibleItem != m_controlItemMap.end() &&
268 visiblePosition(m_firstVisibleItem->second)!=0)
269 m_firstVisibleItem = m_controlItemMap.end();
270 }
271
272 // If this was the last visible item
273 if (it == m_lastVisibleItem) {
274 // and not the first in the list
275 if (it != m_controlItemMap.begin()) {
276 // check the next item to the left
277 --m_lastVisibleItem;
278 // If this is invisible, there are no visible items
279 if (visiblePosition(m_lastVisibleItem->second)!=0) m_lastVisibleItem = m_controlItemMap.end();
280 }
281 // if it's first in the list then there are no visible items
282 else m_lastVisibleItem = m_controlItemMap.end();
283 }
284
285 // If this was the first invisible item left (could be part of a selection moved off screen)
286 if (it == m_nextItemLeft) {
287 // and not the first in the list
288 if (it != m_controlItemMap.begin()) {
289 // use the next to the left (we know it is invisible)
290 --m_nextItemLeft;
291 }
292 // if it's first in the list then there are no invisible items to the left
293 else m_nextItemLeft = m_controlItemMap.end();
294 }
295 }
296
eraseControlItem(const Event * event)297 void ControlRuler::eraseControlItem(const Event *event)
298 {
299 ControlItemMap::iterator it = findControlItem(event);
300 if (it != m_controlItemMap.end()) eraseControlItem(it);
301 }
302
eraseControlItem(const ControlItemMap::iterator & it)303 void ControlRuler::eraseControlItem(const ControlItemMap::iterator &it)
304 {
305 removeControlItem(it);
306 }
307
moveItem(ControlItem * item)308 void ControlRuler::moveItem(ControlItem *item)
309 {
310 // ??? This is pretty ugly. A naked pointer being used to find
311 // a shared pointer. It's as if the three callers of this
312 // function also need to pass in the shared pointer. That
313 // would look a bit funny:
314 //
315 // item->reconfigure(item);
316
317 // Move the item within m_controlItemMap
318 // Need to check changes in visibility
319 // DO NOT change isSelected or m_selectedItems as this is used to loop this
320 ControlItemMap::iterator it = findControlItem(item);
321 // Not found? Bail.
322 if (it == m_controlItemMap.end())
323 return;
324
325 // Copy the shared pointer so that the item isn't deleted.
326 QSharedPointer<ControlItem> item2 = it->second;
327
328 removeCheckVisibleLimits(it);
329 m_controlItemMap.erase(it);
330 it = m_controlItemMap.insert(
331 ControlItemMap::value_type(item2->xStart(), item2));
332 addCheckVisibleLimits(it);
333 }
334
visiblePosition(QSharedPointer<ControlItem> item)335 int ControlRuler::visiblePosition(QSharedPointer<ControlItem> item)
336 {
337 // Check visibility of an item
338 // Returns: -1 - item is off screen left
339 // 0 - item is visible
340 // +1 - item is off screen right
341 if (item->xEnd() < m_pannedRect.left()) return -1;
342 if (item->xStart() > m_pannedRect.right()) return 1;
343
344 return 0;
345 }
346
getXMax()347 float ControlRuler::getXMax()
348 {
349 return (m_rulerScale->getXForTime(m_segment->getEndTime()));
350 }
351
getXMin()352 float ControlRuler::getXMin()
353 {
354 return (m_rulerScale->getXForTime(m_segment->getStartTime()));
355 }
356
updateSegment()357 void ControlRuler::updateSegment()
358 {
359 // Bring the segment up to date with the ControlRuler's items
360 // A number of different actions take place here:
361 // 1) m_eventSelection is empty
362 // 2) m_eventSelection has events
363 // a) Events in the selection have been modified in value only
364 // b) Events in the selection have moved in time
365 //
366 // Either run through the ruler's EventSelection, updating from each item
367 // or, if there isn't one, go through m_selectedItems
368 timeT start,end;
369
370 QString commandLabel = "Adjust control/property";
371
372 // ??? MEMORY LEAK (confirmed) Probably due to return from middle.
373 MacroCommand *macro = new MacroCommand(commandLabel);
374
375 // Find the extent of the selected items
376 float xmin=FLT_MAX,xmax=-1.0;
377
378 // EventSelection::addEvent adds timeT(1) to its extentt for zero duration events so need to mimic this here
379 timeT durationAdd = 0;
380
381 for (ControlItemList::iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
382 if ((*it)->xStart() < xmin) xmin = (*it)->xStart();
383 if ((*it)->xEnd() > xmax) {
384 xmax = (*it)->xEnd();
385 if ((*it)->xEnd() == (*it)->xStart())
386 durationAdd = 1;
387 else
388 durationAdd = 0;
389 }
390 }
391
392 start = getRulerScale()->getTimeForX(xmin);
393 end = getRulerScale()->getTimeForX(xmax)+durationAdd;
394
395 if (m_eventSelection->getAddedEvents() == 0) {
396 // We do not have a valid set of selected events to update
397 if (m_selectedItems.size() == 0) {
398 // There are no selected items, nothing to update
399 return;
400 }
401
402 // Events will be added by the controlItem->updateSegment methods
403 commandLabel = "Add control";
404 macro->setName(commandLabel);
405
406 } else {
407 // Check for movement in time here and delete events if necessary
408 if (start != m_eventSelection->getStartTime() || end != m_eventSelection->getEndTime()) {
409 commandLabel = "Move control";
410 macro->setName(commandLabel);
411
412 // Get the limits of the change for undo
413 start = std::min(start,m_eventSelection->getStartTime());
414 end = std::max(end,m_eventSelection->getEndTime());
415
416 }
417 }
418
419 // Add change command to macro
420 // ControlChangeCommand calls each selected items updateSegment method
421 // Note that updateSegment deletes and renews the event whether it has moved or not
422 macro->addCommand(new ControlChangeCommand(m_selectedItems,
423 *m_segment,
424 start,
425 end));
426
427 CommandHistory::getInstance()->addCommand(macro);
428
429 updateSelection();
430 }
431
slotUpdate()432 void ControlRuler::slotUpdate()
433 {
434 //RG_DEBUG << "slotUpdate()";
435
436 ///TODO Set some update flag?
437 }
438
notationLayoutUpdated(timeT startTime,timeT)439 void ControlRuler::notationLayoutUpdated(timeT startTime, timeT /*endTime*/)
440 {
441 // notationLayoutUpdated should be called after notation has adjusted the layout
442 // Clearly, for property control rulers, notes may have been moved so their position needs updating
443 // The rulers may also have changed so ControllerEventRulers also need updating
444 // Property control items may now need to be repositioned within the ControlItemMap
445 // as new items are all created with a zero x-position, and have now been put in place.
446 // For this reason, we need to collect items into a separate list otherwise we get the
447 // dreaded 'modifying a list within a loop of the list' problem which can take quite a long
448 // time to fix!
449 ControlItemVector itemsToUpdate;
450 ControlItemMap::iterator it = m_controlItemMap.begin();
451 while (it != m_controlItemMap.end() && it->first == 0) {
452 itemsToUpdate.push_back(it->second);
453 ++it;
454 }
455
456 while (it != m_controlItemMap.end() && it->first < getRulerScale()->getXForTime(startTime)) ++it;
457
458 // Would like to only update in the defined region but, unfortunately, everything after this time
459 // may well have moved as well so we have to do everything after startTime
460 while (it != m_controlItemMap.end()) {
461 itemsToUpdate.push_back(it->second);
462 ++it;
463 }
464
465 for (ControlItemVector::iterator vit = itemsToUpdate.begin();
466 vit != itemsToUpdate.end();
467 ++vit) {
468 (*vit)->update();
469 //RG_DEBUG << "notationLayoutUpdated(): Updated item: " << hex << (long)(*vit);
470 }
471
472 update();
473 }
474
paintEvent(QPaintEvent *)475 void ControlRuler::paintEvent(QPaintEvent * /*event*/)
476 {
477 //RG_DEBUG << "paintEvent(): width()=" << width() << " height()=" << height();
478
479 QPainter painter(this);
480
481 QPen pen;
482 QBrush brush;
483
484 pen.setStyle(Qt::NoPen);
485 painter.setPen(pen);
486
487 brush.setStyle(Qt::SolidPattern);
488 brush.setColor(Qt::white);
489 painter.setBrush(brush);
490
491 painter.drawRect(0,0,width(),height());
492
493 double xstart = m_rulerScale->getXForTime(m_segment->getStartTime());
494 double xend = m_rulerScale->getXForTime(m_segment->getEndTime());
495
496 xstart = mapXToWidget(xstart*m_xScale);
497 xend = mapXToWidget(xend*m_xScale);
498
499 //RG_DEBUG << "paintEvent(): xstart=" << xstart;
500
501 painter.setPen(QColor(127, 127, 127));
502 painter.drawLine(xstart, mapYToWidget(0.0f), xend, mapYToWidget(0.0f));
503 painter.drawLine(xstart, mapYToWidget(0.5f), xend, mapYToWidget(0.5f));
504 painter.drawLine(xstart, mapYToWidget(1.0f), xend, mapYToWidget(1.0f));
505
506 painter.setPen(QColor(192, 192, 192));
507 painter.drawLine(xstart, mapYToWidget(0.25f), xend, mapYToWidget(0.25f));
508 painter.drawLine(xstart, mapYToWidget(0.75f), xend, mapYToWidget(0.75f));
509 }
510
slotScrollHorizSmallSteps(int)511 void ControlRuler::slotScrollHorizSmallSteps(int /*step*/)
512 {
513 }
514
mapXToWidget(float x)515 int ControlRuler::mapXToWidget(float x)
516 {
517 return (0.5+(m_xOffset+x-m_pannedRect.left()) / m_xScale);
518 }
519
mapYToWidget(float y)520 int ControlRuler::mapYToWidget(float y)
521 {
522 return (0.5+(-y+1.0f) / m_yScale);
523 }
524
mapRectToWidget(QRectF * rect)525 QRect ControlRuler::mapRectToWidget(QRectF *rect)
526 {
527 QRect newrect;
528
529 newrect.setTopLeft(QPoint(mapXToWidget(rect->left()),mapYToWidget(rect->top())));
530 newrect.setBottomRight(QPoint(mapXToWidget(rect->right()),mapYToWidget(rect->bottom())));
531
532 return newrect;
533 }
534
mapItemToWidget(QSharedPointer<ControlItem> poly)535 QPolygon ControlRuler::mapItemToWidget(QSharedPointer<ControlItem> poly)
536 {
537
538 QPolygon newpoly;
539 QPoint newpoint;
540 for (QPolygonF::iterator it = poly->begin(); it != poly->end(); ++it) {
541 newpoint.setX(mapXToWidget((*it).x()));
542 newpoint.setY(mapYToWidget((*it).y()));
543 newpoly.push_back(newpoint);
544 }
545
546 return newpoly;
547 }
548
mapWidgetToItem(QPoint * point)549 QPointF ControlRuler::mapWidgetToItem(QPoint *point)
550 {
551
552 QPointF newpoint;
553 newpoint.setX(m_xScale*(point->x()) + m_pannedRect.left() - m_xOffset);
554 newpoint.setY(-m_yScale*(point->y()) + 1.0f);
555 return newpoint;
556 }
557
slotSetPannedRect(QRectF pr)558 void ControlRuler::slotSetPannedRect(QRectF pr)
559 {
560 m_pannedRect = pr;
561 m_xScale = (double) m_pannedRect.width() / (double) width();
562 m_yScale = 1.0f / (double) height();
563
564 // Create the visible items list
565 ///TODO Improve efficiency using xstart and xstop ordered lists of control items
566 m_visibleItems.clear();
567 bool anyVisibleYet = false;
568
569 m_nextItemLeft = m_controlItemMap.end();
570 m_firstVisibleItem = m_controlItemMap.end();
571 m_lastVisibleItem = m_controlItemMap.end();
572
573 ControlItemMap::iterator it;
574 for (it = m_controlItemMap.begin();it != m_controlItemMap.end(); ++it) {
575 int visPos = visiblePosition(it->second);
576
577 if (visPos == -1) m_nextItemLeft = it;
578
579 if (visPos == 0) {
580 if (!anyVisibleYet) {
581 m_firstVisibleItem = it;
582 anyVisibleYet = true;
583 }
584
585 m_visibleItems.push_back(it->second);
586 m_lastVisibleItem = it;
587 }
588
589 if (visPos == 1) break;
590 }
591
592 //RG_DEBUG << "slotSetPannedRect() - visible items: " << m_visibleItems.size();
593 }
594
resizeEvent(QResizeEvent *)595 void ControlRuler::resizeEvent(QResizeEvent *)
596 {
597 // Note slotSetPannedRect is called (from ControlRulerWidget::slotSetPannedRect)
598 // on a resize event. However, this call is too early and width() has not been
599 // updated. This event handler patches that problem. Could be more efficient.
600 slotSetPannedRect(m_pannedRect);
601 }
602
setTool(const QString &)603 void ControlRuler::setTool(const QString & /*name*/)
604 {
605 }
606
createControlMouseEvent(QMouseEvent * e)607 ControlMouseEvent ControlRuler::createControlMouseEvent(QMouseEvent* e)
608 {
609 ControlMouseEvent controlMouseEvent;
610 QPoint widgetMousePos = e->pos();
611 QPointF mousePos = mapWidgetToItem(&widgetMousePos);
612 controlMouseEvent.x = mousePos.x();
613 controlMouseEvent.y = mousePos.y();
614
615 for (ControlItemList::iterator it = m_visibleItems.begin();
616 it != m_visibleItems.end(); ++it) {
617 if ((*it)->containsPoint(mousePos,Qt::OddEvenFill)) {
618 controlMouseEvent.itemList.push_back(*it);
619 }
620 }
621
622 controlMouseEvent.buttons = e->buttons();
623 controlMouseEvent.modifiers = e->modifiers();
624
625 return controlMouseEvent;
626 }
627
mousePressEvent(QMouseEvent * e)628 void ControlRuler::mousePressEvent(QMouseEvent* e)
629 {
630 if (!m_currentTool)
631 return;
632
633 if (e->button() == Qt::LeftButton) {
634 ControlMouseEvent controlMouseEvent = createControlMouseEvent(e);
635 m_currentTool->handleLeftButtonPress(&controlMouseEvent);
636 }
637
638 emit mousePress();
639 }
640
mouseReleaseEvent(QMouseEvent * e)641 void ControlRuler::mouseReleaseEvent(QMouseEvent* e)
642 {
643 if (!m_currentTool)
644 return;
645
646 if (e->button() == Qt::LeftButton) {
647 ControlMouseEvent controlMouseEvent = createControlMouseEvent(e);
648 m_currentTool->handleMouseRelease(&controlMouseEvent);
649 }
650
651 emit mouseRelease();
652 }
653
mouseMoveEvent(QMouseEvent * e)654 void ControlRuler::mouseMoveEvent(QMouseEvent* e)
655 {
656 if (!m_currentTool)
657 return;
658
659 ControlMouseEvent controlMouseEvent = createControlMouseEvent(e);
660 FollowMode mode = m_currentTool->handleMouseMove(&controlMouseEvent);
661
662 emit mouseMove(mode);
663 }
664
665 void
wheelEvent(QWheelEvent *)666 ControlRuler::wheelEvent(QWheelEvent * /*e*/)
667 {
668 // not sure what to do yet
669
670 }
671
contextMenuEvent(QContextMenuEvent * e)672 void ControlRuler::contextMenuEvent(QContextMenuEvent* e)
673 {
674 if (!m_menu && !m_menuName.isEmpty())
675 createMenu();
676
677 if (m_menu) {
678 // RG_DEBUG << "ControlRuler::showMenu() - show menu with" << m_menu->count() << " items";
679 m_lastEventPos = e->pos(); ///CJ OK ??? - inverseMapPoint(e->pos());
680 m_menu->exec(QCursor::pos());
681 } else {
682 RG_WARNING << "contextMenuEvent(): no menu to show";
683 }
684 }
685
createMenu()686 void ControlRuler::createMenu()
687 {
688 RG_DEBUG << "createMenu()";
689
690 QMainWindow* parentMainWindow = dynamic_cast<QMainWindow*>(topLevelWidget());
691
692 if (parentMainWindow ) { // parentMainWindow->factory()) {
693 m_menu = parentMainWindow->findChild<QMenu*>(m_menuName);
694
695 if (!m_menu) {
696 RG_WARNING << "createMenu() failed";
697 }
698 } else {
699 RG_WARNING << "createMenu() failed: no parent factory";
700 }
701 }
702
703 void
clearSelectedItems()704 ControlRuler::clearSelectedItems()
705 {
706 for (ControlItemList::iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
707 (*it)->setSelected(false);
708 }
709 m_selectedItems.clear();
710
711 if (m_eventSelection) delete m_eventSelection;
712
713 m_eventSelection = new EventSelection(*m_segment);
714 }
715
updateSelection()716 void ControlRuler::updateSelection()
717 {
718 if (m_eventSelection) delete m_eventSelection;
719 m_eventSelection = new EventSelection(*m_segment);
720
721 for (ControlItemList::iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
722 m_eventSelection->addEvent((*it)->getEvent());
723 }
724
725 RG_DEBUG << "updateSelection(): emitting rulerSelectionChanged()";
726
727 emit rulerSelectionChanged(m_eventSelection);
728 }
729
addToSelection(QSharedPointer<ControlItem> item)730 void ControlRuler::addToSelection(QSharedPointer<ControlItem> item)
731 {
732 ControlItemList::iterator found =
733 std::find (m_selectedItems.begin(),
734 m_selectedItems.end(), item);
735
736 // If we already have this item, do nothing.
737 if (found != m_selectedItems.end()) { return; }
738 m_selectedItems.push_back(item);
739 item->setSelected(true);
740 m_eventSelection->addEvent(item->getEvent());
741 emit rulerSelectionChanged(m_eventSelection);
742
743 RG_DEBUG << "addToSelection() done";
744 }
745
removeFromSelection(QSharedPointer<ControlItem> item)746 void ControlRuler::removeFromSelection(QSharedPointer<ControlItem> item)
747 {
748 m_selectedItems.remove(item);
749 item->setSelected(false);
750 m_eventSelection->removeEvent(item->getEvent());
751 emit rulerSelectionChanged(m_eventSelection);
752 }
753
clear()754 void ControlRuler::clear()
755 {
756 RG_DEBUG << "clear() - m_controlItemMap.size(): " << m_controlItemMap.size();
757
758 m_controlItemMap.clear();
759 m_firstVisibleItem = m_controlItemMap.end();
760 m_lastVisibleItem = m_controlItemMap.end();
761 m_nextItemLeft = m_controlItemMap.end();
762
763 m_visibleItems.clear();
764 m_selectedItems.clear();
765 }
766
valueToY(long val)767 float ControlRuler::valueToY(long val)
768 {
769 float y = (float)(val-getMinItemValue())
770 /(float)(getMaxItemValue()-getMinItemValue());
771 return y;
772 }
773
yToValue(float y)774 long ControlRuler::yToValue(float y)
775 {
776 // NOTE: while debugging #1451 I had debug output here and it confirmed that
777 // value is returning very reasonable numbers, which get mangled elsewhere
778 long value = (long)(y*(getMaxItemValue()-getMinItemValue()))+getMinItemValue();
779 return value;
780 }
781
valueToColour(int max,int val)782 QColor ControlRuler::valueToColour(int max, int val)
783 {
784 int maxDefault = DefaultVelocityColour::getInstance()->getMaxValue();
785
786 int value = val;
787
788 // Scale value accordingly
789 //
790 if (maxDefault != max)
791 value = int(double(maxDefault) * double(val) / double(max));
792
793 return DefaultVelocityColour::getInstance()->getColour(value);
794 }
795
flipForwards()796 void ControlRuler::flipForwards()
797 {
798 ///CJ Expect to drop tghis with a better way of ordering bars
799 // std::pair<int, int> minMax = getZMinMax();
800
801 }
802
flipBackwards()803 void ControlRuler::flipBackwards()
804 {
805 ///CJ Expect to drop tghis with a better way of ordering bars
806 // std::pair<int, int> minMax = getZMinMax();
807
808 }
809
getZMinMax()810 std::pair<int, int> ControlRuler::getZMinMax()
811 {
812 std::vector<int> zList;
813
814 std::sort(zList.begin(), zList.end());
815
816 return std::pair<int, int>(zList[0], zList[zList.size() - 1]);
817 }
818
819
820 }
821