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