1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //    $Id: dcanvas.cpp,v 1.16.2.10 2009/10/15 22:45:50 terminator356 Exp $
5 //  (C) Copyright 1999 Werner Schweer (ws@seh.de)
6 //  (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include <QPainter>
25 #include <QApplication>
26 #include <QMessageBox>
27 #include <QClipboard>
28 #include <QDrag>
29 #include <QDragLeaveEvent>
30 #include <QPolygon>
31 #include <QDragEnterEvent>
32 #include <QDragMoveEvent>
33 #include <QDropEvent>
34 #include <QResizeEvent>
35 #include <QList>
36 #include <QPair>
37 #include <QToolTip>
38 
39 #include <stdio.h>
40 #include <limits.h>
41 #include <errno.h>
42 
43 #include "dcanvas.h"
44 #include "midieditor.h"
45 #include "drumedit.h"
46 #include "drummap.h"
47 #include "drum_ordering.h"
48 #include "event.h"
49 #include "mpevent.h"
50 #include "xml.h"
51 #include "globals.h"
52 #include "midiport.h"
53 #include "audio.h"
54 #include "midi_consts.h"
55 #include "shortcuts.h"
56 #include "icons.h"
57 #include "functions.h"
58 #include "helper.h"
59 #include "operations.h"
60 #include "gconfig.h"
61 #include "app.h"
62 
63 #define CARET   10
64 #define CARET2   5
65 
66 using MusEGlobal::debugMsg;
67 using MusEGlobal::heavyDebugMsg;
68 using MusECore::Track;
69 using MusECore::MidiTrack;
70 
71 namespace MusEGui {
72 
73 //---------------------------------------------------------
74 //   DEvent
75 //---------------------------------------------------------
76 
DEvent(MusECore::Event e,MusECore::Part * p,int instr)77 DEvent::DEvent(MusECore::Event e, MusECore::Part* p, int instr)
78   : EItem(e, p)
79       {
80       int y  = instr * TH + TH/2;
81       int tick = e.tick() + p->tick();
82       setPos(QPoint(tick, y));
83       setBBox(QRect(-CARET2, -CARET2, CARET, CARET));
84       // Give the moving point an initial value.
85       setMp(pos());
86       }
87 
88 //---------------------------------------------------------
89 //   addItem
90 //---------------------------------------------------------
91 
addItem(MusECore::Part * part,const MusECore::Event & event)92 CItem* DrumCanvas::addItem(MusECore::Part* part, const MusECore::Event& event)
93       {
94       if (signed(event.tick())<0) {
95             printf("ERROR: trying to add event before current part!\n");
96             return nullptr;
97       }
98 
99       int instr=pitch_and_track_to_instrument(event.pitch(), part->track());
100       if (instr<0)
101       {
102         if (heavyDebugMsg) printf("trying to add event which is hidden or not in any part known to me\n");
103         return nullptr;
104       }
105 
106       DEvent* ev = new DEvent(event, part, instr);
107       items.add(ev);
108 
109       return ev;
110       }
111 
112 //---------------------------------------------------------
113 //   DrumCanvas
114 //---------------------------------------------------------
115 
DrumCanvas(MidiEditor * pr,QWidget * parent,int sx,int sy,const char * name)116 DrumCanvas::DrumCanvas(MidiEditor* pr, QWidget* parent, int sx,
117    int sy, const char* name)
118    : EventCanvas(pr, parent, sx, sy, name)
119       {
120       setObjectName("DrumCanvas");
121 
122       drumEditor=static_cast<DrumEdit*>(pr);
123 
124       setStatusTip(tr("Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help."));
125 
126       ourDrumMap=nullptr;
127       rebuildOurDrumMap();
128 
129       setVirt(false);
130       cursorPos= QPoint(0,0);
131       _stepSize=1;
132 
133       steprec=new MusECore::StepRec(nullptr);
134 
135       songChanged(SC_TRACK_INSERTED);
136       connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int)));
137       }
138 
~DrumCanvas()139 DrumCanvas::~DrumCanvas()
140 {
141   if (must_delete_our_drum_map && ourDrumMap!=nullptr)
142     delete [] ourDrumMap;
143 
144   delete steprec;
145 }
146 
147 //---------------------------------------------------------
148 //   index2Note
149 //   Return false if invalid index
150 //---------------------------------------------------------
151 
index2Note(int index,int * port,int * channel,int * note)152 bool DrumCanvas::index2Note(int index, int* port, int* channel, int* note)
153 {
154       if ((index<0) || (index>=getOurDrumMapSize()))
155         return false;
156 
157       int mport, ch;
158       // Default to track port if -1 and track channel if -1.
159       MusECore::Track* track = nullptr;
160       MusECore::MidiTrack* mt = nullptr;
161       if(ourDrumMap[index].port == -1)
162       {
163         track = *instrument_map[index].tracks.begin();
164         if(!track->isMidiTrack())
165           return false;
166         mt = static_cast<MusECore::MidiTrack*>(track);
167         mport = mt->outPort();
168       }
169       else
170         mport = ourDrumMap[index].port;
171 
172       if(ourDrumMap[index].channel == -1)
173       {
174         if(!track)
175         {
176           track = *instrument_map[index].tracks.begin();
177           if(!track->isMidiTrack())
178             return false;
179           mt = static_cast<MusECore::MidiTrack*>(track);
180         }
181         ch = mt->outChannel();
182       }
183       else
184         ch = ourDrumMap[index].channel;
185 
186       if(port)
187         *port = mport;
188       if(channel)
189         *channel = ch;
190       if(note)
191         //*note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch;
192         *note = ourDrumMap[index].anote;
193 
194       return true;
195 }
196 
197 //---------------------------------------------------------
198 //   moveCanvasItems
199 //---------------------------------------------------------
200 
moveCanvasItems(CItemMap & items,int dp,int dx,DragType dtype,bool rasterize)201 MusECore::Undo DrumCanvas::moveCanvasItems(CItemMap& items, int dp, int dx, DragType dtype, bool rasterize)
202 {
203 
204   if(editor->parts()->empty())
205     return MusECore::Undo(); //return empty list
206 
207   MusECore::PartsToChangeMap parts2change;
208   MusECore::Undo operations;
209 
210   for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip)
211   {
212     MusECore::Part* part = ip->second;
213     if(!part)
214       continue;
215 
216     int npartoffset = 0;
217     for(iCItem ici = items.begin(); ici != items.end(); ++ici)
218     {
219       CItem* ci = ici->second;
220       ci->setMoving(false);
221 
222       if(ci->part() != part)
223         continue;
224 
225       int x = ci->pos().x() + dx;
226       int y = pitch2y(y2pitch(ci->pos().y()) + dp);
227       QPoint newpos = QPoint(x, y);
228       if(rasterize)
229         newpos = raster(newpos);
230 
231       // Test moving the item...
232       DEvent* nevent = (DEvent*) ci;
233       MusECore::Event event    = nevent->event();
234       x              = newpos.x();
235       if(x < 0)
236         x = 0;
237       int ntick = (rasterize ? editor->rasterVal(x) : x) - part->tick();
238       if(ntick < 0)
239         ntick = 0;
240       int diff = ntick + event.lenTick() - part->lenTick();
241 
242       // If moving the item would require a new part size...
243       if(diff > npartoffset)
244         npartoffset = diff;
245     }
246 
247     if(npartoffset > 0)
248     {
249       MusECore::iPartToChange ip2c = parts2change.find(part);
250       if(ip2c == parts2change.end())
251       {
252         MusECore::PartToChange p2c = {nullptr, npartoffset};
253         parts2change.insert(std::pair<MusECore::Part*, MusECore::PartToChange> (part, p2c));
254       }
255       else
256         ip2c->second.xdiff = npartoffset;
257     }
258   }
259 
260   bool forbidden=false;
261   for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c)
262   {
263     MusECore::Part* opart = ip2c->first;
264     if (opart->hasHiddenEvents() & MusECore::Part::RightEventsHidden)
265     {
266 			forbidden=true;
267 			break;
268 		}
269   }
270 
271 
272 	if (!forbidden)
273 	{
274 		std::vector< CItem* > doneList;
275 		typedef std::vector< CItem* >::iterator iDoneList;
276 
277 		for(iCItem ici = items.begin(); ici != items.end(); ++ici)
278 		{
279 			CItem* ci = ici->second;
280 
281       const QPoint oldpos(ci->pos());
282 			int x = ci->pos().x();
283 			int y = ci->pos().y();
284 			int nx = x + dx;
285 			int ny = pitch2y(y2pitch(y) + dp);
286 			QPoint newpos = QPoint(nx, ny);
287 			if(rasterize)
288 			  newpos = raster(newpos);
289 			selectItem(ci, true);
290 
291 			iDoneList idl;
292 			for(idl = doneList.begin(); idl != doneList.end(); ++idl)
293 				// This compares EventBase pointers to see if they're the same...
294 				if((*idl)->event() == ci->event())
295 					break;
296 
297 			// Do not process if the event has already been processed (meaning it's an event in a clone part)...
298 			if (idl == doneList.end())
299 			{
300 				if (moveItem(operations, ci, newpos, dtype, rasterize) == false) //error?
301         {
302           QMessageBox::warning(this, tr("Moving items failed"), tr("The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track.\nChanging the current part with ALT+LEFT/RIGHT may help."));
303           return MusECore::Undo(); //return empty list
304         }
305 				doneList.push_back(ci);
306 			}
307 			ci->move(newpos);
308 
309 			itemReleased(ci, oldpos);
310 
311 			if(dtype == MOVE_COPY || dtype == MOVE_CLONE)
312 						selectItem(ci, false);
313 		}
314 
315 		itemsReleased();
316 
317 		for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c)
318 		{
319 			MusECore::Part* opart = ip2c->first;
320 			int diff = ip2c->second.xdiff;
321 
322 			schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations);
323 		}
324 
325   	return operations;
326   }
327   else
328   {
329 		return MusECore::Undo(); //return empty list
330 	}
331 }
332 
333 //---------------------------------------------------------
334 //   moveItem
335 //---------------------------------------------------------
336 
moveItem(MusECore::Undo & operations,CItem * item,const QPoint & pos,DragType dtype,bool rasterize)337 bool DrumCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& pos, DragType dtype, bool rasterize)
338       {
339       DEvent* nevent   = (DEvent*) item;
340 
341       MusECore::MidiPart* part   = (MusECore::MidiPart*)nevent->part();
342       MusECore::MidiPart* dest_part   = part;
343 
344       int instrument        = y2pitch(pos.y());
345       if (instrument<0) instrument=0;
346       if (instrument>=getOurDrumMapSize()) instrument=getOurDrumMapSize()-1;
347       MusECore::Event event = nevent->event();
348       if (!instrument_map[instrument].tracks.contains(dest_part->track()))
349       {
350         if (debugMsg)
351           printf("trying to move an event into a different track. checking if curPart is set correctly...\n");
352 
353         if (!instrument_map[instrument].tracks.contains(curPart->track()))
354         {
355           printf ("ERROR: tried to move an event into a track which is different from both the initial part's and the curPart's track! ignoring this one...\n");
356           return false;
357         }
358         else
359           dest_part=(MusECore::MidiPart*)curPart;
360       }
361 
362       int x            = pos.x();
363       if (x < 0)
364             x = 0;
365       int ntick        = (rasterize ? editor->rasterVal(x) : x) - dest_part->tick();
366       if (ntick < 0)
367             ntick = 0;
368 
369       event.setSelected(false);
370       MusECore::Event newEvent   = event.clone();
371       newEvent.setSelected(true);
372 
373       int ev_pitch = instrument_map[instrument].pitch;
374       newEvent.setPitch(ev_pitch);
375       newEvent.setTick(ntick);
376 
377       // don't check, whether the new event is within the part
378       // at this place. with operation groups, the part isn't
379       // resized yet. (flo93)
380 
381       if (dtype == MOVE_COPY || dtype == MOVE_CLONE)
382             operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, dest_part, false, false));
383       else
384       {
385             if (dest_part == part)
386                 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false));
387             else
388             {
389                 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, part, false, false));
390                 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, dest_part, false, false));
391             }
392       }
393       return true;
394 }
395 
396 //---------------------------------------------------------
397 //   newItem
398 //---------------------------------------------------------
399 
newItem(const QPoint & p,int state)400 CItem* DrumCanvas::newItem(const QPoint& p, int state)
401       {
402       int instr = y2pitch(p.y());
403       if ((instr<0) || (instr>=getOurDrumMapSize()))
404         return nullptr;
405 
406       int k4  = (Qt::MetaModifier | Qt::AltModifier);
407       //int nk4 = Qt::ControlModifier;
408 
409       int k2  = Qt::MetaModifier;
410       int nk2 = (Qt::ControlModifier | Qt::AltModifier);
411 
412       int k1  = (Qt::ControlModifier | Qt::MetaModifier);
413       int nk1 = Qt::AltModifier;
414 
415       int velo  = ourDrumMap[instr].lv3;
416       if      ((state & k4) == k4) // && !(state & nk4))
417             velo = ourDrumMap[instr].lv4;
418       else if ((state & k2) == k2 && !(state & nk2))
419             velo = ourDrumMap[instr].lv2;
420       else if ((state & k1) == k1 && !(state & nk1))
421             velo = ourDrumMap[instr].lv1;
422       int tick = p.x();
423       if(tick < 0)
424         tick = 0;
425       if(!(state & Qt::ShiftModifier))
426         tick = editor->rasterVal(tick);
427       return newItem(tick, instr, velo);
428       }
429 
430 //---------------------------------------------------------
431 //   newItem
432 //---------------------------------------------------------
433 
newItem(int tick,int instrument,int velocity)434 CItem* DrumCanvas::newItem(int tick, int instrument, int velocity)
435 {
436   if ((instrument<0) || (instrument>=getOurDrumMapSize()))
437     return nullptr;
438 
439   if (!instrument_map[instrument].tracks.contains(curPart->track()))
440   {
441     if (debugMsg)
442       printf("tried to create a new Item which cannot be inside the current track. looking for destination part...\n");
443 
444     QSet<MusECore::Part*> parts = parts_at_tick(tick, instrument_map[instrument].tracks);
445 
446     if (parts.count() != 1)
447     {
448       QMessageBox::warning(this, tr("Creating event failed"), tr("Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous.\nSelect the destination part, then try again."));
449       return nullptr;
450     }
451     else
452     {
453       setCurrentPart(*parts.begin());
454     }
455   }
456   // else or if we found an alternative part (which has now been set as curPart)
457 
458   tick    -= curPart->tick();
459   if (tick < 0)
460         return nullptr;
461   MusECore::Event e(MusECore::Note);
462   e.setTick(tick);
463   e.setPitch(instrument_map[instrument].pitch);
464   e.setVelo(velocity);
465   e.setLenTick(ourDrumMap[instrument].len);
466   if(_playEvents)
467   {
468     int pitch, port, channel;
469     if(index2Note(instrument, &port, &channel, &pitch))
470       startPlayEvent(pitch, e.velo(), port, channel);
471   }
472   return new DEvent(e, curPart, instrument);
473 }
474 
475 //---------------------------------------------------------
476 //   newItem
477 //---------------------------------------------------------
newItem(CItem * item,bool noSnap)478 void DrumCanvas::newItem(CItem* item, bool noSnap) {
479      newItem(item, noSnap,true);
480 }
481 
newItem(CItem * item,bool noSnap,bool replace)482 void DrumCanvas::newItem(CItem* item, bool noSnap, bool replace)
483 {
484    if(!item)
485    {
486      printf("THIS SHOULD NEVER HAPPEN: DrumCanvas::newItem called with nullptr item!\n");
487      return;
488    }
489 
490     DEvent* nevent = (DEvent*) item;
491     MusECore::Event event    = nevent->event();
492     MusECore::Part* part = nevent->part();
493     int ptick = part->tick();
494     int x = item->x();
495     if (x<ptick)
496           x=ptick;
497     if (!noSnap)
498           x = editor->rasterVal(x);
499     if (x<ptick)
500           x=ptick;
501     event.setTick(x - ptick);
502     int npitch = y2pitch(item->y());
503     if ((npitch<0) || (npitch>=getOurDrumMapSize()))
504       return;
505     npitch = instrument_map[npitch].pitch;
506     event.setPitch(npitch);
507     event.setSelected(true);
508     // check for existing event
509     //    if found change command semantic from insert to delete
510     MusECore::Undo operations;
511     std::pair<MusECore::ciEvent,MusECore::ciEvent> range =
512       part->events().equal_range(event.type() == MusECore::Wave ? event.frame() : event.tick());
513     MusECore::Event oev;
514     bool found = false;
515     for(MusECore::ciEvent i = range.first; i != range.second; ++i)
516     {
517       oev = i->second;
518       if(!oev.isNote())
519         continue;
520       if(oev.pitch() == npitch)
521       {
522         found = true;
523         break;
524       }
525     }
526 
527     // Indicate do undo, and do not do port controller values and clone parts.
528     int diff = event.endTick()-part->lenTick();
529     if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed
530     {
531       if(found)
532       {
533         if(replace)
534           operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,event, oev, part, false, false));
535         else
536           operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,oev, part, false, false));
537       }
538       else
539         operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false));
540 
541       if (diff > 0) // part must be extended?
542       {
543             schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations);
544             printf("newItem: extending\n");
545       }
546     }
547     else // forbid action by not applying it
548     {
549       if(found)
550         operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,oev, part, false, false));
551     }
552 
553     if(!operations.empty())
554       MusEGlobal::song->applyOperationGroup(operations);
555     else
556       songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary
557                                       //to remove "forbidden" events from the list again
558 }
559 
560 //---------------------------------------------------------
561 //   deleteItem
562 //---------------------------------------------------------
563 
deleteItem(CItem * item)564 bool DrumCanvas::deleteItem(CItem* item)
565       {
566       MusECore::Event ev = ((DEvent*)item)->event();
567       // Indicate do undo, and do not do port controller values and clone parts.
568       MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,
569                       ev, ((DEvent*)item)->part(), false, false));
570       return false;
571       }
572 
573 //---------------------------------------------------------
574 //   itemPressed
575 //---------------------------------------------------------
576 
itemPressed(const CItem * item)577 void DrumCanvas::itemPressed(const CItem* item)
578       {
579       if (!_playEvents)
580             return;
581       MusECore::Event e = ((DEvent*)item)->event();
582       int index = e.pitch();
583       // play note:
584 
585       for (int i = 0; i < instrument_map.size(); ++i) {
586           if (instrument_map.at(i).pitch == index) {
587               index = i;
588               break;
589           }
590       }
591       int pitch, port, channel;
592       if(index2Note(index, &port, &channel, &pitch))
593         startPlayEvent(pitch, e.velo(), port, channel);
594       }
595 
596 //---------------------------------------------------------
597 //   itemReleased
598 //---------------------------------------------------------
599 
itemReleased(const CItem * item,const QPoint &)600 void DrumCanvas::itemReleased(const CItem* item, const QPoint&)
601       {
602       const int oindex = y2pitch(item->mp().y());
603 //       for (int i = 0; i < instrument_map.size(); ++i) {
604 //           if (instrument_map.at(i).pitch == index) {
605 //               index = i;
606 //               break;
607 //           }
608 //       }
609 
610       int opitch, oport, ochannel;
611       if(!index2Note(oindex, &oport, &ochannel, &opitch))
612       {
613         // Stop any playing notes:
614         stopPlayEvents();
615         return;
616       }
617 
618       // stop note:
619       stopStuckNote(oport, ochannel, opitch);
620       }
621 
622 
623 //---------------------------------------------------------
624 //   itemMoving
625 //---------------------------------------------------------
626 
itemMoving(const CItem * item,const QPoint & newMP)627 void DrumCanvas::itemMoving(const CItem* item, const QPoint& newMP)
628 {
629       const int oindex = y2pitch(item->mp().y());
630       const int index = y2pitch(newMP.y());
631       int opitch, oport, ochannel, pitch, port, channel;
632       if(!index2Note(oindex, &oport, &ochannel, &opitch))
633       {
634         // Stop any playing notes:
635         stopPlayEvents();
636         return;
637       }
638       if(!index2Note(index, &port, &channel, &pitch))
639       {
640         // Stop any playing notes:
641         stopPlayEvents();
642         return;
643       }
644 
645       if(port == oport && channel == ochannel && pitch == opitch)
646         return;
647 
648       // Stop any playing note:
649       stopStuckNote(port, channel, opitch);
650 }
651 
652 //---------------------------------------------------------
653 //   itemMoved
654 //---------------------------------------------------------
655 
itemMoved(const CItem * item,const QPoint & oldMP)656 void DrumCanvas::itemMoved(const CItem* item, const QPoint& oldMP)
657       {
658       const int oindex = y2pitch(oldMP.y());
659       const int index = y2pitch(item->mp().y());
660       int opitch, oport, ochannel, pitch, port, channel;
661       if(!index2Note(oindex, &oport, &ochannel, &opitch))
662       {
663         // Stop any playing notes:
664         stopPlayEvents();
665         return;
666       }
667       if(!index2Note(index, &port, &channel, &pitch))
668       {
669         // Stop any playing notes:
670         stopPlayEvents();
671         return;
672       }
673 
674       if(port == oport && channel == ochannel && pitch == opitch)
675         return;
676 
677       if(_playEvents)
678       {
679         // Unlike with PianoRoll::itemMoved(), don't play multiple notes (no concept of chords).
680         if(item == curItem)
681         {
682             const MusECore::Event e = ((DEvent*)item)->event();
683             // play note:
684             startPlayEvent(pitch, e.velo(), port, channel);
685             }
686       }
687       }
688 
689 //---------------------------------------------------------
690 //   drawItem
691 //---------------------------------------------------------
692 
drawItem(QPainter & p,const CItem * item,const QRect & mr,const QRegion &)693 void DrumCanvas::drawItem(QPainter&p, const CItem*item, const QRect& mr, const QRegion&)
694       {
695       DEvent* e   = (DEvent*) item;
696       int mx = 0, my = 0;
697       mx = mapx(item->pos().x());
698       my = mapy(item->pos().y());
699 
700       QPolygon pa(4);
701       pa.setPoint(0, mx - CARET2, my);
702       pa.setPoint(1, mx,          my - CARET2);
703       pa.setPoint(2, mx + CARET2, my);
704       pa.setPoint(3, mx,          my + CARET2);
705       QRect r(pa.boundingRect());
706       r = r.intersected(mr);
707       if(!r.isValid())
708         return;
709 
710       QPen pen;
711       pen.setCosmetic(true);
712       pen.setColor(Qt::black);
713       p.setPen(pen);
714 
715       if (e->part() != curPart)
716       {
717             if(item->isMoving())
718               p.setBrush(Qt::gray);
719             else if(item->isSelected())
720               p.setBrush(Qt::black);
721             else
722               p.setBrush(Qt::lightGray);
723       }
724       else if (item->isMoving()) {
725               p.setBrush(Qt::gray);
726             }
727       else if (item->isSelected())
728       {
729             p.setBrush(MusEGlobal::config.midiItemSelectedColor);
730 //            p.setBrush(Qt::black);
731       }
732       else
733       {
734             int velo    = e->event().velo();
735             MusECore::DrumMap* dm = &ourDrumMap[y2pitch(my)]; //Get the drum item
736             QColor color;
737             if (velo < dm->lv1)
738                   color.setRgb(240, 240, 255);
739             else if (velo < dm->lv2)
740                   color.setRgb(200, 200, 255);
741             else if (velo < dm->lv3)
742                   color.setRgb(170, 170, 255);
743             else
744                   color = MusEGlobal::config.midiItemColor;
745 //            color.setRgb(0, 0, 255);
746             p.setBrush(color);
747       }
748 
749       p.drawPolygon(pa);
750       }
751 
752 //---------------------------------------------------------
753 //   drawMoving
754 //    draws moving items
755 //---------------------------------------------------------
756 
drawMoving(QPainter & p,const CItem * item,const QRect & rect,const QRegion &)757 void DrumCanvas::drawMoving(QPainter& p, const CItem* item, const QRect& rect, const QRegion&)
758     {
759       QPolygon pa(4);
760       QPoint pt = map(item->mp());
761       int x = pt.x();
762       int y = pt.y();
763       pa.setPoint(0, x-CARET2,  y + TH/2);
764       pa.setPoint(1, x,         y + TH/2+CARET2);
765       pa.setPoint(2, x+CARET2,  y + TH/2);
766       pa.setPoint(3, x,         y + (TH-CARET)/2);
767       QRect mr(pa.boundingRect());
768       mr = mr.intersected(rect);
769       if(!mr.isValid())
770         return;
771       QPen pen;
772       pen.setCosmetic(true);
773       pen.setColor(Qt::black);
774       p.setPen(pen);
775       p.setBrush(Qt::black);
776       p.drawPolygon(pa);
777     }
778 
779 //---------------------------------------------------------
780 //   drawCanvas
781 //---------------------------------------------------------
782 
drawCanvas(QPainter & p,const QRect & mr,const QRegion & rg)783 void DrumCanvas::drawCanvas(QPainter& p, const QRect& mr, const QRegion& rg)
784       {
785       const QRect ur = mapDev(mr);
786 
787       int ux = ur.x();
788       if(ux < 0)
789         ux = 0;
790       const int uy = ur.y();
791       const int uw = ur.width();
792       const int uh = ur.height();
793       const int ux_2 = ux + uw;
794       const int uy_2 = uy + uh;
795 
796       QPen pen;
797       pen.setCosmetic(true);
798       pen.setColor(MusEGlobal::config.midiDividerColor);
799       p.setPen(pen);
800 
801       if (MusEGlobal::config.canvasShowGrid || MusEGlobal::config.canvasShowGridHorizontalAlways)
802       {
803         //---------------------------------------------------
804         //  horizontal lines
805         //---------------------------------------------------
806 
807         int uyy  = ((uy-1) / TH) * TH + TH;
808 
809   // For testing...
810   //       fprintf(stderr, "DrumCanvas::drawCanvas ux:%d uy:%d uw:%d uh:%d uyy:%d\n", ux, uy, uw, uh, uyy);
811 
812         for (; uyy < uy_2; uyy += TH) {
813               p.drawLine(ux, uyy, ux_2, uyy);
814               }
815       }
816 
817       if (MusEGlobal::config.canvasShowGrid)
818       {
819         //---------------------------------------------------
820         // vertical lines
821         //---------------------------------------------------
822 
823         if (MusEGlobal::config.canvasShowGrid)
824           drawTickRaster(p, mr, rg, editor->raster(), false, false, false,
825                         MusEGlobal::config.midiCanvasBeatColor,
826                         MusEGlobal::config.midiCanvasBeatColor,
827                         MusEGlobal::config.midiCanvasFineColor,
828                         MusEGlobal::config.midiCanvasBarColor);
829       }
830 }
831 
832 //---------------------------------------------------------
833 //   drawTopItem
834 //---------------------------------------------------------
835 
drawTopItem(QPainter & p,const QRect &,const QRegion &)836 void DrumCanvas::drawTopItem(QPainter& p, const QRect&, const QRegion&)
837 {
838   // draw cursor
839   if (_tool == CursorTool) {
840     QPen pen;
841     pen.setCosmetic(true);
842     pen.setColor(Qt::black);
843     p.setPen(pen);
844 
845     int y = mapy(TH * cursorPos.y());
846 
847 //    p.drawPixmap(mapx(cursorPos.x())-TH/2,y,TH,TH, *cursorIconSVG);
848     cursorIconSVG->paint(&p, mapx(cursorPos.x())-TH/2, y, TH, TH);
849     // need to figure out a coordinate system for the cursor, complicated stuff.
850   }
851 
852 }
853 //---------------------------------------------------------
854 //   y2pitch
855 //---------------------------------------------------------
856 
y2pitch(int y) const857 int DrumCanvas::y2pitch(int y) const
858       {
859       int pitch = y/TH;
860       if (pitch >= instrument_map.size())
861             pitch = instrument_map.size()-1;
862       else if (pitch<0)
863             pitch = 0;
864       return pitch;
865       }
866 
867 //---------------------------------------------------------
868 //   pitch2y
869 //---------------------------------------------------------
870 
pitch2y(int pitch) const871 int DrumCanvas::pitch2y(int pitch) const
872       {
873       return pitch * TH;
874       }
875 
876 //---------------------------------------------------------
877 //   cmd
878 //---------------------------------------------------------
879 
cmd(int cmd)880 void DrumCanvas::cmd(int cmd)
881       {
882       switch (cmd) {
883             case CMD_SELECT_ALL:     // select all
884                   for (iCItem k = items.begin(); k != items.end(); ++k) {
885                         if (!k->second->isSelected())
886                               selectItem(k->second, true);
887                         }
888                   break;
889             case CMD_SELECT_NONE:     // select none
890                   deselectAll();
891                   break;
892             case CMD_SELECT_INVERT:     // invert selection
893                   for (iCItem k = items.begin(); k != items.end(); ++k) {
894                         selectItem(k->second, !k->second->isSelected());
895                         }
896                   break;
897             case CMD_SELECT_ILOOP:     // select inside loop
898                   for (iCItem k = items.begin(); k != items.end(); ++k) {
899                         DEvent* nevent =(DEvent*)(k->second);
900                         MusECore::Part* part = nevent->part();
901                         MusECore::Event event = nevent->event();
902                         unsigned tick  = event.tick() + part->tick();
903                         if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())
904                               selectItem(k->second, false);
905                         else
906                               selectItem(k->second, true);
907                         }
908                   break;
909             case CMD_SELECT_OLOOP:     // select outside loop
910                   for (iCItem k = items.begin(); k != items.end(); ++k) {
911                         DEvent* nevent = (DEvent*)(k->second);
912                         MusECore::Part* part     = nevent->part();
913                         MusECore::Event event    = nevent->event();
914                         unsigned tick  = event.tick() + part->tick();
915                         if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())
916                               selectItem(k->second, true);
917                         else
918                               selectItem(k->second, false);
919                         }
920                   break;
921             case CMD_RANGE_TO_SELECTION:
922                 setRangeToSelection();
923                 break;
924             case CMD_SELECT_PREV_PART:     // select previous part
925                   {
926                       MusECore::Part* pt = editor->curCanvasPart();
927                       MusECore::Part* newpt = pt;
928                       MusECore::PartList* pl = editor->parts();
929                       for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip)
930                         if(ip->second == pt)
931                         {
932                           if(ip == pl->begin())
933                             ip = pl->end();
934                           --ip;
935                           newpt = ip->second;
936                           break;
937                         }
938                       if(newpt != pt)
939                         editor->setCurCanvasPart(newpt);
940                   }
941                   break;
942             case CMD_SELECT_NEXT_PART:     // select next part
943                   {
944                       MusECore::Part* pt = editor->curCanvasPart();
945                       MusECore::Part* newpt = pt;
946                       MusECore::PartList* pl = editor->parts();
947                       for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip)
948                         if(ip->second == pt)
949                         {
950                           ++ip;
951                           if(ip == pl->end())
952                             ip = pl->begin();
953                           newpt = ip->second;
954                           break;
955                         }
956                       if(newpt != pt)
957                         editor->setCurCanvasPart(newpt);
958                   }
959                   break;
960 
961             case CMD_FIXED_LEN: //Set notes to the length specified in the drummap
962                   if (!selectionSize())
963                         break;
964                   MusEGlobal::song->startUndo();
965                   for (iCItem k = items.begin(); k != items.end(); ++k) {
966                         if (k->second->isSelected()) {
967                               DEvent* devent = (DEvent*)(k->second);
968                               MusECore::Event event    = devent->event();
969                               MusECore::Event newEvent = event.clone();
970                               // newEvent.setLenTick(drumMap[event.pitch()].len);
971                               newEvent.setLenTick(ourDrumMap[y2pitch(devent->y())].len);
972                               // Operation is undoable but do not start/end undo.
973                               // Indicate do not do port controller values and clone parts.
974                               MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,
975                                                 newEvent, event, devent->part(), false, false), MusECore::Song::OperationUndoable);
976                               }
977                         }
978                   MusEGlobal::song->endUndo(SC_EVENT_MODIFIED);
979                   break;
980             case CMD_LEFT:
981                   {
982                       int spos = pos[0];
983                       if(spos > 0)
984                       {
985                         spos -= 1;     // Nudge by -1, then snap down with raster1.
986                         spos = MusEGlobal::sigmap.raster1(spos, editor->rasterStep(pos[0]));
987                       }
988                       if(spos < 0)
989                         spos = 0;
990                       MusECore::Pos p(spos,true);
991                       MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
992                   }
993                   break;
994             case CMD_RIGHT:
995                   {
996                       int spos = MusEGlobal::sigmap.raster2(pos[0] + 1, editor->rasterStep(pos[0]));    // Nudge by +1, then snap up with raster2.
997                       MusECore::Pos p(spos,true);
998                       MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
999                   }
1000                   break;
1001             case CMD_LEFT_NOSNAP:
1002                   {
1003                     printf("left no snap\n");
1004                   int spos = pos[0] - editor->rasterStep(pos[0]);
1005                   if (spos < 0)
1006                         spos = 0;
1007                   MusECore::Pos p(spos,true);
1008                   MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW
1009                   }
1010                   break;
1011             case CMD_RIGHT_NOSNAP:
1012                   {
1013                   MusECore::Pos p(pos[0] + editor->rasterStep(pos[0]), true);
1014                   MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW
1015                   }
1016                   break;
1017             }
1018       itemSelectionsChanged();
1019       redraw();
1020       }
1021 
1022 
1023 //---------------------------------------------------------
1024 //   startDrag
1025 //---------------------------------------------------------
1026 
startDrag(CItem *,DragType t)1027 void DrumCanvas::startDrag(CItem* /* item*/, DragType t)
1028 {
1029    QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1);
1030 
1031    if (md)
1032    {
1033       // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object.
1034       //  The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can
1035       //  clean up after the drag and drop operation has been completed. "
1036       QDrag* drag = new QDrag(this);
1037       drag->setMimeData(md);
1038 
1039       if (t == MOVE_COPY || t == MOVE_CLONE)
1040          drag->exec(Qt::CopyAction);
1041       else
1042          drag->exec(Qt::MoveAction);
1043    }
1044 }
1045 
1046 //---------------------------------------------------------
1047 //   dragEnterEvent
1048 //---------------------------------------------------------
1049 
dragEnterEvent(QDragEnterEvent * event)1050 void DrumCanvas::dragEnterEvent(QDragEnterEvent* event)
1051       {
1052       event->acceptProposedAction();  // TODO CHECK Tim.
1053       }
1054 
1055 //---------------------------------------------------------
1056 //   dragMoveEvent
1057 //---------------------------------------------------------
1058 
dragMoveEvent(QDragMoveEvent *)1059 void DrumCanvas::dragMoveEvent(QDragMoveEvent*)
1060       {
1061       }
1062 
1063 //---------------------------------------------------------
1064 //   dragLeaveEvent
1065 //---------------------------------------------------------
1066 
dragLeaveEvent(QDragLeaveEvent *)1067 void DrumCanvas::dragLeaveEvent(QDragLeaveEvent*)
1068       {
1069       }
1070 
1071 
1072 //---------------------------------------------------------
1073 //   keyPressed - called from DList
1074 //---------------------------------------------------------
1075 
keyPressed(int index,int velocity)1076 void DrumCanvas::keyPressed(int index, int velocity)
1077 {
1078       if(velocity > 127)
1079         velocity = 127;
1080       else if(velocity <= 0)
1081         velocity = 1;
1082 
1083       // Stop all notes.
1084       stopPlayEvents();
1085 
1086       if ((index<0) || (index>=getOurDrumMapSize()))
1087         return;
1088       // called from DList - play event
1089       // play note:
1090       if(_playEvents)
1091       {
1092         int pitch, port, channel;
1093         if(index2Note(index, &port, &channel, &pitch))
1094           startPlayEvent(pitch, velocity, port, channel);
1095       }
1096 
1097       if (_steprec) /* && pos[0] >= start_tick && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */
1098       {
1099         if ( curPart && instrument_map[index].tracks.contains(curPart->track()) )
1100             steprec->record(curPart,instrument_map[index].pitch,ourDrumMap[index].len,editor->raster(),velocity,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, -1 /* invalid pitch as "really played" -> the "insert rest" feature is never triggered */);
1101         else
1102         {
1103           QSet<MusECore::Part*> parts = parts_at_tick(pos[0], instrument_map[index].tracks);
1104 
1105           if (parts.count() != 1)
1106             QMessageBox::warning(this, tr("Recording event failed"), tr("Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous.\nSelect the destination part, then try again."));
1107           else
1108             steprec->record(*parts.begin(), instrument_map[index].pitch,ourDrumMap[index].len,editor->raster(),velocity,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, -1 /* invalid pitch as "really played" -> the "insert rest" feature is never triggered */);
1109 
1110         }
1111       }
1112 }
1113 
1114 //---------------------------------------------------------
1115 //   keyReleased
1116 //---------------------------------------------------------
1117 
keyReleased(int,bool)1118 void DrumCanvas::keyReleased(int, bool)
1119       {
1120       // release note:
1121       if(_playEvents)
1122         stopPlayEvents();
1123       }
1124 
1125 //---------------------------------------------------------
1126 //   mapChanged
1127 //---------------------------------------------------------
1128 
mapChanged(int spitch,int dpitch)1129 void DrumCanvas::mapChanged(int spitch, int dpitch)
1130 {
1131    // spitch may be the same as dpitch! and something in here must be executed
1132    // even if they're same (i assume it's song->update(SC_DRUMMAP)) (flo93)
1133 
1134     if (dpitch!=spitch)
1135     {
1136       using MusEGlobal::global_drum_ordering_t;
1137       using MusEGlobal::global_drum_ordering;
1138 
1139       for (QSet<MusECore::Track*>::iterator it=instrument_map[spitch].tracks.begin();
1140                                             it!=instrument_map[spitch].tracks.end(); it++)
1141       {
1142         if (dynamic_cast<MusECore::MidiTrack*>(*it))
1143         dynamic_cast<MusECore::MidiTrack*>(*it)->set_drummap_ordering_tied_to_patch(false);
1144       }
1145       for (QSet<MusECore::Track*>::iterator it=instrument_map[dpitch].tracks.begin();
1146                                             it!=instrument_map[dpitch].tracks.end(); it++)
1147       {
1148         if (dynamic_cast<MusECore::MidiTrack*>(*it))
1149         dynamic_cast<MusECore::MidiTrack*>(*it)->set_drummap_ordering_tied_to_patch(false);
1150       }
1151 
1152       MusECore::DrumMap dm_temp = ourDrumMap[spitch];
1153       instrument_number_mapping_t im_temp = instrument_map[spitch];
1154 
1155       global_drum_ordering_t order_temp;
1156       for (global_drum_ordering_t::iterator it=global_drum_ordering.begin(); it!=global_drum_ordering.end();)
1157       {
1158         if (im_temp.pitch==it->second && im_temp.tracks.contains(it->first))
1159         {
1160           order_temp.push_back(*it);
1161           it=global_drum_ordering.erase(it);
1162         }
1163         else
1164           it++;
1165       }
1166 
1167       // the instrument represented by instrument_map[dpitch] is always the instrument
1168       // which will be immediately AFTER our dragged instrument. or it's invalid
1169       if (dpitch < getOurDrumMapSize())
1170       {
1171         for (global_drum_ordering_t::iterator it=global_drum_ordering.begin(); it!=global_drum_ordering.end(); it++)
1172           if (instrument_map[dpitch].pitch==it->second && instrument_map[dpitch].tracks.contains(it->first))
1173           {
1174             while (!order_temp.empty())
1175               it=global_drum_ordering.insert(it, order_temp.takeLast());
1176 
1177             break;
1178           }
1179       }
1180       else
1181       {
1182         global_drum_ordering_t::iterator it=global_drum_ordering.end();
1183         while (!order_temp.empty())
1184           it=global_drum_ordering.insert(it, order_temp.takeLast());
1185       }
1186 
1187 
1188 
1189 
1190 
1191       if (dpitch > spitch)
1192       {
1193         for (int i=spitch; i<dpitch-1; i++)
1194         {
1195           ourDrumMap[i]=ourDrumMap[i+1];
1196           instrument_map[i]=instrument_map[i+1];
1197         }
1198 
1199         ourDrumMap[dpitch-1] = dm_temp;
1200         instrument_map[dpitch-1] = im_temp;
1201       }
1202       else if (spitch > dpitch)
1203       {
1204         for (int i=spitch; i>dpitch; i--)
1205         {
1206           ourDrumMap[i]=ourDrumMap[i-1];
1207           instrument_map[i]=instrument_map[i-1];
1208         }
1209 
1210         ourDrumMap[dpitch] = dm_temp;
1211         instrument_map[dpitch] = im_temp;
1212       }
1213     }
1214 
1215 
1216     MusEGlobal::song->update(SC_DRUMMAP); // this causes a complete rebuild of ourDrumMap
1217                                           // which also handles the changed order in all
1218                                           // other drum editors
1219 }
1220 
1221 //---------------------------------------------------------
1222 //   resizeEvent
1223 //---------------------------------------------------------
1224 
resizeEvent(QResizeEvent * ev)1225 void DrumCanvas::resizeEvent(QResizeEvent* ev)
1226       {
1227       if (ev->size().width() != ev->oldSize().width())
1228             emit newWidth(ev->size().width());
1229       EventCanvas::resizeEvent(ev);
1230       }
1231 
1232 
1233 //---------------------------------------------------------
1234 //   modifySelected
1235 //---------------------------------------------------------
1236 
modifySelected(NoteInfo::ValType type,int val,bool delta_mode)1237 void DrumCanvas::modifySelected(NoteInfo::ValType type, int val, bool delta_mode)
1238       {
1239       QList< QPair<int,MusECore::Event> > already_done;
1240       MusECore::Undo operations;
1241       for (iCItem i = items.begin(); i != items.end(); ++i) {
1242             if (!(i->second->isSelected()))
1243                   continue;
1244             DEvent* e   = (DEvent*)(i->second);
1245             MusECore::Event event = e->event();
1246             if (event.type() != MusECore::Note)
1247                   continue;
1248 
1249             MusECore::MidiPart* part = (MusECore::MidiPart*)(e->part());
1250 
1251             if (already_done.contains(QPair<int,MusECore::Event>(part->clonemaster_sn(), event)))
1252               continue;
1253 
1254             MusECore::Event newEvent = event.clone();
1255 
1256             switch (type) {
1257                   case NoteInfo::VAL_TIME:
1258                         {
1259                         int newTime = val;
1260                         if(delta_mode)
1261                           newTime += event.tick();
1262                         else
1263                           newTime -= part->tick();
1264                         if (newTime < 0)
1265                            newTime = 0;
1266                         newEvent.setTick(newTime);
1267                         }
1268                         break;
1269                   case NoteInfo::VAL_LEN:
1270                         {
1271                         int len = val;
1272                         if(delta_mode)
1273                           len += event.lenTick();
1274                         if (len < 1)
1275                               len = 1;
1276                         newEvent.setLenTick(len);
1277                         }
1278                         break;
1279                   case NoteInfo::VAL_VELON:
1280                         {
1281                         int velo = val;
1282                         if(delta_mode)
1283                           velo += event.velo();
1284                         if (velo > 127)
1285                               velo = 127;
1286                         else if (velo < 0)
1287                               // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now.
1288 //                               velo = 0;
1289                               velo = 1;
1290                         newEvent.setVelo(velo);
1291                         }
1292                         break;
1293                   case NoteInfo::VAL_VELOFF:
1294                         {
1295                         int velo = val;
1296                         if(delta_mode)
1297                           velo += event.veloOff();
1298                         if (velo > 127)
1299                               velo = 127;
1300                         else if (velo < 0)
1301                               velo = 0;
1302                         newEvent.setVeloOff(velo);
1303                         }
1304                         break;
1305                   case NoteInfo::VAL_PITCH:
1306                         {
1307                           int direction = -val;
1308                           for (int i = 0; i < instrument_map.size(); ++i) {
1309                               if (instrument_map.at(i).pitch == event.pitch()) {
1310                                   int nextPos = i + direction;
1311                                   if (nextPos> -1 && nextPos < instrument_map.size())
1312                                     newEvent.setPitch(instrument_map.at(nextPos).pitch);
1313                                   break;
1314                               }
1315                           }
1316                         }
1317                         break;
1318                   }
1319 
1320             operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false));
1321 
1322             already_done.append(QPair<int,MusECore::Event>(part->clonemaster_sn(), event));
1323             }
1324       MusEGlobal::song->applyOperationGroup(operations);
1325       }
1326 
1327 //---------------------------------------------------------
1328 //   curPartChanged
1329 //---------------------------------------------------------
1330 
curPartChanged()1331 void DrumCanvas::curPartChanged()
1332       {
1333       EventCanvas::curPartChanged();
1334       editor->setWindowTitle(getCaption());
1335       }
1336 
1337 //---------------------------------------------------------
1338 //   getNextStep - gets next tick in the chosen direction
1339 //                 when raster and stepSize are taken into account
1340 //---------------------------------------------------------
getNextStep(unsigned int currentPos,int basicStep,int stepSize)1341 int DrumCanvas::getNextStep(unsigned int currentPos, int basicStep, int stepSize)
1342 {
1343   int newPos = currentPos; // newPos will be updated for every loop
1344   for (int i = 0; i < stepSize; i++)
1345   {
1346     if (basicStep > 0) // moving right
1347     {
1348       // Nudge by +1, then snap up with raster2.
1349       newPos = MusEGlobal::sigmap.raster2(newPos + basicStep, editor->rasterStep(newPos));
1350       if (newPos > signed(curPart->endTick() - editor->rasterStep(curPart->endTick())))
1351       {
1352         newPos = curPart->tick();
1353       }
1354     }
1355     else // moving left
1356     {
1357       // Nudge by -1, then snap up with raster1.
1358       newPos = MusEGlobal::sigmap.raster1(newPos + basicStep, editor->rasterStep(newPos));
1359 
1360       if (newPos < signed(curPart->tick()))
1361       {
1362         newPos = MusEGlobal::sigmap.raster1(curPart->endTick()-1, editor->rasterStep(curPart->endTick()));
1363       }
1364     }
1365   }
1366   return newPos;
1367 }
1368 
1369 //---------------------------------------------------------
1370 //   keyPress
1371 //---------------------------------------------------------
1372 
keyPress(QKeyEvent * event)1373 void DrumCanvas::keyPress(QKeyEvent* event)
1374 {
1375   if (_tool == CursorTool) {
1376 
1377     int key = event->key();
1378     if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier)
1379           key += Qt::SHIFT;
1380     if (((QInputEvent*)event)->modifiers() & Qt::AltModifier)
1381           key += Qt::ALT;
1382     if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier)
1383           key+= Qt::CTRL;
1384 
1385     // Select items by key (PianoRoll & DrumEditor)
1386     if (key == shortcuts[SHRT_SEL_RIGHT].key) {
1387       cursorPos.setX(getNextStep(cursorPos.x(),1));
1388 
1389       selectCursorEvent(getEventAtCursorPos());
1390       if (mapx(cursorPos.x()) < 0 || mapx(cursorPos.x()) > width())
1391         emit followEvent(cursorPos.x());
1392       update();
1393       return;
1394     }
1395     else if (key == shortcuts[SHRT_SEL_LEFT].key) {
1396       cursorPos.setX(getNextStep(cursorPos.x(),-1));
1397 
1398       selectCursorEvent(getEventAtCursorPos());
1399       if (mapx(cursorPos.x()) < 0 || mapx(cursorPos.x()) > width())
1400         emit followEvent(cursorPos.x());
1401       update();
1402       return;
1403     }
1404     // NOTE: The inner NewItem may play the note. But let us not stop the note so shortly after playing it.
1405     //       So it is up to the corresponding keyRelease() to stop the note.
1406     else if ( key == shortcuts[SHRT_ADDNOTE_1].key ||
1407               key == shortcuts[SHRT_ADDNOTE_2].key ||
1408               key == shortcuts[SHRT_ADDNOTE_3].key ||
1409               key == shortcuts[SHRT_ADDNOTE_4].key)
1410     {
1411         if (key ==shortcuts[SHRT_ADDNOTE_1].key) {
1412           newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv1),false,true);
1413         }
1414         else if(key ==shortcuts[SHRT_ADDNOTE_2].key) {
1415           newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv2),false,true);
1416         }
1417         else if(key ==shortcuts[SHRT_ADDNOTE_3].key) {
1418           newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv3),false,true);
1419         }
1420         else if(key ==shortcuts[SHRT_ADDNOTE_4].key) {
1421           newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv4),false,true);
1422         }
1423 
1424         cursorPos.setX(getNextStep(cursorPos.x(),1, _stepSize));
1425         selectCursorEvent(getEventAtCursorPos());
1426         if (mapx(cursorPos.x()) < 0 || mapx(cursorPos.x()) > width())
1427           emit followEvent(cursorPos.x());
1428         return;
1429     }
1430   }
1431   EventCanvas::keyPress(event);
1432 }
1433 
1434 //---------------------------------------------------------
1435 //   keyRelease
1436 //---------------------------------------------------------
1437 
keyRelease(QKeyEvent * event)1438 void DrumCanvas::keyRelease(QKeyEvent* event)
1439 {
1440   if (_tool == CursorTool)
1441   {
1442     if (_playEvents)
1443     {
1444       int key = event->key();
1445       if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier)
1446             key += Qt::SHIFT;
1447       if (((QInputEvent*)event)->modifiers() & Qt::AltModifier)
1448             key += Qt::ALT;
1449       if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier)
1450             key+= Qt::CTRL;
1451       if (key == shortcuts[SHRT_ADDNOTE_1].key ||
1452           key == shortcuts[SHRT_ADDNOTE_2].key ||
1453           key == shortcuts[SHRT_ADDNOTE_3].key ||
1454           key == shortcuts[SHRT_ADDNOTE_4].key)
1455       {
1456         // Must stop note that was played.
1457                 stopPlayEvents();
1458         return;
1459       }
1460     }
1461   }
1462   EventCanvas::keyRelease(event);
1463 }
1464 
1465 //---------------------------------------------------------
1466 //   setTool2
1467 //---------------------------------------------------------
setTool2(int)1468 void DrumCanvas::setTool2(int)
1469 {
1470   if (_tool == CursorTool)
1471     deselectAll();
1472   if (unsigned(cursorPos.x()) < curPart->tick())
1473     cursorPos.setX(curPart->tick());
1474   update();
1475 }
1476 //---------------------------------------------------------
1477 //   setCurDrumInstrument
1478 //---------------------------------------------------------
setCurDrumInstrument(int i)1479 void DrumCanvas::setCurDrumInstrument(int i)
1480 {
1481   cursorPos.setY(i);
1482   update();
1483 }
1484 
1485 //---------------------------------------------------------
1486 //   setStep
1487 //---------------------------------------------------------
setStep(int v)1488 void DrumCanvas::setStep(int v)
1489 {
1490   _stepSize=v;
1491 }
1492 
1493 //---------------------------------------------------------
1494 //   getEventAtCursorPos
1495 //---------------------------------------------------------
getEventAtCursorPos()1496 const MusECore::Event* DrumCanvas::getEventAtCursorPos()
1497 {
1498     if (_tool != CursorTool)
1499       return 0;
1500     if (instrument_map[cursorPos.y()].tracks.contains(curPart->track()))
1501     {
1502       MusECore::ciEvent lower  = curPart->events().lower_bound(cursorPos.x()-curPart->tick());
1503       MusECore::ciEvent upper  = curPart->events().upper_bound(cursorPos.x()-curPart->tick());
1504       int curPitch = instrument_map[cursorPos.y()].pitch;
1505       for (MusECore::ciEvent i = lower; i != upper; ++i) {
1506         const MusECore::Event& ev = i->second;
1507         if (ev.isNote()  &&  ev.pitch() == curPitch)
1508           return &ev;
1509       }
1510     }
1511     // else or if the for loop didn't find anything
1512     return 0;
1513 }
1514 //---------------------------------------------------------
1515 //   selectCursorEvent
1516 //---------------------------------------------------------
selectCursorEvent(const MusECore::Event * ev)1517 void DrumCanvas::selectCursorEvent(const MusECore::Event* ev)
1518 {
1519   for (iCItem i = items.begin(); i != items.end(); ++i)
1520   {
1521         MusECore::Event e = i->second->event();
1522 
1523         if (ev && ev->tick() == e.tick() && ev->pitch() == e.pitch() && e.isNote())
1524           i->second->setSelected(true);
1525         else
1526           i->second->setSelected(false);
1527 
1528   }
1529   itemSelectionsChanged();
1530 }
1531 
1532 //---------------------------------------------------------
1533 //   midiNote
1534 //---------------------------------------------------------
midiNote(int pitch,int velo)1535 void DrumCanvas::midiNote(int pitch, int velo)
1536 {
1537       using MusECore::Track;
1538       using MusECore::Part;
1539 
1540       if (debugMsg) printf("DrumCanvas::midiNote: pitch=%i, velo=%i\n", pitch, velo);
1541 
1542       if (_midiin && _steprec && !MusEGlobal::audio->isPlaying() && velo && !(MusEGlobal::globalKeyState & Qt::AltModifier) /* && pos[0] >= start_tick && pos[0] < end_tick [removed by flo93: this is handled in steprec->record()] */ )
1543       {
1544          if (pitch == MusEGlobal::rcSteprecNote) // skip the fancy code below, simply record a rest
1545          {
1546            if (curPart)
1547              steprec->record(curPart,0xdead,0xbeef,editor->raster(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, pitch);
1548          }
1549          else
1550          {
1551             QSet<Track*> possible_dest_tracks;
1552             Part* rec_part=nullptr;
1553             int rec_index=-1;
1554 
1555             int ourDrumMapSize=getOurDrumMapSize();
1556             int i;
1557             for (i=0;i<ourDrumMapSize;i++)
1558             {
1559               if ( instrument_map[i].tracks.contains(curPart->track()) && ourDrumMap[i].enote==pitch)
1560               {
1561                 rec_part=curPart;
1562                 rec_index=i;
1563                 break;
1564               }
1565               else if (ourDrumMap[i].enote==pitch)
1566                 possible_dest_tracks.unite(instrument_map[i].tracks);
1567             }
1568 
1569             if (rec_part == nullptr) // if recording to curPart isn't possible
1570             {
1571               QSet<Part*> possible_dest_parts = parts_at_tick(pos[0], possible_dest_tracks);
1572 
1573               if (possible_dest_parts.count() != 1)
1574                 QMessageBox::warning(this, tr("Recording event failed"), tr("Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous.\nSelect the destination part, then try again."));
1575               else
1576               {
1577                 rec_part = *possible_dest_parts.begin();
1578                 Track* dest_track=rec_part->track();
1579 
1580                 for (i=0;i<ourDrumMapSize;i++)
1581                   if ( instrument_map[i].tracks.contains(dest_track) && ourDrumMap[i].enote==pitch)
1582                   {
1583                     rec_index=i;
1584                     break;
1585                   }
1586 
1587                 if (rec_index==-1)
1588                 {
1589                   printf("ERROR: THIS SHOULD NEVER HAPPEN: i found a destination part for step recording, but now i can't find the instrument any more in DrumCanvas::midiNote()?!\n");
1590                   QMessageBox::critical(this, tr("Internal error"), tr("Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded."));
1591                   rec_part=nullptr;
1592                 }
1593               }
1594             }
1595 
1596             if (rec_part != nullptr)
1597               steprec->record(rec_part,instrument_map[rec_index].pitch,ourDrumMap[rec_index].len,editor->raster(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, pitch);
1598          }
1599       }
1600 }
1601 
1602 
pitch_and_track_to_instrument(int pitch,MusECore::Track * track)1603 int DrumCanvas::pitch_and_track_to_instrument(int pitch, MusECore::Track* track)
1604 {
1605   for (int i=0; i<instrument_map.size(); i++)
1606     if (instrument_map[i].tracks.contains(track) && instrument_map[i].pitch==pitch)
1607       return i;
1608 
1609   if (heavyDebugMsg) printf("DrumCanvas::pitch_and_track_to_instrument() called with invalid arguments.\n");
1610   return -1;
1611 }
1612 
propagate_drummap_change(int instrument,int fields,bool isReset,bool includeDefault,bool isInstrumentMod,bool doWholeMap)1613 void DrumCanvas::propagate_drummap_change(int instrument, int fields, bool isReset, bool includeDefault, bool isInstrumentMod, bool doWholeMap)
1614 {
1615   //fprintf(stderr, "DrumCanvas::propagate_track_drummap_change instrument:%d fields:%x isReset:%d isInstrumentMod:%d\n",
1616   //        instrument, fields, isReset, isInstrumentMod);
1617   const QSet<MusECore::Track*>& tracks=instrument_map[instrument].tracks;
1618   int index=instrument_map[instrument].pitch;
1619 
1620   MusECore::DrumMapTrackOperation* dmop = new MusECore::DrumMapTrackOperation;
1621   dmop->_isReset = isReset;
1622   dmop->_includeDefault = includeDefault;
1623   dmop->_doWholeMap = doWholeMap;
1624   dmop->_isInstrumentMod = isInstrumentMod;
1625 
1626   MusECore::PendingOperationList operations;
1627   MusECore::Track* t;
1628   for(QSet<MusECore::Track*>::const_iterator it = tracks.begin(); it != tracks.end(); it++)
1629   {
1630     t = *it;
1631     if(!t->isDrumTrack())
1632       continue;
1633     MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(t);
1634     dmop->_tracks.push_back(mt);
1635   }
1636 
1637   if(isReset)
1638     dmop->_workingItemList.add(index, MusECore::WorkingDrumMapEntry(MusECore::DrumMap(), fields)); // Fixme: Dummy map. Should just be fields.
1639   else
1640     dmop->_workingItemList.add(index, MusECore::WorkingDrumMapEntry(ourDrumMap[instrument], fields));
1641 
1642   operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ModifyTrackDrumMapItem));
1643   MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1644 }
1645 
isWorkingMapInstrument(int instr,int fields) const1646 int DrumCanvas::isWorkingMapInstrument(int instr, int fields) const
1647 {
1648   const QSet<MusECore::Track*>& tracks=instrument_map[instr].tracks;
1649   int index=instrument_map[instr].pitch;
1650 
1651   MusECore::Track* t;
1652   MusECore::MidiTrack* mt;
1653   int ret = MusECore::WorkingDrumMapEntry::NoOverride;
1654   for (QSet<MusECore::Track*>::const_iterator it = tracks.begin(); it != tracks.end(); it++)
1655   {
1656     t = *it;
1657     if(!t->isDrumTrack())
1658       continue;
1659     mt = static_cast<MusECore::MidiTrack*>(t);
1660     // Don't pass a patch - ask it to take care of patch number for us.
1661     ret |= mt->isWorkingMapItem(index, fields);
1662   }
1663   return ret;
1664 }
1665 
hasOverrides(int instr) const1666 bool DrumCanvas::hasOverrides(int instr) const
1667 {
1668   const QSet<MusECore::Track*>& tracks=instrument_map[instr].tracks;
1669   MusECore::Track* t;
1670   MusECore::MidiTrack* mt;
1671   for (QSet<MusECore::Track*>::const_iterator it = tracks.begin(); it != tracks.end(); it++)
1672   {
1673     t = *it;
1674     if(!t->isDrumTrack())
1675       continue;
1676     mt = static_cast<MusECore::MidiTrack*>(t);
1677     if(!mt->workingDrumMap()->empty())
1678       return true;
1679   }
1680   return false;
1681 }
1682 
resetOverridesForAllPatches(int instr)1683 void DrumCanvas::resetOverridesForAllPatches(int instr)
1684 {
1685   if(QMessageBox::warning(this, tr("Drum map"),
1686      tr("Reset the track's drum map with instrument defaults?"),
1687      QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok)
1688   {
1689     MusECore::PendingOperationList operations;
1690     const QSet<MusECore::Track*>& tracks=instrument_map[instr].tracks;
1691     MusECore::Track* t;
1692     MusECore::MidiTrack* mt;
1693     MusECore::WorkingDrumMapPatchList* new_wdmpl;
1694     MusECore::DrumMapTrackPatchReplaceOperation* dmop;
1695     for (QSet<MusECore::Track*>::const_iterator it = tracks.begin(); it != tracks.end(); it++)
1696     {
1697       t = *it;
1698       if(!t->isDrumTrack())
1699         continue;
1700       mt = static_cast<MusECore::MidiTrack*>(t);
1701       if(!mt->workingDrumMap()->empty())
1702       {
1703         // Completely blank replacement list.
1704         new_wdmpl = new MusECore::WorkingDrumMapPatchList();
1705         // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the
1706         //  original lists will be deleted, in the operation following.
1707         dmop = new MusECore::DrumMapTrackPatchReplaceOperation;
1708         dmop->_isInstrumentMod = false; // Not instrument operation.
1709         dmop->_workingItemPatchList = new_wdmpl;
1710         dmop->_track = static_cast<MusECore::MidiTrack*>(t);
1711         operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList));
1712       }
1713     }
1714     if(!operations.empty())
1715       MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1716   }
1717 }
1718 
rebuildOurDrumMap()1719 void DrumCanvas::rebuildOurDrumMap()
1720 {
1721   using MusECore::drummaps_almost_equal;
1722   using MusECore::Track;
1723   using MusECore::MidiTrack;
1724   using MusECore::TrackList;
1725   using MusECore::ciTrack;
1726   using MusECore::ciPart;
1727   using MusECore::DrumMap;
1728   using MusEGlobal::global_drum_ordering_t;
1729   using MusEGlobal::global_drum_ordering;
1730 
1731   //fprintf(stderr, "DrumCanvas::rebuildOurDrumMap\n");
1732   bool need_update = false;
1733 
1734   TrackList* tl=MusEGlobal::song->tracks();
1735   QList< QSet<Track*> > track_groups;
1736   QVector<instrument_number_mapping_t> old_instrument_map = instrument_map;
1737 
1738   instrument_map.clear();
1739 
1740   for (ciTrack track = tl->begin(); track!=tl->end(); track++)
1741   {
1742     ciPart p_it;
1743     for (p_it=drumEditor->parts()->begin(); p_it!=drumEditor->parts()->end(); p_it++)
1744       if (p_it->second->track() == *track)
1745         break;
1746 
1747     if (p_it!=drumEditor->parts()->end()) // if *track is represented by some part in this editor
1748     {
1749       bool inserted=false;
1750 
1751       switch (drumEditor->group_mode())
1752       {
1753         case DrumEdit::GROUP_SAME_CHANNEL:
1754           for (QList< QSet<Track*> >::iterator group=track_groups.begin(); group!=track_groups.end(); group++)
1755             if ( ((MidiTrack*)*group->begin())->outChannel() == ((MidiTrack*)*track)->outChannel()  &&
1756                   ((MidiTrack*)*group->begin())->outPort() == ((MidiTrack*)*track)->outPort()  &&
1757                   (drummaps_almost_equal(((MidiTrack*)*group->begin())->drummap(), ((MidiTrack*)*track)->drummap())) )
1758             {
1759               group->insert(*track);
1760               inserted=true;
1761               break;
1762             }
1763           break;
1764 
1765         case DrumEdit::GROUP_MAX:
1766           for (QList< QSet<Track*> >::iterator group=track_groups.begin(); group!=track_groups.end(); group++)
1767             if (drummaps_almost_equal(((MidiTrack*)*group->begin())->drummap(), ((MidiTrack*)*track)->drummap()))
1768             {
1769               group->insert(*track);
1770               inserted=true;
1771               break;
1772             }
1773           break;
1774 
1775         case DrumEdit::DONT_GROUP:
1776           inserted=false;
1777           break;
1778 
1779         default:
1780           printf("THIS SHOULD NEVER HAPPEN: group_mode() is invalid!\n");
1781           inserted=false;
1782       }
1783 
1784       if (!inserted)
1785       {
1786         QSet<Track*> temp;
1787         temp.insert(*track);
1788         track_groups.push_back(temp);
1789       }
1790     }
1791   }
1792 
1793   // from now, we assume that every track_group's entry only groups tracks with identical
1794   // drum maps, but not necessarily identical hide-lists together.
1795   QVector< std::pair<MidiTrack*,int> > ignore_order_entries;
1796   for (global_drum_ordering_t::iterator order_it=global_drum_ordering.begin(); order_it!=global_drum_ordering.end(); order_it++)
1797   {
1798     // if this entry should be ignored, ignore it.
1799     if (ignore_order_entries.contains(*order_it))
1800       continue;
1801 
1802     // look if we have order_it->first (the MidiTrack*) in any of our track groups
1803     QList< QSet<Track*> >::iterator group;
1804     for (group=track_groups.begin(); group!=track_groups.end(); group++)
1805       if (group->contains(order_it->first))
1806         break;
1807 
1808     if (group!=track_groups.end()) // we have
1809     {
1810       int pitch=order_it->second;
1811 
1812       bool mute=true;
1813       bool hidden=true;
1814 
1815       if (drumEditor->ignore_hide()) hidden=false;
1816 
1817       for (QSet<Track*>::iterator track=group->begin(); track!=group->end() && (mute || hidden); track++)
1818       {
1819         if (dynamic_cast<MidiTrack*>(*track)->drummap()[pitch].mute == false)
1820           mute=false;
1821         if (dynamic_cast<MidiTrack*>(*track)->drummap()[pitch].hide == false)
1822           hidden=false;
1823       }
1824 
1825       if (!hidden)
1826       {
1827         for (QSet<Track*>::iterator track=group->begin(); track!=group->end(); track++)
1828         {
1829           DrumMap* dm = &dynamic_cast<MidiTrack*>(*track)->drummap()[pitch];
1830           if (dm->mute != mute)
1831           {
1832             dm->mute=mute;
1833             need_update = true;
1834           }
1835         }
1836         instrument_map.append(instrument_number_mapping_t(*group, pitch));
1837       }
1838 
1839       for (QSet<Track*>::iterator track=group->begin(); track!=group->end(); track++)
1840         ignore_order_entries.append(std::pair<MidiTrack*,int>(dynamic_cast<MidiTrack*>(*track), pitch));
1841     }
1842     // else ignore it
1843   }
1844 
1845 
1846   // maybe delete and then populate ourDrumMap
1847 
1848   if (must_delete_our_drum_map && ourDrumMap!=nullptr)
1849     delete [] ourDrumMap;
1850 
1851   int size = instrument_map.size();
1852   ourDrumMap=new DrumMap[size];
1853   must_delete_our_drum_map=true;
1854 
1855   Track* t;
1856   MidiTrack* mt;
1857   int index;
1858   for (int i=0;i<size;i++)
1859   {
1860     t = *instrument_map[i].tracks.begin();
1861     if(!t->isMidiTrack())
1862       continue;
1863     mt = static_cast<MidiTrack*>(t);
1864     index = instrument_map[i].pitch;
1865     ourDrumMap[i] = mt->drummap()[index];
1866   }
1867 
1868   if (instrument_map!=old_instrument_map)
1869   {
1870     if (debugMsg) printf("rebuilt drummap and instrument map, size is now %i\n",size);
1871 
1872     songChanged(SC_EVENT_INSERTED); // force an update of the itemlist
1873     emit ourDrumMapChanged(true);
1874   }
1875   else
1876     emit ourDrumMapChanged(false);
1877 
1878   if (need_update)
1879     MusEGlobal::song->update(SC_DRUMMAP, true); // i know, this causes a recursion, which possibly
1880                                                 // isn't the most elegant solution here. but it will
1881                                                 // never be an infinite recursion
1882 }
1883 
mouseMove(QMouseEvent * event)1884 void DrumCanvas::mouseMove(QMouseEvent* event) {
1885 
1886     EventCanvas::mouseMove(event);
1887 
1888     if (MusEGlobal::config.showNoteTooltips)
1889         showNoteTooltip(event);
1890 
1891     if (MusEGlobal::config.showStatusBar)
1892         showStatusTip(event);
1893 }
1894 
showNoteTooltip(QMouseEvent * event)1895 void DrumCanvas::showNoteTooltip(QMouseEvent* event) {
1896 
1897     static CItem* hoverItem = nullptr;
1898 
1899     if (!(_tool & (MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool | MusEGui::CursorTool)))
1900         return;
1901 
1902     CItem* item = findCurrentItem(event->pos());
1903     if (item && hoverItem == item)
1904         return;
1905 
1906     // careful, drum list can be hidden/reduced by various display options
1907     // int pitch = drumEditor->get_instrument_map()[y2pitch(event->pos().y())].pitch;
1908     const auto& imap = drumEditor->get_instrument_map();
1909     if (imap.isEmpty())
1910         return;
1911 
1912     const int ipitch = y2pitch(event->pos().y());
1913     if (ipitch < 0 || ipitch >= imap.size())
1914         return;
1915 
1916     const int pitch = imap.at(ipitch).pitch;
1917 
1918     QString str;
1919     if (track()->drummap()[pitch].name.isEmpty())
1920         str = MusECore::pitch2string(pitch) + " (" + QString::number(pitch) + ")";
1921     else
1922         str = track()->drummap()[pitch].name + " (" + MusECore::pitch2string(pitch)
1923                 + "/" + QString::number(pitch) + ")";
1924 
1925     if (item) {
1926         hoverItem = item;
1927 
1928         MusECore::Pos start(item->event().tick() + item->part()->tick());
1929 
1930         int bar, beat, tick, hour, min, sec, msec;
1931 
1932         start.mbt(&bar, &beat, &tick);
1933         QString str_bar = QString("%1.%2.%3")
1934                 .arg(bar + 1,  4, 10, QLatin1Char('0'))
1935                 .arg(beat + 1, 2, 10, QLatin1Char('0'))
1936                 .arg(tick,     3, 10, QLatin1Char('0'));
1937 
1938         start.msmu(&hour, &min, &sec, &msec, nullptr);
1939         QString str_time = QString("%1:%2:%3.%4")
1940                 .arg(hour,  2, 10, QLatin1Char('0'))
1941                 .arg(min,   2, 10, QLatin1Char('0'))
1942                 .arg(sec,   2, 10, QLatin1Char('0'))
1943                 .arg(msec,  3, 10, QLatin1Char('0'));
1944 
1945         str = tr("Note: ") + str + "\n"
1946                     + tr("Velocity: ") + QString::number(item->event().velo()) + "\n"
1947                     + tr("Start (bar): ") +  str_bar + "\n"
1948                     + tr("Start (time): ") + str_time;
1949 
1950     } else {
1951         hoverItem = nullptr;
1952     }
1953 
1954     QToolTip::showText(QPoint(event->globalX(), event->globalY() + 10), str);
1955 }
1956 
showStatusTip(QMouseEvent * event)1957 void DrumCanvas::showStatusTip(QMouseEvent* event) {
1958 
1959     static CItem* hoverItem = nullptr;
1960     static Tool localTool;
1961 
1962     CItem* item = findCurrentItem(event->pos());
1963     if (item) {
1964         if (hoverItem == item && localTool == _tool)
1965             return;
1966 
1967         hoverItem = item;
1968         localTool = _tool;
1969 
1970         QString s;
1971         if (_tool & (MusEGui::PointerTool ))
1972             s = tr("LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete");
1973         else if (_tool & (MusEGui::PencilTool))
1974             s = tr("LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete");
1975         else if (_tool & (MusEGui::RubberTool))
1976             s = tr("LMB: Delete");
1977         else if (_tool & (MusEGui::CursorTool))
1978             s = tr("Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete.");
1979 
1980         if (!s.isEmpty())
1981             MusEGlobal::muse->setStatusBarText(s);
1982     } else {
1983         if (hoverItem != nullptr) {
1984             MusEGlobal::muse->clearStatusBarText();
1985             hoverItem = nullptr;
1986         }
1987     }
1988 }
1989 
setCursor()1990 void DrumCanvas::setCursor()
1991 {
1992     // Avoid duplication, just do it below.
1993     //showCursor();
1994 
1995     switch (drag) {
1996 
1997     case DRAGX_MOVE:
1998     case DRAGX_COPY:
1999     case DRAGX_CLONE:
2000         // Make sure to do this.
2001         showCursor();
2002         QWidget::setCursor(*pencilMoveHorizCursor);
2003         break;
2004 
2005     case DRAGY_MOVE:
2006     case DRAGY_COPY:
2007     case DRAGY_CLONE:
2008         // Make sure to do this.
2009         showCursor();
2010         QWidget::setCursor(*pencilMoveVertCursor);
2011         break;
2012 
2013     case DRAG_MOVE:
2014     case DRAG_COPY:
2015     case DRAG_CLONE:
2016         // Make sure to do this.
2017         showCursor();
2018         QWidget::setCursor(*pencilMove4WayCursor);
2019         break;
2020 
2021     case DRAG_RESIZE:
2022         // Make sure to do this.
2023         showCursor();
2024         QWidget::setCursor(*pencilMoveHorizCursor);
2025         break;
2026 
2027     default:
2028         // Let the Canvas handle it, and call showCursor().
2029         Canvas::setCursor();
2030         break;
2031     }
2032 }
2033 
2034 //---------------------------------------------------------
2035 //   setMouseOverItemCursor
2036 //---------------------------------------------------------
2037 
setMouseOverItemCursor()2038 void DrumCanvas::setMouseOverItemCursor()
2039 {
2040   //showCursor();
2041   QWidget::setCursor(*pencilMove4WayCursor);
2042 }
2043 
2044 
2045 } // namespace MusEGui
2046