1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //    $Id: canvas.cpp,v 1.10.2.17 2009/05/03 04:14:01 terminator356 Exp $
5 //  (C) Copyright 1999 Werner Schweer (ws@seh.de)
6 //  Additions, modifications (C) Copyright 2011-2013 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 <stdio.h>
25 #include "muse_math.h"
26 
27 #include "canvas.h"
28 
29 #include <QApplication>
30 #include <QDesktopWidget>
31 #include <QCursor>
32 #include <QScreen>
33 
34 #include <vector>
35 
36 #include "gconfig.h"
37 #include "song.h"
38 #include "event.h"
39 #include "icons.h"
40 #include "../marker/marker.h"
41 #include "part.h"
42 #include "fastlog.h"
43 #include "menutitleitem.h"
44 #include "shortcuts.h"
45 #include "helper.h"
46 #include "globals.h"
47 #include "app.h"
48 
49 // Forwards from header:
50 #include <QPainter>
51 #include <QMenu>
52 #include <QTimer>
53 #include <QWheelEvent>
54 #include <QMouseEvent>
55 #include <QKeyEvent>
56 #include "undo.h"
57 
58 #define ABS(x)  ((x) < 0) ? -(x) : (x)
59 
60 namespace MusEGui {
61 
62 //---------------------------------------------------------
63 //   Canvas
64 //---------------------------------------------------------
65 
Canvas(QWidget * parent,int sx,int sy,const char * name)66 Canvas::Canvas(QWidget* parent, int sx, int sy, const char* name)
67    : View(parent, sx, sy, name)
68       {
69       _cursorOverrideCount = 0;
70       canvasTools = 0;
71       itemPopupMenu = nullptr;
72 
73       button = Qt::NoButton;
74       keyState = Qt::NoModifier;
75       _mouseGrabbed = false;
76 
77       canScrollLeft = true;
78       canScrollRight = true;
79       canScrollUp = true;
80       canScrollDown = true;
81       hscrollDir = HSCROLL_NONE;
82       vscrollDir = VSCROLL_NONE;
83       scrollTimer=nullptr;
84       ignore_mouse_move = false;
85       resizeDirection= MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT;
86 
87       supportsResizeToTheLeft = false;
88       supportsMultipleResize = false;
89 
90       scrollSpeed=30;    // hardcoded scroll jump
91 
92       drag    = DRAG_OFF;
93       _tool   = PointerTool;
94       pos[0]  = MusEGlobal::song->cpos();
95       pos[1]  = MusEGlobal::song->lpos();
96       pos[2]  = MusEGlobal::song->rpos();
97       curPart = nullptr;
98       curPartId = -1;
99       curItem = nullptr;
100       newCItem = nullptr;
101       connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(setPos(int, unsigned, bool)));
102       }
103 
~Canvas()104 Canvas::~Canvas()
105 {
106   // Just in case the ref count is not 0. This is our last chance to clear
107   //  our contribution to QApplication::setOverrideCursor references.
108   showCursor();
109 
110   items.clearDelete();
111 
112   if(newCItem)
113   {
114     if(newCItem->event().empty() && newCItem->part()) // Was it a new part, with no event?
115       delete newCItem->part();
116     delete newCItem;
117   }
118 }
119 
120 //---------------------------------------------------------
121 //   lassoToRegion
122 //   static
123 //---------------------------------------------------------
124 
lassoToRegion(const QRect & r_in,QRegion & rg_out) const125 void Canvas::lassoToRegion(const QRect& r_in, QRegion& rg_out) const
126 {
127   const QRect mr = map(r_in);
128   const int x = mr.x();
129   const int y = mr.y();
130   const int w = mr.width();
131   const int h = mr.height();
132 
133   const int x_line_off = 0;
134   const int y_line_off = 0;
135   const int line_w = 1;
136   const int line_h = 1;
137 
138   // Clear the given region.
139   rg_out = QRegion();
140   // Top line.
141   rg_out += QRect(x, y - y_line_off, w + line_w, line_h);
142   // Right line.
143   rg_out += QRect(x + w - x_line_off, y, line_w, h + line_h);
144   // Bottom line.
145   rg_out += QRect(x, y + h - y_line_off, w + line_w, line_h);
146   // Left line.
147   rg_out += QRect(x - x_line_off, y, line_w, h + line_h);
148 
149 // For testing...
150 //       const QVector<QRect> rects = rg_out.rects();
151 //       const int rg_sz = rects.size();
152 //       fprintf(stderr, "Region rect count:%d\n", rg_sz);
153 //       int rg_r_cnt = 0;
154 //       fprintf(stderr, "Region rect count:%d\n", rg_sz);
155 //       for(int i = 0; i < rg_sz; ++i, ++rg_r_cnt)
156 //       {
157 //         const QRect& rg_r = rects.at(i);
158 //         fprintf(stderr, "  #%d: x:%d y:%d w:%d h:%d\n", rg_r_cnt, rg_r.x(), rg_r.y(), rg_r.width(), rg_r.height());
159 //       }
160 }
161 
showCursor(bool show)162 void Canvas::showCursor(bool show)
163 {
164   if(_cursorOverrideCount > 1)
165     fprintf(stderr, "MusE Warning: _cursorOverrideCount > 1 in Canvas::showCursor(%d)\n", show);
166 
167   if(show)
168   {
169     while(_cursorOverrideCount > 0)
170     {
171       QApplication::restoreOverrideCursor();
172       _cursorOverrideCount--;
173     }
174   }
175   else
176   {
177     _cursorOverrideCount++;
178     QApplication::setOverrideCursor(Qt::BlankCursor); // CAUTION
179   }
180 }
181 
setMouseGrab(bool grabbed)182 void Canvas::setMouseGrab(bool grabbed)
183 {
184   if(grabbed && !_mouseGrabbed)
185   {
186     _mouseGrabbed = true;
187     grabMouse(); // CAUTION
188   }
189   else if(!grabbed && _mouseGrabbed)
190   {
191     releaseMouse();
192     _mouseGrabbed = false;
193   }
194 }
195 
196 //---------------------------------------------------------
197 //   setPos
198 //    set one of three markers
199 //    idx   - 0-cpos  1-lpos  2-rpos
200 //    flag  - emit followEvent()
201 //---------------------------------------------------------
202 
setPos(int idx,unsigned val,bool adjustScrollbar)203 void Canvas::setPos(int idx, unsigned val, bool adjustScrollbar)
204       {
205       //if (pos[idx] == val) // Seems to be some refresh problems here, pos[idx] might be val but the gui not updated.
206       //    return;          // skipping this return forces update even if values match. Matching values only seem
207                              // to occur when initializing
208       int opos = mapx(pos[idx]);
209       int npos = mapx(val);
210 
211       if (adjustScrollbar && idx == 0) {
212             switch (MusEGlobal::song->follow()) {
213                   case  MusECore::Song::NO:
214                         break;
215                   case MusECore::Song::JUMP:
216                         if (npos >= width()) {
217                               int ppos =  val - xorg - rmapxDev(width()/8);
218                               if (ppos < 0)
219                                     ppos = 0;
220                               emit followEvent(ppos);
221                               opos = mapx(pos[idx]);
222                               npos = mapx(val);
223                               }
224                         else if (npos < 0) {
225                               int ppos =  val - xorg - rmapxDev(width()*3/4);
226                               if (ppos < 0)
227                                     ppos = 0;
228                               emit followEvent(ppos);
229                               opos = mapx(pos[idx]);
230                               npos = mapx(val);
231                               }
232                         break;
233                   case MusECore::Song::CONTINUOUS:
234                         if (npos > (width()/2)) {
235                               int ppos =  pos[idx] - xorg - rmapxDev(width()/2);
236                               if (ppos < 0)
237                                     ppos = 0;
238                               emit followEvent(ppos);
239                               opos = mapx(pos[idx]);
240                               npos = mapx(val);
241                               }
242                         else if (npos < (width()/2)) {
243                               int ppos =  pos[idx] - xorg - rmapxDev(width()/2);
244                               if (ppos < 0)
245                                     ppos = 0;
246                               emit followEvent(ppos);
247                               opos = mapx(pos[idx]);
248                               npos = mapx(val);
249                               }
250                         break;
251                   }
252             }
253 
254       int x;
255       int w = 1;
256       if (opos > npos) {
257             w += opos - npos;
258             x = npos;
259             }
260       else {
261             w += npos - opos;
262             x = opos;
263             }
264       pos[idx] = val;
265       redraw(QRect(x-1, 0, w+2, height()));
266       }
267 
268 //---------------------------------------------------------
269 //   drawMarkers
270 //---------------------------------------------------------
271 
drawMarkers(QPainter & p,const QRect & mr,const QRegion &)272 void Canvas::drawMarkers(QPainter& p, const QRect& mr, const QRegion&)
273 {
274       const int mx = mr.x();
275       const int my = mr.y();
276       const int mw = mr.width();
277       const int mh = mr.height();
278       const int my_2 = my + mh;
279 
280       const ViewXCoordinate vx(mx, true);
281       const ViewWCoordinate vw(mw, true);
282       const ViewXCoordinate vx_2(mx + mw, true);
283 
284       QPen pen;
285       pen.setCosmetic(true);
286 
287       MusECore::MarkerList* marker = MusEGlobal::song->marker();
288       pen.setColor(MusEGlobal::config.markerColor);
289       p.setPen(pen);
290       for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) {
291             const ViewXCoordinate xp(m->second.tick(), false);
292             if (isXInRange(xp, vx, vx_2)) {
293                   const int mxp = asMapped(xp)._value;
294                   p.drawLine(mxp, my, mxp, my_2);
295                   }
296             }
297 }
298 
299 //---------------------------------------------------------
300 //   draw
301 //---------------------------------------------------------
302 
draw(QPainter & p,const QRect & mr,const QRegion & mrg)303 void Canvas::draw(QPainter& p, const QRect& mr, const QRegion& mrg)
304 {
305 //      printf("draw canvas %x virt %d\n", this, virt());
306 
307 // For testing...
308 //       const QVector<QRect> rects = mrg.rects();
309 //       const int rg_sz = rects.size();
310 //       fprintf(stderr, "Canvas::draw: virt:%d rect: x:%d y:%d w:%d h:%d region rect count:%d\n",
311 //               virt(), mr.x(), mr.y(), mr.width(), mr.height(), rg_sz);
312 //       int rg_r_cnt = 0;
313 //       for(int i = 0; i < rg_sz; ++i, ++rg_r_cnt)
314 //       {
315 //         const QRect& rg_r = rects.at(i);
316 //         fprintf(stderr, "  #%d: x:%d y:%d w:%d h:%d\n", rg_r_cnt, rg_r.x(), rg_r.y(), rg_r.width(), rg_r.height());
317 //       }
318 
319       const int mx = mr.x();
320       const int my = mr.y();
321       const int mw = mr.width();
322       const int mh = mr.height();
323       const int mx_2 = mx + mw;
324       const int my_2 = my + mh;
325 
326       const ViewXCoordinate vx(mx, true);
327       const ViewWCoordinate vw(mw, true);
328       const ViewXCoordinate vx_2(mx + mw, true);
329 
330       int ux_2lim = mapxDev(mx_2);
331       if(ux_2lim <= 0)
332         //x2_lim = 1;
333         ux_2lim = 0;
334 
335       // Force it to +1 so that all the left-most items, or items immediately to the right,
336       //  get a chance to update - this is important since for example the parts on the
337       //  part canvas want to draw a two-pixel wide border which for an item at position x=0
338       //  actually begins at x=-1 and needs to include that small adjustment during updates...
339       ux_2lim += rmapxDev(1);
340 
341       std::vector<CItem*> list1;
342       std::vector<CItem*> list2;
343       std::vector<CItem*> list4;
344 
345       if (virt()) {
346             drawCanvas(p, mr, mrg);
347 
348             //---------------------------------------------------
349             // draw Canvas Items
350             //---------------------------------------------------
351 
352             // ... and we want the upper bound, not lower bound, so that items immediately
353             //  to the right can be updated.
354             iCItem to(items.upper_bound(ux_2lim));
355 
356 // For testing...
357 //             fprintf(stderr, "Canvas::draw: virt:%d x2:%d ux2_lim:%d\n", virt(), mx_2, ux_2lim);
358 //             if(to == items.end())
359 //               fprintf(stderr, "...item not found\n");
360 //             else
361 //               fprintf(stderr, "...item found\n");
362 
363             int ii = 0;
364             for(iCItem i = items.begin(); i != to; ++i, ++ii)
365             {
366               CItem* ci = i->second;
367               // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents().
368               // Not required for now.
369               //ci->part()->hasHiddenEvents();
370 
371 // For testing...
372 //               fprintf(stderr, "...item:%d bbox x:%d y:%d w:%d h:%d\n", ii, ci->bbox().x(), ci->bbox().y(), ci->bbox().width(), ci->bbox().height());
373 
374               // Draw items from other parts behind all others.
375               // Only for items with events (not arranger parts).
376               if(!ci->event().empty() && ci->part() != curPart)
377                 list1.push_back(ci);
378               else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart))
379               {
380                 // Draw selected parts in front of all others.
381                 if(ci->isSelected())
382                   list4.push_back(ci);
383                 else
384                   // Draw unselected parts.
385                   list2.push_back(ci);
386               }
387             }
388 
389             // Draw non-current part backgrounds behind all others:
390             drawParts(p, false, mr, mrg);
391 
392             int i;
393             int sz = list1.size();
394             for(i = 0; i != sz; ++i)
395               drawItem(p, list1[i], mr, mrg);
396 
397             // Draw current part background in front of all others:
398             drawParts(p, true, mr, mrg);
399 
400             sz = list2.size();
401             for(i = 0; i != sz; ++i)
402               drawItem(p, list2[i], mr, mrg);
403             sz = list4.size();
404             for(i = 0; i != sz; ++i)
405               drawItem(p, list4[i], mr, mrg);
406 
407             // Draw items being moved, a special way in their original location.
408             to = moving.lower_bound(ux_2lim);
409             for (iCItem i = moving.begin(); i != to; ++i)
410                   drawItem(p, i->second, mr, mrg);
411 
412             // Draw special top item for new recordings etc.
413             drawTopItem(p,mr, mrg);
414 
415             // Draw special new item for first-time placement.
416             // It is not in the item list yet. It will be added when mouse released.
417             if(newCItem)
418               drawItem(p, newCItem, mr, mrg);
419       }
420       else {
421             p.save();
422             setPainter(p);
423 
424             drawCanvas(p, mr, mrg);
425             p.restore();
426 
427             //---------------------------------------------------
428             // draw Canvas Items
429             //---------------------------------------------------
430 
431             for(iCItem i = items.begin(); i != items.end(); ++i)
432             {
433               CItem* ci = i->second;
434               // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents().
435               // Not required for now.
436               //ci->part()->hasHiddenEvents();
437 
438               // Draw items from other parts behind all others.
439               // Only for items with events (not arranger parts).
440               if(!ci->event().empty() && ci->part() != curPart)
441                 list1.push_back(ci);
442               else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart))
443               {
444                 // Draw selected parts in front of all others.
445                 if(ci->isSelected())
446                   list4.push_back(ci);
447                 else
448                   // Draw unselected parts.
449                   list2.push_back(ci);
450               }
451             }
452 
453             // Draw non-current part backgrounds behind all others:
454             drawParts(p, false, mr, mrg);
455 
456             int i;
457             int sz = list1.size();
458             for(i = 0; i != sz; ++i)
459               drawItem(p, list1[i], mr, mrg);
460 
461             // Draw current part background in front of all others:
462             drawParts(p, true, mr, mrg);
463 
464             sz = list2.size();
465             for(i = 0; i != sz; ++i)
466               drawItem(p, list2[i], mr, mrg);
467             sz = list4.size();
468             for(i = 0; i != sz; ++i)
469               drawItem(p, list4[i], mr, mrg);
470 
471             // Draw items being moved, a special way in their original location.
472             for (iCItem i = moving.begin(); i != moving.end(); ++i)
473                         drawItem(p, i->second, mr, mrg);
474 
475             // Draw special top item for new recordings etc.
476             drawTopItem(p, mr, mrg);
477 
478             // Draw special new item for first-time placement.
479             // It is not in the item list yet. It will be added when mouse released.
480             if(newCItem)
481               drawItem(p, newCItem, mr, mrg);
482 
483             p.save();
484             setPainter(p);
485       }
486 
487 
488       QPen pen;
489       pen.setCosmetic(true);
490 
491       //---------------------------------------------------
492       //    draw marker
493       //---------------------------------------------------
494 
495       //p.save();
496       bool wmtxen = p.worldMatrixEnabled();
497       p.setWorldMatrixEnabled(false);
498 
499       drawMarkers(p, mr, mrg);
500 
501 
502       //---------------------------------------------------
503       //    draw location marker
504       //---------------------------------------------------
505 
506       pen.setColor(MusEGlobal::config.rangeMarkerColor);
507       p.setPen(pen);
508       int mlx;
509       ViewXCoordinate lxp0(pos[0], false);
510       ViewXCoordinate lxp1(pos[1], false);
511       ViewXCoordinate lxp2(pos[2], false);
512       if (isXInRange(lxp1, vx, vx_2)) {
513             mlx = asMapped(lxp1)._value;
514             p.drawLine(mlx, my, mlx, my_2);
515             }
516       if (isXInRange(lxp2, vx, vx_2)) {
517             mlx = asMapped(lxp2)._value;
518             p.drawLine(mlx, my, mlx, my_2);
519             }
520       // Draw the red main position cursor last, on top of the others.
521       pen.setColor(MusEGlobal::config.positionMarkerColor);
522       p.setPen(pen);
523 
524 // For testing...
525 //       fprintf(stderr, "...location marker: pos[0]%d x:%d x2:%d\n",
526 //               pos[0], x, x2);
527 
528       if (isXInRange(lxp0, vx, vx_2)) {
529             mlx = asMapped(lxp0)._value;
530 
531 // For testing...
532 //             fprintf(stderr, "...location marker in range. Drawing line at mx:%d my:%d mx:%d my2:%d\n",
533 //                 mx, my, mx, my2);
534 
535             p.drawLine(mlx, my, mlx, my_2);
536             }
537 
538       if(drag == DRAG_ZOOM)
539         p.drawPixmap(mapFromGlobal(global_start), zoomAtIconSVG->pixmap(QSize(MusEGlobal::config.cursorSize, MusEGlobal::config.cursorSize)));
540 
541       //p.restore();
542       //p.setWorldMatrixEnabled(true);
543       p.setWorldMatrixEnabled(wmtxen);
544 
545       //---------------------------------------------------
546       //    draw lasso
547       //---------------------------------------------------
548 
549       if (drag == DRAG_LASSO) {
550             p.setWorldMatrixEnabled(false);
551             pen.setColor(Qt::blue);
552             p.setPen(pen);
553             p.setBrush(Qt::NoBrush);
554             QRect _r(map(lasso));
555             p.drawRect(_r);
556             p.setWorldMatrixEnabled(wmtxen);
557             }
558 
559       //---------------------------------------------------
560       //    draw outlines of potential drop places of moving items
561       //---------------------------------------------------
562 
563       if(virt())
564       {
565         for(iCItem i = moving.begin(); i != moving.end(); ++i)
566           drawMoving(p, i->second, mr, mrg);
567       }
568       else
569       {
570         p.restore();
571         for(iCItem i = moving.begin(); i != moving.end(); ++i)
572           drawMoving(p, i->second, mr, mrg);
573         setPainter(p);
574       }
575 }
576 
577 #define HR_WHEEL_STEPSIZE 2
578 #define WHEEL_STEPSIZE 50
579 //#define WHEEL_DELTA   120
580 
581 //---------------------------------------------------------
582 //   wheelEvent
583 //---------------------------------------------------------
wheelEvent(QWheelEvent * ev)584 void Canvas::wheelEvent(QWheelEvent* ev)
585 {
586     // NOTE: X allows for Alt key + wheel, which changes from vertical
587     //        wheel values to horizontal values! Works in any window.
588 
589     int keyState = ev->modifiers();
590 
591     QPoint delta       = ev->pixelDelta();  // WHEEL_DELTA;
592 
593     int wheel_step_sz = 0;
594     if(delta.isNull())
595     {
596       delta = ev->angleDelta() / 8;
597       if(delta.isNull())
598         return;
599       delta /= 15;
600       wheel_step_sz = WHEEL_STEPSIZE;
601     }
602     else
603     {
604       delta /= 2;
605       wheel_step_sz = HR_WHEEL_STEPSIZE;
606     }
607 
608     bool shift      = keyState & Qt::ShiftModifier;
609     bool ctrl       = keyState & Qt::ControlModifier;
610 
611     if (ctrl) {  // zoom horizontally
612 
613       int d = 0;
614       if(delta.x() != 0)
615         d = delta.x();
616       else if(delta.y() != 0)
617         d = delta.y();
618       if(d != 0)
619 #if QT_VERSION >= 0x050e00
620         emit horizontalZoom(d > 0, ev->globalPosition().toPoint());
621 #else
622         emit horizontalZoom(d > 0, ev->globalPos());
623 #endif
624       return;
625     }
626 
627     if (shift  || delta.x() != 0) { // scroll horizontally
628 
629         int scrolldelta = - delta.x();
630         if (shift) {
631           scrolldelta = - delta.y();
632         }
633 
634         int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1));
635         if (xpixelscale <= 0) {
636           xpixelscale = 1;
637         }
638         int scrollstep = wheel_step_sz * (scrolldelta);
639         scrollstep = scrollstep / 10;
640         int newXpos = xpos + xpixelscale * scrollstep;
641 
642         if (newXpos < 0) {
643           newXpos = 0;
644         }
645 
646         emit horizontalScroll((unsigned)newXpos);
647 
648     }
649 
650     if (!shift && delta.y() != 0) { // scroll vertically
651 
652         int scrolldelta = delta.y();
653         int ypixelscale = rmapyDev(1);
654 
655         if (ypixelscale <= 0)
656               ypixelscale = 1;
657 
658         int scrollstep = wheel_step_sz * (-scrolldelta);
659         scrollstep = scrollstep / 2;
660         int newYpos = ypos + ypixelscale * scrollstep;
661 
662         if (newYpos < 0)
663               newYpos = 0;
664 
665         emit verticalScroll((unsigned)newYpos);
666     }
667 }
668 
redirectedWheelEvent(QWheelEvent * ev)669 void Canvas::redirectedWheelEvent(QWheelEvent* ev)
670       {
671       wheelEvent(ev);
672       }
673 
674 //---------------------------------------------------------
675 //   deselectAll
676 //---------------------------------------------------------
677 
deselectAll()678 void Canvas::deselectAll()
679       {
680       for (iCItem i = items.begin(); i != items.end(); ++i)
681             i->second->setSelected(false);
682       }
683 
684 //---------------------------------------------------------
685 //   selectItem
686 //---------------------------------------------------------
687 
selectItem(CItem * e,bool flag)688 void Canvas::selectItem(CItem* e, bool flag)
689       {
690       e->setSelected(flag);
691       }
692 
693 //---------------------------------------------------------
694 //   startMoving
695 //    copy selection-List to moving-List
696 //---------------------------------------------------------
697 
startMoving(const QPoint & pos,int dir,DragType,bool rasterize)698 void Canvas::startMoving(const QPoint& pos, int dir, DragType, bool rasterize)
699       {
700       for (iCItem i = items.begin(); i != items.end(); ++i) {
701             if (i->second->isSelected()) {
702                   i->second->setMoving(true);
703                   // Give the moving point an initial value.
704                   i->second->setMp(i->second->pos());
705                   moving.add(i->second);
706                   }
707             }
708       moveItems(pos, dir, rasterize);
709       }
710 
711 //---------------------------------------------------------
712 //   moveItems
713 //    dir = 0     move in all directions
714 //          1     move only horizontal
715 //          2     move only vertical
716 //---------------------------------------------------------
717 
moveItems(const QPoint & pos,int dir,bool rasterize)718 void Canvas::moveItems(const QPoint& pos, int dir, bool rasterize)
719       {
720       int dp = y2pitch(pos.y()) - y2pitch(start.y());
721       int dx = pos.x() - start.x();
722       if (dir == 1)
723             dp = 0;
724       else if (dir == 2)
725             dx = 0;
726       QPoint cur_item_mp, mp, cur_item_old_mp, old_mp;
727       CItem* item;
728       int x, y, nx, ny;
729 
730       // Inform the classes that an item is about to be moved.
731       //
732       // Simply for consistency with the code below, inform of the current item first.
733       if(curItem)
734       {
735         x = curItem->pos().x();
736         y = curItem->pos().y();
737         nx = x + dx;
738         ny = pitch2y(y2pitch(y) + dp);
739         if(rasterize)
740           cur_item_mp = raster(QPoint(nx, ny));
741         else
742           cur_item_mp = QPoint(nx, ny);
743 
744         cur_item_old_mp = curItem->mp();
745         if (cur_item_old_mp != cur_item_mp) {
746               itemMoving(curItem, cur_item_mp);
747               }
748       }
749       // Now inform of all the other items except the current item.
750       for (iCItem i = moving.begin(); i != moving.end(); ++i) {
751             item = i->second;
752             if(item == curItem)
753               continue;
754             x = item->pos().x();
755             y = item->pos().y();
756             nx = x + dx;
757             ny = pitch2y(y2pitch(y) + dp);
758             if(rasterize)
759               mp = raster(QPoint(nx, ny));
760             else
761               mp = QPoint(nx, ny);
762 
763             old_mp = i->second->mp();
764             if (old_mp != mp) {
765                   itemMoving(i->second, mp);
766                   }
767             }
768 
769       // Move the current item first since other item movements (sounds)
770       //  may depend on it being already moved (chords).
771       if(curItem)
772       {
773         x = curItem->pos().x();
774         y = curItem->pos().y();
775         nx = x + dx;
776         ny = pitch2y(y2pitch(y) + dp);
777         if(rasterize)
778           mp = raster(QPoint(nx, ny));
779         else
780           mp = QPoint(nx, ny);
781 
782         old_mp = curItem->mp();
783         if (old_mp != mp) {
784               curItem->setMp(mp);
785               itemMoved(curItem, old_mp);
786               }
787       }
788       // Now move all the other items except the current item.
789       for (iCItem i = moving.begin(); i != moving.end(); ++i) {
790             item = i->second;
791             if(item == curItem)
792               continue;
793             int x = item->pos().x();
794             int y = item->pos().y();
795             int nx = x + dx;
796             int ny;
797             ny = pitch2y(y2pitch(y) + dp);
798             if(rasterize)
799               mp = raster(QPoint(nx, ny));
800             else
801               mp = QPoint(nx, ny);
802 
803             old_mp = i->second->mp();
804             if (old_mp != mp) {
805                   i->second->setMp(mp);
806                   itemMoved(i->second, old_mp);
807                   }
808             }
809       redraw();
810       }
811 
812 //---------------------------------------------------------
813 //   viewKeyPressEvent
814 //---------------------------------------------------------
815 
viewKeyPressEvent(QKeyEvent * event)816 void Canvas::viewKeyPressEvent(QKeyEvent* event)
817       {
818       keyPress(event);
819       }
820 
821 //---------------------------------------------------------
822 //   viewKeyReleaseEvent
823 //---------------------------------------------------------
824 
viewKeyReleaseEvent(QKeyEvent * event)825 void Canvas::viewKeyReleaseEvent(QKeyEvent* event)
826       {
827       keyRelease(event);
828       }
829 
830 //---------------------------------------------------------
831 //   viewMousePressEvent
832 //---------------------------------------------------------
833 
viewMousePressEvent(QMouseEvent * event)834 void Canvas::viewMousePressEvent(QMouseEvent* event)
835       {
836       if (!mousePress(event))
837       {
838           cancelMouseOps();
839           return;
840       }
841       keyState = event->modifiers();
842       button = event->button();
843       //printf("viewMousePressEvent buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button());
844 
845       // special events if right button is clicked while operations
846       // like moving or drawing lasso is performed.
847       if (event->buttons() & Qt::RightButton & ~(button)) {
848           //printf("viewMousePressEvent special buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button());
849           switch (drag) {
850               case DRAG_LASSO:
851                 drag = DRAG_OFF;
852                 setCursor();
853                 setMouseGrab(false);
854 
855                 redraw();
856                 return;
857               case DRAG_MOVE:
858                 drag = DRAG_OFF;
859                 setCursor();
860                 setMouseGrab(false);
861 
862                 endMoveItems (start, MOVE_MOVE, 0);
863                 return;
864               default:
865                 break;
866           }
867       }
868 
869       // ignore event if (another) button is already active:
870       if (event->buttons() ^ button) {
871             //printf("viewMousePressEvent ignoring buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button());
872             // Do nothing, even if the mouse is grabbed or we have a moving list.
873             return;
874             }
875 
876       // Cancel all previous mouse ops. Right now there should be no moving list and drag should be off etc.
877       // If there is, it's an error. It likely means we missed a mouseRelease event.
878       cancelMouseOps();
879 
880       bool alt        = keyState & Qt::AltModifier;
881       bool ctrl       = keyState & Qt::ControlModifier;
882       bool shift      = keyState & Qt::ShiftModifier;
883 
884       start           = event->pos();
885       ev_pos          = start;
886       global_start    = event->globalPos();
887       ev_global_pos   = global_start;
888 
889       curItem = findCurrentItem(start);
890 
891       if (curItem && (button == Qt::MidButton)) {
892           if (_tool == PointerTool || _tool == PencilTool || _tool == RubberTool) {
893             deleteItem(start); // changed from "start drag" to "delete" by flo93
894             drag = DRAG_DELETE;
895             setCursor();
896             }
897       }
898       else if (button == Qt::RightButton) {
899             if (curItem) {
900                   if (ctrl && virt() && (_tool == PointerTool || _tool == PencilTool || _tool == RubberTool)) {       // Non-virt width is meaningless, such as drums.
901                         drag = DRAG_OFF;
902                         setCursor();
903                         int dx = start.x() - curItem->x();
904                         curItem->setWidth(dx);
905                         start.setX(curItem->x());
906                         deselectAll();
907                         selectItem(curItem, true);
908                         itemSelectionsChanged(nullptr, true);
909                         resizeItem(curItem, shift, false);
910                         redraw();
911                         }
912                   else {
913                         itemPopupMenu = genItemPopup(curItem);
914                         if (itemPopupMenu) {
915                               QAction *act = itemPopupMenu->exec(QCursor::pos());
916                               if (act && act->data().isValid())
917                                     itemPopup(curItem, act->data().toInt(), start);
918                               delete itemPopupMenu;
919                               }
920                         }
921                   }
922             else {
923                   canvasPopupMenu = genCanvasPopup();
924                   if (canvasPopupMenu) {
925                         QAction *act = canvasPopupMenu->exec(QCursor::pos(), nullptr);
926                         if (act)
927                               canvasPopup(act->data().toInt());
928                         delete canvasPopupMenu;
929                         }
930                   }
931             }
932       else if (button == Qt::LeftButton) {
933             switch (_tool) {
934                   case PointerTool:
935                         if (curItem) {
936                               itemPressed(curItem);
937                               // Alt alone is usually reserved for moving a window in X11. Ignore shift + alt.
938                               if (ctrl && !alt)
939                                     drag = DRAG_COPY_START;
940                               else if (ctrl && alt)
941                                     drag = DRAG_CLONE_START;
942                               else if (!ctrl && !alt)
943                                     drag = DRAG_MOVE_START;
944                               }
945                         else
946                               drag = DRAG_LASSO_START;
947                         setCursor();
948                         setMouseGrab(true); // CAUTION
949                         break;
950 
951                   case RubberTool:
952                         deleteItem(start);
953                         drag = DRAG_DELETE;
954                         setCursor();
955                         break;
956 
957                   case PencilTool:
958                   {
959                         bool deselect_all = false;
960                         if (curItem) {
961                                 if(!virt()) { // Non-virt width is meaningless, such as drums.
962                                   itemPressed(curItem);
963                                   // Alt alone is usually reserved for moving a window in X11. Ignore shift + alt.
964                                   if (ctrl && !alt)
965                                         drag = DRAG_COPY_START;
966                                   else if (ctrl && alt)
967                                         drag = DRAG_CLONE_START;
968                                   else if (!ctrl && !alt)
969                                         drag = DRAG_MOVE_START;
970                                   setCursor();
971                                   setMouseGrab(true); // CAUTION
972                                   break;
973 
974                                 } else if (supportsMultipleResize && ctrl) {
975                                     if (!shift) { //Select or deselect only the clicked item
976                                         selectItem(curItem, !(curItem->isSelected()));
977                                     } else { //Select or deselect all on the same pitch
978                                         bool selected = !(curItem->isSelected());
979                                         for (auto &it: items)
980                                             if (it.second->y() == curItem->y() )
981                                                 selectItem(it.second, selected);
982                                     }
983                                 } else {
984                                   drag = DRAG_RESIZE;
985                                   resizeDirection = MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT;
986                                   if(supportsResizeToTheLeft){
987                                      if(curItem->x() + (curItem->width() / 2) > ev_pos.x()){
988                                         resizeDirection = MusECore::ResizeDirection::RESIZE_TO_THE_LEFT;
989                                      }
990                                   }
991                                   setCursor();
992 
993                                   if (supportsMultipleResize) {
994                                       if (!curItem->isSelected()) {
995                                         deselectAll();
996                                         deselect_all = true;
997                                         selectItem(curItem, true);
998                                       }
999                                       if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT)
1000                                           resizeSelected(start.x() - curItem->x() - curItem->width());
1001                                       else
1002                                           resizeSelected(start.x() - curItem->x(), true);
1003                                   } else {
1004                                       if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) {
1005                                           int dx = start.x() - curItem->x();
1006                                           curItem->setWidth(dx);
1007                                       } else {
1008                                           int endX = curItem->x() + curItem->width();
1009                                           end = QPoint(endX, curItem->y());
1010                                           resizeToTheLeft(ev_pos);
1011                                       }
1012                                   }
1013 
1014                                   start = curItem->pos();
1015 
1016                                   if (!supportsMultipleResize) {
1017                                     deselectAll();
1018                                     deselect_all = true;
1019                                     selectItem(curItem, true);
1020                                   }
1021                                 }
1022                               }
1023                         else {
1024                               drag = DRAG_NEW;
1025                               setCursor();
1026                               curItem = newItem(start, keyState);
1027                               if (curItem)
1028                                     newCItem = curItem;
1029                               else {
1030                                     drag = DRAG_OFF;
1031                                     setCursor();
1032                                     }
1033                               deselectAll();
1034                               deselect_all = true;
1035                               // selectItem() will be called in viewMouseReleaseEvent().
1036                               }
1037                         itemSelectionsChanged(NULL, deselect_all);
1038                         redraw();
1039                   }
1040                   break;
1041 
1042                   case PanTool:
1043                         {
1044                           drag = DRAG_PAN;
1045                           setCursor();
1046                           if(MusEGlobal::config.borderlessMouse)
1047                           {
1048                             //  "It is almost never necessary to grab the mouse when using Qt, as Qt grabs
1049                             //   and releases it sensibly. In particular, Qt grabs the mouse when a mouse
1050                             //   button is pressed and keeps it until the last button is released."
1051                             //
1052                             // Apparently not. For some reason this was necessary. When the cursor is dragged
1053                             //  outside the window, holding left then pressing right mouse button COMPLETELY
1054                             //  bypasses us, leaving the app's default right-click handler to popup, and leaving
1055                             //  us in a really BAD state: mouse is grabbed (and hidden) and no way out !
1056                             //
1057                             // That is likely just how QWidget works, but here using global cursor overrides
1058                             //  it is disastrous. TESTED: Yes, that is how other controls work. Hitting another
1059                             //  button while the mouse has been dragged outside causes it to bypass us !
1060                             setMouseGrab(true); // CAUTION
1061 
1062                             QRect r = QApplication::primaryScreen()->geometry();
1063                             ignore_mouse_move = true;      // Avoid recursion.
1064                             QCursor::setPos( QPoint(r.width()/2, r.height()/2) );
1065                             //ignore_mouse_move = false;
1066                           }
1067                         }
1068                         break;
1069 
1070                   case ZoomTool:
1071                         {
1072                           drag = DRAG_ZOOM;
1073                           setCursor();
1074                           if(MusEGlobal::config.borderlessMouse)
1075                           {
1076                             setMouseGrab(true); // CAUTION
1077 
1078 // screenGeometry() is obsolete. Qt >= 5.6 ? use primaryScreen().
1079                             QRect r = QApplication::primaryScreen()->geometry();
1080                             ignore_mouse_move = true;      // Avoid recursion.
1081                             QCursor::setPos( QPoint(r.width()/2, r.height()/2) );
1082                             //ignore_mouse_move = false;
1083                           }
1084                           // Update the small zoom drawing area
1085                           QPoint pt = mapFromGlobal(global_start);
1086                           QSize cursize = zoomIconSVG->actualSize(QSize(MusEGlobal::config.cursorSize, MusEGlobal::config.cursorSize));
1087                           update(pt.x(), pt.y(), cursize.width(), cursize.height());
1088                         }
1089                         break;
1090 
1091                   default:
1092                         break;
1093                   }
1094             }
1095       }
1096 
scrollTimerDone()1097 void Canvas::scrollTimerDone()
1098 {
1099       //printf("Canvas::scrollTimerDone drag:%d doScroll:%d\n", drag, doScroll);
1100       if (doScroll && drag != DRAG_OFF && drag != DRAG_ZOOM)
1101       {
1102         //printf("Canvas::scrollTimerDone drag != DRAG_OFF && doScroll\n");
1103         int modifiers = QApplication::keyboardModifiers();
1104         bool ctrl  = modifiers & Qt::ControlModifier;
1105         bool meta  = modifiers & Qt::MetaModifier;
1106         bool alt   = modifiers & Qt::AltModifier;
1107         bool right_button = QApplication::mouseButtons() & Qt::RightButton;
1108         bool scrollDoResize = ((!ctrl && !right_button) || meta || alt) && virt();  // Non-virt width is meaningless, such as drums.
1109         int dx = 0;
1110         int dy = 0;
1111         bool doHMove = false;
1112         bool doVMove = false;
1113 
1114         // If lassoing, update the old lasso region.
1115         // Do it BEFORE scrolling.
1116         switch(drag)
1117         {
1118           case DRAG_LASSO:
1119                 // Update the old lasso region.
1120                 redraw(lassoRegion);
1121                 break;
1122 
1123           default:
1124                 break;
1125         }
1126 
1127         switch(hscrollDir)
1128         {
1129           case HSCROLL_RIGHT:
1130             switch(drag)
1131             {
1132               case DRAG_NEW:
1133               case DRAG_RESIZE:
1134               case DRAGX_MOVE:
1135               case DRAGX_COPY:
1136               case DRAGX_CLONE:
1137               case DRAGY_MOVE:
1138               case DRAGY_COPY:
1139               case DRAGY_CLONE:
1140               case DRAG_MOVE:
1141               case DRAG_COPY:
1142               case DRAG_CLONE:
1143               case DRAG_PAN:
1144                 emit horizontalScrollNoLimit(xpos + scrollSpeed);
1145                 canScrollLeft = true;
1146                 dx = rmapxDev(scrollSpeed);
1147                 ev_pos.setX(ev_pos.x() + dx);
1148                 doHMove = true;
1149               break;
1150               default:
1151                 if(canScrollRight)
1152                 {
1153                   int curxpos = xpos;
1154                   emit horizontalScroll(xpos + scrollSpeed);
1155                   if(xpos <= curxpos)
1156                     canScrollRight = false;
1157                   else
1158                   {
1159                     canScrollLeft = true;
1160                     dx = rmapxDev(scrollSpeed);
1161                     ev_pos.setX(ev_pos.x() + dx);
1162                     doHMove = true;
1163                   }
1164                 }
1165               break;
1166             }
1167           break;
1168           case HSCROLL_LEFT:
1169             if(canScrollLeft)
1170             {
1171               int curxpos = xpos;
1172               emit horizontalScroll(xpos - scrollSpeed);
1173               if(xpos >= curxpos)
1174                 canScrollLeft = false;
1175               else
1176               {
1177                 canScrollRight = true;
1178                 dx = -rmapxDev(scrollSpeed);
1179                 ev_pos.setX(ev_pos.x() + dx);
1180                 doHMove = true;
1181               }
1182             }
1183           break;
1184           default:
1185           break;
1186         }
1187         switch(vscrollDir)
1188         {
1189           case VSCROLL_DOWN:
1190             if(canScrollDown)
1191             {
1192               int curypos = ypos;
1193               emit verticalScroll(ypos + scrollSpeed);
1194               if(ypos <= curypos)
1195                 canScrollDown = false;
1196               else
1197               {
1198                 canScrollUp = true;
1199                 dy = rmapyDev(scrollSpeed);
1200                 ev_pos.setY(ev_pos.y() + dy);
1201                 doVMove = true;
1202               }
1203             }
1204           break;
1205           case VSCROLL_UP:
1206             if(canScrollUp)
1207             {
1208               int curypos = ypos;
1209               emit verticalScroll(ypos - scrollSpeed);
1210               if(ypos >= curypos)
1211                 canScrollUp = false;
1212               else
1213               {
1214                 canScrollDown = true;
1215                 dy = -rmapyDev(scrollSpeed);
1216                 ev_pos.setY(ev_pos.y() + dy);
1217                 doVMove = true;
1218               }
1219             }
1220           break;
1221           default:
1222           break;
1223         }
1224 
1225         //printf("Canvas::scrollTimerDone doHMove:%d doVMove:%d\n", doHMove, doVMove);
1226 
1227         if(!doHMove && !doVMove)
1228         {
1229           delete scrollTimer;
1230           scrollTimer=NULL;
1231           doScroll = false;
1232           return;
1233         }
1234         QPoint dist = ev_pos - start;
1235         switch(drag)
1236         {
1237           case DRAG_MOVE:
1238           case DRAG_COPY:
1239           case DRAG_CLONE:
1240                 moveItems(ev_pos, 0, false);
1241                 break;
1242           case DRAGX_MOVE:
1243           case DRAGX_COPY:
1244           case DRAGX_CLONE:
1245                 moveItems(ev_pos, 1, false);
1246                 break;
1247           case DRAGY_MOVE:
1248           case DRAGY_COPY:
1249           case DRAGY_CLONE:
1250                 moveItems(ev_pos, 2, false);
1251                 break;
1252           case DRAG_LASSO:
1253                 // Set the new lasso rectangle and compute the new lasso region.
1254                 setLasso(QRect(start.x(), start.y(), dist.x(), dist.y()));
1255                 // Update the new lasso region.
1256                 redraw(lassoRegion);
1257                 break;
1258 
1259           case DRAG_NEW:
1260                 if(newCItem)
1261                 {
1262                   if((doHMove && !scrollDoResize) || doVMove)
1263                   {
1264                     int nx = newCItem->x();
1265                     int ny = newCItem->y();
1266                     if(doHMove && !scrollDoResize)
1267                       nx += dx;
1268                     if(nx < 0)
1269                       nx = 0;
1270                     if(doVMove)
1271                       ny += dy;
1272                     if(ny < 0)
1273                       ny = 0;
1274                     const QPoint new_pos(nx, ny);
1275                     itemMoving(newCItem, new_pos);
1276                     newCItem->move(new_pos);
1277                     const QPoint old_mp = newCItem->mp();
1278                     // Even though we only move the primary position here,
1279                     //  set the mp as well so note sounding logic can work easier.
1280                     newCItem->setMp(newCItem->pos());
1281                     itemMoved(newCItem, old_mp);
1282                   }
1283                   if(scrollDoResize && doHMove)
1284                   {
1285                     int w = ev_pos.x() - newCItem->x();
1286                     if(w < 1)
1287                       w = 1;
1288                     newCItem->setWidth(w);
1289                   }
1290                   redraw();
1291                 }
1292                 break;
1293 
1294           case DRAG_RESIZE:
1295                 if (curItem && doHMove) {
1296                     if (supportsMultipleResize) {
1297                         if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT)
1298                             resizeSelected(ev_pos.x() - curItem->x() - curItem->width());
1299                         else
1300                             resizeSelected(ev_pos.x() - curItem->x(), true);
1301                     } else {
1302                         int w = ev_pos.x() - curItem->x();
1303                         if(w < 1)
1304                             w = 1;
1305                         curItem->setWidth(w);
1306                     }
1307                     redraw();
1308                 }
1309                 break;
1310           default:
1311                 break;
1312         }
1313         //printf("Canvas::scrollTimerDone starting scrollTimer: Currently active?%d\n", scrollTimer->isActive());
1314 
1315         // Make sure to yield to other events, otherwise other events take a long time to reach us,
1316         //  causing scrolling to take a painfully long time to stop. Try up to 100 ms for each yield:
1317         //qApp->processEvents(100);       // FIXME: Didn't help at all.
1318         scrollTimer->setSingleShot(true);
1319         scrollTimer->start(80);           // OK, setting a timeout 80 helped.
1320       }
1321       else
1322       {
1323           //printf("Canvas::scrollTimerDone !(drag != DRAG_OFF && doScroll) deleting scrollTimer\n");
1324           delete scrollTimer;
1325           scrollTimer=nullptr;
1326       }
1327 }
1328 
1329 //---------------------------------------------------------
1330 //   viewMouseMoveEvent
1331 //---------------------------------------------------------
1332 
viewMouseMoveEvent(QMouseEvent * event)1333 void Canvas::viewMouseMoveEvent(QMouseEvent* event)
1334       {
1335       if(ignore_mouse_move)
1336       {
1337         ignore_mouse_move = false;
1338         event->accept();
1339         return;
1340       }
1341 // For testing...
1342       //fprintf(stderr, "ypos=%d yorg=%d ymag=%d event->y=%d ->gy:%d mapy(yorg)=%d rmapy0=%d yOffset=%d rmapy(yOffset()=%d\n",
1343       //                 ypos,   yorg,   ymag,   event->y(), event->globalY(), mapy(yorg), rmapy(0), yOffset(), rmapy(yOffset()));
1344 
1345       // Drag not off and left mouse button not pressed? It's an error.
1346       // Meaning likely mouseRelease was not called (which CAN happen).
1347       if(drag != DRAG_OFF && !(event->buttons() & Qt::LeftButton))
1348       {
1349 // For testing...
1350 //         fprintf(stderr, "Canvas::viewMouseMoveEvent: calling cancelMouseOps()\n");
1351 
1352         // Be sure to cancel any relative stuff. Otherwise it's left in a bad state.
1353         cancelMouseOps();
1354       }
1355 
1356       QRect  screen_rect    = QApplication::primaryScreen()->geometry();
1357       QPoint screen_center  = QPoint(screen_rect.width()/2, screen_rect.height()/2);
1358       QPoint glob_dist      = event->globalPos() - ev_global_pos;
1359       QPoint glob_zoom_dist = MusEGlobal::config.borderlessMouse ? (event->globalPos() - screen_center) : glob_dist;
1360       QPoint last_dist      = event->pos() - ev_pos;
1361 
1362       ev_pos     = event->pos();
1363       QPoint dist  = ev_pos - start;
1364       int ax       = ABS(rmapx(dist.x()));
1365       int ay       = ABS(rmapy(dist.y()));
1366       bool isMoving  = (ax >= 2) || (ay > 2);
1367       Qt::KeyboardModifiers modifiers = event->modifiers();
1368       bool ctrl  = modifiers & Qt::ControlModifier;
1369       bool shift = modifiers & Qt::ShiftModifier;
1370       bool meta  = modifiers & Qt::MetaModifier;
1371       bool alt   = modifiers & Qt::AltModifier;
1372       bool right_button = event->buttons() & Qt::RightButton;
1373 
1374       // set scrolling variables: doScroll, scrollRight
1375       // No auto scroll in zoom mode or normal pan mode.
1376       if (drag != DRAG_OFF && drag != DRAG_ZOOM && (drag != DRAG_PAN || !MusEGlobal::config.borderlessMouse)) {
1377             int ex = rmapx(event->x())+mapx(0);
1378             if(ex < 15 && (canScrollLeft || drag == DRAG_PAN))
1379               hscrollDir = (drag == DRAG_PAN ? HSCROLL_RIGHT : HSCROLL_LEFT);
1380             else
1381             if(ex > (width() - 15))
1382               switch(drag)
1383               {
1384                 case DRAG_NEW:
1385                 case DRAG_RESIZE:
1386                 case DRAGX_MOVE:
1387                 case DRAGX_COPY:
1388                 case DRAGX_CLONE:
1389                 case DRAGY_MOVE:
1390                 case DRAGY_COPY:
1391                 case DRAGY_CLONE:
1392                 case DRAG_MOVE:
1393                 case DRAG_COPY:
1394                 case DRAG_CLONE:
1395                 case DRAG_PAN:
1396                     hscrollDir = (drag == DRAG_PAN ? HSCROLL_LEFT : HSCROLL_RIGHT);
1397                 break;
1398                 default:
1399                   if(canScrollRight)
1400                     hscrollDir = (drag == DRAG_PAN ? HSCROLL_LEFT : HSCROLL_RIGHT);
1401                   else
1402                     hscrollDir = HSCROLL_NONE;
1403                 break;
1404               }
1405             else
1406               hscrollDir = HSCROLL_NONE;
1407 
1408             int ey = rmapy(event->y())+mapy(0);
1409             if(ey < 15 && (canScrollUp || drag == DRAG_PAN))
1410               vscrollDir = (drag == DRAG_PAN ? VSCROLL_DOWN : VSCROLL_UP);
1411             else
1412             if(ey > (height() - 15) && (canScrollDown || drag == DRAG_PAN))
1413               vscrollDir = (drag == DRAG_PAN ? VSCROLL_UP : VSCROLL_DOWN);
1414             else
1415               vscrollDir = VSCROLL_NONE;
1416 
1417             if(hscrollDir != HSCROLL_NONE || vscrollDir != VSCROLL_NONE)
1418             {
1419               doScroll=true;
1420               if (!scrollTimer)
1421               {
1422                   scrollTimer= new QTimer(this);
1423                   connect( scrollTimer, SIGNAL(timeout()), SLOT(scrollTimerDone()) );
1424                   scrollTimer->setSingleShot(true); // single-shot timer
1425                   scrollTimer->start(0);
1426               }
1427             }
1428             else
1429                 doScroll=false;
1430 
1431           }
1432       else
1433       {
1434         doScroll=false;
1435 
1436         canScrollLeft = true;
1437         canScrollRight = true;
1438         canScrollUp = true;
1439         canScrollDown = true;
1440       }
1441 
1442       switch (drag) {
1443             case DRAG_LASSO_START:
1444                   if (!isMoving)
1445                         break;
1446                   drag = DRAG_LASSO;
1447                   setCursor();
1448                   // proceed with DRAG_LASSO:
1449                   // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4:
1450                   // FALLTHROUGH
1451             case DRAG_LASSO:
1452                   {
1453                   // Update the old lasso region.
1454                   redraw(lassoRegion);
1455 
1456                   // Set the new lasso rectangle and compute the new lasso region.
1457                   setLasso(QRect(start.x(), start.y(), dist.x(), dist.y()));
1458 
1459                   // printf("xorg=%d xmag=%d event->x=%d, mapx(xorg)=%d rmapx0=%d xOffset=%d rmapx(xOffset()=%d\n",
1460                   //         xorg, xmag, event->x(),mapx(xorg), rmapx(0), xOffset(),rmapx(xOffset()));
1461 
1462                   // Update the new lasso region.
1463                   redraw(lassoRegion);
1464                   }
1465                   break;
1466 
1467             case DRAG_MOVE_START:
1468             case DRAG_COPY_START:
1469             case DRAG_CLONE_START:
1470             {
1471                   if (!isMoving)
1472                         break;
1473                   int dir = 0;
1474                   if (keyState & Qt::ShiftModifier) {
1475                         if (ax > ay) {
1476                               if (drag == DRAG_MOVE_START)
1477                                     drag = DRAGX_MOVE;
1478                               else if (drag == DRAG_COPY_START)
1479                                     drag = DRAGX_COPY;
1480                               else
1481                                     drag = DRAGX_CLONE;
1482                               dir = 1;
1483                               }
1484                         else {
1485                               if (drag == DRAG_MOVE_START)
1486                                     drag = DRAGY_MOVE;
1487                               else if (drag == DRAG_COPY_START)
1488                                     drag = DRAGY_COPY;
1489                               else
1490                                     drag = DRAGY_CLONE;
1491                               dir = 2;
1492                               }
1493                         }
1494                   else {
1495                         if (drag == DRAG_MOVE_START)
1496                               drag = DRAG_MOVE;
1497                         else if (drag == DRAG_COPY_START)
1498                               drag = DRAG_COPY;
1499                         else
1500                               drag = DRAG_CLONE;
1501                         }
1502                   setCursor();
1503                   if (curItem && !curItem->isSelected()) {
1504                         if (drag == DRAG_MOVE)
1505                               deselectAll();
1506                         selectItem(curItem, true);
1507                         itemSelectionsChanged(nullptr, drag == DRAG_MOVE);
1508                         redraw();
1509                         }
1510                   DragType dt;
1511                   if (drag == DRAG_MOVE)
1512                         dt = MOVE_MOVE;
1513                   else if (drag == DRAG_COPY)
1514                         dt = MOVE_COPY;
1515                   else
1516                         dt = MOVE_CLONE;
1517 
1518                   startMoving(ev_pos, dir, dt, !(keyState & Qt::ShiftModifier));
1519                   break;
1520             }
1521 
1522             case DRAG_MOVE:
1523             case DRAG_COPY:
1524             case DRAG_CLONE:
1525                   if(!scrollTimer)
1526                     moveItems(ev_pos, 0, !shift);
1527                   break;
1528 
1529             case DRAGX_MOVE:
1530             case DRAGX_COPY:
1531             case DRAGX_CLONE:
1532                   if(!scrollTimer)
1533                     moveItems(ev_pos, 1, !shift);
1534                   break;
1535 
1536             case DRAGY_MOVE:
1537             case DRAGY_COPY:
1538             case DRAGY_CLONE:
1539                   if(!scrollTimer)
1540                     moveItems(ev_pos, 2, !shift);
1541                   break;
1542 
1543             case DRAG_NEW:
1544                   if(newCItem) {
1545                     if (last_dist.x()) {
1546                           if(((ctrl || right_button) && !meta && !alt) || !virt())  // Non-virt width is meaningless, such as drums.
1547                           {
1548                             int nx = ev_pos.x() - newCItem->width();  // Keep the cursor at the right edge.
1549                             if(nx < 0)
1550                               nx = 0;
1551                             if(!shift)
1552                             {
1553                               nx = raster(QPoint(nx, 0)).x();  // 0 is dummy, we want only x
1554                               if(nx < 0)
1555                                 nx = 0;
1556                             }
1557                             newCItem->move(QPoint(nx, newCItem->y()));
1558                             // Even though we only move the primary position here,
1559                             //  set the mp as well.
1560                             newCItem->setMp(newCItem->pos());
1561                           }
1562                           else
1563                           {
1564                             int w = ev_pos.x() - newCItem->x();
1565                             if(w < 1)
1566                               w = 1;
1567                             newCItem->setWidth(w);
1568                           }
1569                           }
1570                     if (last_dist.y()) {
1571                           const int x = newCItem->x();
1572                           const int y = ev_pos.y();
1573                           const int ny = pitch2y(y2pitch(y)) - yItemOffset();
1574                           const QPoint pt = QPoint(x, ny);
1575                           const QPoint old_pt = newCItem->mp();
1576                           itemMoving(newCItem, pt);
1577                           newCItem->move(pt);
1578                           newCItem->setHeight(y2height(y));
1579                           // Even though we only move the primary position here,
1580                           //  set the mp as well so note sounding logic can work easier.
1581                           newCItem->setMp(newCItem->pos());
1582                           itemMoved(newCItem, old_pt);
1583                           }
1584                     if (last_dist.x() || last_dist.y())
1585                       redraw();
1586                   }
1587                   break;
1588 
1589             case DRAG_RESIZE:
1590                   if (curItem && last_dist.x()) {
1591                       if (supportsMultipleResize) {
1592                           resizeSelected(last_dist.x(), resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT);
1593                       } else {
1594                           if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) {
1595                               int w = ev_pos.x() - curItem->x();
1596                               if(w < 1)
1597                                   w = 1;
1598                               curItem->setWidth(w);
1599                           } else {
1600                               resizeToTheLeft(ev_pos);
1601                           }
1602                       }
1603                       redraw();
1604                   }
1605                   break;
1606 
1607             case DRAG_DELETE:
1608                   deleteItem(ev_pos);
1609                   break;
1610 
1611             case DRAG_PAN:
1612                   {
1613                     bool changed = false;
1614                     if((!shift || (shift && ctrl)) && glob_zoom_dist.x() != 0 && (!doScroll || hscrollDir == HSCROLL_NONE))  // Don't interfere if auto-scrolling
1615                     {
1616                       emit horizontalScroll(xpos - glob_zoom_dist.x());
1617                       changed = true;
1618                     }
1619                     if((!ctrl || (shift && ctrl)) && glob_zoom_dist.y() != 0 && (!doScroll || vscrollDir == VSCROLL_NONE))   // Don't interfere if auto-scrolling
1620                     {
1621                       emit verticalScroll(ypos - glob_zoom_dist.y());
1622                       changed = true;
1623                     }
1624                     if(MusEGlobal::config.borderlessMouse && changed)
1625                     {
1626                       ignore_mouse_move = true;      // Avoid recursion.
1627                       QCursor::setPos(screen_center);
1628                       //ignore_mouse_move = false;
1629                     }
1630                   }
1631                   break;
1632 
1633             case DRAG_ZOOM:
1634                   if(glob_zoom_dist.x() != 0)
1635                       emit horizontalZoom(glob_zoom_dist.x(), global_start);
1636                   //if(glob_zoom_dist.y() != 0)
1637                   //    emit verticalZoom(glob_zoom_dist.y(), global_start);  // TODO
1638                   if(MusEGlobal::config.borderlessMouse && (glob_zoom_dist.x() != 0 || glob_zoom_dist.y() != 0))
1639                   {
1640                     ignore_mouse_move = true;      // Avoid recursion.
1641                     QCursor::setPos(screen_center);
1642                     //ignore_mouse_move = false;
1643                   }
1644                   break;
1645 
1646             case DRAG_OFF:
1647                   if(_tool == PencilTool){
1648                      if(findCurrentItem(ev_pos)){
1649                         setMouseOverItemCursor();
1650                         break;
1651                      }
1652                   }
1653                   else if(_tool == AutomationTool || _tool == StretchTool || _tool == SamplerateTool){
1654                     // The PartCanvas and WaveCanvas mouseMove will take care of its own cursor.
1655                     // Break otherwise there is bad flickering as the 'pointing hand' competes with 'cross' etc.
1656                     break;
1657                   }
1658                   setCursor();
1659                   break;
1660             }
1661 
1662       ev_global_pos = event->globalPos();
1663 
1664       if(drag != DRAG_ZOOM && (drag != DRAG_PAN || !MusEGlobal::config.borderlessMouse))
1665         mouseMove(event);
1666       }
1667 
1668 //---------------------------------------------------------
1669 //   viewMouseReleaseEvent
1670 //---------------------------------------------------------
1671 
viewMouseReleaseEvent(QMouseEvent * event)1672 void Canvas::viewMouseReleaseEvent(QMouseEvent* event)
1673 {
1674       doScroll = false;
1675       canScrollLeft = true;
1676       canScrollRight = true;
1677       canScrollUp = true;
1678       canScrollDown = true;
1679       // We want only the left mouse release events. Ignore anything else.
1680       // Do nothing, even if the mouse is grabbed or we have a moving list.
1681       if(event->button() != Qt::LeftButton)
1682       {
1683         return;
1684       }
1685 
1686       // Immediately cancel any mouse grabbing.
1687       // Because for example there are a few message boxes that may appear
1688       //  in the subsequent code, and the mouse will not work in them if it
1689       //  is still grabbed.
1690       setMouseGrab(false);
1691 
1692       QPoint pos = event->pos();
1693       bool ctrl = event->modifiers() & Qt::ControlModifier;
1694       bool shift = event->modifiers() & Qt::ShiftModifier;
1695       bool alt = event->modifiers() & Qt::AltModifier;
1696       bool redrawFlag = false;
1697 
1698       switch (drag) {
1699             case DRAG_MOVE_START:
1700             case DRAG_COPY_START:
1701             case DRAG_CLONE_START:
1702                   if (curItem && curItem->part() != curPart) {
1703                         curPart = curItem->part();
1704                         curPartId = curPart->sn();
1705                         curPartChanged();
1706                         }
1707                   if (alt || !ctrl)
1708                         deselectAll();
1709                   if(curItem)
1710                   {
1711                     if (!shift) { //Select or deselect only the clicked item
1712                         selectItem(curItem, !(ctrl && curItem->isSelected()));
1713                         }
1714                     else { //Select or deselect all on the same pitch (e.g. same y-value)
1715                         bool selectionFlag = !(ctrl && curItem->isSelected());
1716                         for (iCItem i = items.begin(); i != items.end(); ++i)
1717                               if (i->second->y() == curItem->y() )
1718                                     selectItem(i->second, selectionFlag);
1719                         }
1720                   }
1721 
1722                   itemSelectionsChanged(nullptr, !ctrl);
1723                   redrawFlag = true;
1724                   if(curItem)
1725                     itemReleased(curItem, curItem->pos());
1726                   itemsReleased();
1727                   break;
1728             case DRAG_COPY:
1729                   endMoveItems(pos, MOVE_COPY, 0, !shift);
1730                   break;
1731             case DRAGX_COPY:
1732                   endMoveItems(pos, MOVE_COPY, 1, !shift);
1733                   break;
1734             case DRAGY_COPY:
1735                   endMoveItems(pos, MOVE_COPY, 2, !shift);
1736                   break;
1737             case DRAG_MOVE:
1738                   endMoveItems(pos, MOVE_MOVE, 0, !shift);
1739                   break;
1740             case DRAGX_MOVE:
1741                   endMoveItems(pos, MOVE_MOVE, 1, !shift);
1742                   break;
1743             case DRAGY_MOVE:
1744                   endMoveItems(pos, MOVE_MOVE, 2, !shift);
1745                   break;
1746             case DRAG_CLONE:
1747                   endMoveItems(pos, MOVE_CLONE, 0, !shift);
1748                   break;
1749             case DRAGX_CLONE:
1750                   endMoveItems(pos, MOVE_CLONE, 1, !shift);
1751                   break;
1752             case DRAGY_CLONE:
1753                   endMoveItems(pos, MOVE_CLONE, 2, !shift);
1754                   break;
1755             case DRAG_OFF:
1756                   break;
1757             case DRAG_RESIZE:
1758                   if (curItem) {
1759                       if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) {
1760                           if (!supportsMultipleResize) {
1761                               QPoint rpos = QPoint(!shift ? raster(pos).x() : pos.x(), curItem->y());
1762                               resizeToTheLeft(rpos);
1763                           }
1764                       }
1765                       resizeItem(curItem, shift, ctrl);
1766                       itemSelectionsChanged();
1767                       redraw();
1768                       resizeDirection = MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT; // reset to default state or ctrl+rightclick resize will cease to work
1769                   }
1770                   break;
1771             case DRAG_NEW:
1772                   if(newCItem)
1773                   {
1774                     items.add(newCItem);
1775                     curItem = newCItem;
1776                     newCItem = nullptr;
1777                     itemReleased(curItem, curItem->pos());
1778                     itemsReleased();
1779                     newItem(curItem, shift);
1780                     redrawFlag = true;
1781                   }
1782                   break;
1783             case DRAG_LASSO_START:
1784                   // Set the new lasso rectangle and compute the new lasso region.
1785                   setLasso(QRect(start.x(), start.y(), rmapxDev(1), rmapyDev(1)));
1786                   //fallthrough
1787             case DRAG_LASSO:
1788                   if (!ctrl)
1789                         deselectAll();
1790                   // Set the new lasso rectangle and compute the new lasso region.
1791                   selectLasso(ctrl);
1792                   itemSelectionsChanged(nullptr, !ctrl);
1793                   redrawFlag = true;
1794                   break;
1795 
1796             case DRAG_DELETE:
1797                   break;
1798 
1799             case DRAG_PAN:
1800                   if(MusEGlobal::config.borderlessMouse)
1801                   {
1802                     pos = global_start;
1803                     ignore_mouse_move = true;      // Avoid recursion.
1804                     QCursor::setPos(global_start);
1805                     //ignore_mouse_move = false;
1806                   } else
1807                       QWidget::setCursor(*handCursor);
1808                   break;
1809 
1810             case DRAG_ZOOM:
1811                   if(MusEGlobal::config.borderlessMouse)
1812                   {
1813                     pos = global_start;
1814                     ignore_mouse_move = true;      // Avoid recursion.
1815                     QCursor::setPos(global_start);
1816                     //ignore_mouse_move = false;
1817                   }
1818                   break;
1819             }
1820       //printf("Canvas::viewMouseReleaseEvent setting drag to DRAG_OFF\n");
1821 
1822       // Just in case it was somehow forgotten:
1823       if(newCItem)
1824       {
1825         if(newCItem->event().empty() && newCItem->part()) // Was it a new part, with no event?
1826           delete newCItem->part();
1827         delete newCItem;
1828         newCItem = nullptr;
1829       }
1830 
1831       if(drag == DRAG_ZOOM) // Update the small zoom drawing area
1832       {
1833         drag = DRAG_OFF;
1834         QPoint pt = mapFromGlobal(global_start);
1835         QSize cursize = zoomIconSVG->actualSize(QSize(MusEGlobal::config.cursorSize, MusEGlobal::config.cursorSize));
1836         update(pt.x(), pt.y(), cursize.width(), cursize.height());
1837       }
1838 
1839       // Cancel all previous mouse ops. Right now there should be no moving list and drag should be off etc.
1840       cancelMouseOps();
1841 
1842       // Be sure to reset the cursor.
1843       setCursor();
1844 
1845       if (redrawFlag)
1846             redraw();
1847 
1848       // HACK
1849       QMouseEvent e(event->type(), pos,
1850          event->globalPos(), event->button(), event->buttons(), event->modifiers());
1851       mouseRelease(&e);
1852 
1853       //mouseRelease(pos);
1854 }
1855 
1856 //---------------------------------------------------------
1857 //   selectLasso
1858 //---------------------------------------------------------
1859 
selectLasso(bool toggle)1860 bool Canvas::selectLasso(bool toggle)
1861       {
1862       int n = 0;
1863       if (virt()) {
1864             for (iCItem i = items.begin(); i != items.end(); ++i) {
1865                   if (i->second->intersects(lasso)) {
1866                         selectItem(i->second, !(toggle && i->second->isSelected()));
1867                         ++n;
1868                         }
1869                   }
1870             }
1871       else {
1872             for (iCItem i = items.begin(); i != items.end(); ++i) {
1873                   QRect box = i->second->bbox();
1874                   int x = rmapxDev(box.x());
1875                   int y = rmapyDev(box.y());
1876                   int w = rmapxDev(box.width());
1877                   int h = rmapyDev(box.height());
1878                   QRect r(x, y, w, h);
1879                   r.translate(i->second->pos().x(), i->second->pos().y());
1880                   if (r.intersects(lasso)) {
1881                         selectItem(i->second, !(toggle && i->second->isSelected()));
1882                         ++n;
1883                         }
1884                   }
1885             }
1886 
1887       return n != 0;
1888       }
1889 
1890 
1891 //---------------------------------------------------------
1892 //   getCurrentDrag
1893 //   returns 0 if there is no drag operation
1894 //---------------------------------------------------------
1895 
getCurrentDrag()1896 int Canvas::getCurrentDrag()
1897       {
1898       //printf("getCurrentDrag=%d\n", drag);
1899       return drag;
1900       }
1901 
1902 //---------------------------------------------------------
1903 //   deleteItem
1904 //---------------------------------------------------------
1905 
deleteItem(const QPoint & p)1906 void Canvas::deleteItem(const QPoint& p)
1907       {
1908       if (virt()) {
1909             for (iCItem i = items.begin(); i != items.end(); ++i) {
1910                   if (i->second->contains(p)) {
1911                         selectItem(i->second, false);
1912                         if (!deleteItem(i->second)) {
1913                               if (drag == DRAG_DELETE)
1914                                     drag = DRAG_OFF;
1915                               }
1916                         break;
1917                         }
1918                   }
1919             }
1920       else {
1921             for (iCItem i = items.begin(); i != items.end(); ++i) {
1922                   QRect box = i->second->bbox();
1923                   int x = rmapxDev(box.x());
1924                   int y = rmapyDev(box.y());
1925                   int w = rmapxDev(box.width());
1926                   int h = rmapyDev(box.height());
1927                   QRect r(x, y, w, h);
1928                   r.translate(i->second->pos().x(), i->second->pos().y());
1929                   if (r.contains(p)) {
1930                         if (deleteItem(i->second)) {
1931                               selectItem(i->second, false);
1932                               }
1933                         break;
1934                         }
1935                   }
1936             }
1937       }
1938 
1939 //---------------------------------------------------------
1940 //   setTool
1941 //---------------------------------------------------------
1942 
setTool(int t)1943 void Canvas::setTool(int t)
1944       {
1945       if (_tool == Tool(t))
1946             return;
1947       _tool = Tool(t);
1948       setCursor();
1949       MusEGlobal::muse->clearStatusBarText();
1950       update();
1951       }
1952 
1953 //---------------------------------------------------------
1954 //   findCurrentItem
1955 //---------------------------------------------------------
1956 
findCurrentItem(const QPoint & cStart)1957 CItem *Canvas::findCurrentItem(const QPoint &cStart)
1958 {
1959    //---------------------------------------------------
1960    //    set curItem to item mouse is pointing
1961    //    (if any)
1962    //---------------------------------------------------
1963 
1964    CItem *item = nullptr;
1965    if (virt())
1966       item = items.find(cStart);
1967    else {
1968       for (ciCItem i = items.begin(); i != items.end(); ++i) {
1969          QRect box = i->second->bbox();
1970          int x = rmapxDev(box.x());
1971          int y = rmapyDev(box.y());
1972          int w = rmapxDev(box.width());
1973          int h = rmapyDev(box.height());
1974          QRect r(x, y, w, h);
1975          r.translate(i->second->pos().x(), i->second->pos().y());
1976          if (r.contains(cStart)) {
1977             if(i->second->isSelected())
1978               return i->second;
1979             else
1980             {
1981               if(!item)
1982                 item = i->second;
1983             }
1984          }
1985       }
1986    }
1987    return item;
1988 }
1989 
setLasso(const QRect & r)1990 void Canvas::setLasso(const QRect& r)
1991 {
1992   lasso = normalizeQRect(r);
1993   lassoToRegion(lasso, lassoRegion);
1994 }
1995 
resizeSelected(const int & dist,const bool left)1996 void Canvas::resizeSelected(const int &dist, const bool left)
1997 {
1998     for (auto &it: items) {
1999         if (!it.second->isSelected())
2000             continue;
2001 
2002         if (left) {
2003             QPoint mp(qMin(it.second->x() + it.second->width() - 2, it.second->x() + dist), it.second->y());
2004             it.second->setTopLeft(mp);
2005 
2006         } else {
2007             it.second->setWidth(qMax(1, it.second->width() + dist));
2008         }
2009     }
2010 }
2011 
resizeToTheLeft(const QPoint & pos)2012 void Canvas::resizeToTheLeft(const QPoint &pos)
2013 {
2014    int newX = pos.x();
2015    if(end.x() - newX < 1)
2016       newX = end.x() - 1;
2017    int dx = end.x() - newX;
2018    curItem->setWidth(dx);
2019    QPoint mp(newX, curItem->y());
2020    curItem->move(mp);
2021    //fprintf(stderr, "newX=%d, dx=%d\n", newX, dx);
2022 }
2023 
setCursor()2024 void Canvas::setCursor()
2025 {
2026     showCursor();
2027     switch (drag) {
2028     case DRAGX_MOVE:
2029     case DRAGX_COPY:
2030     case DRAGX_CLONE:
2031         QWidget::setCursor(QCursor(Qt::SizeHorCursor));
2032         break;
2033 
2034     case DRAGY_MOVE:
2035     case DRAGY_COPY:
2036     case DRAGY_CLONE:
2037         QWidget::setCursor(QCursor(Qt::SizeVerCursor));
2038         break;
2039 
2040     case DRAG_MOVE:
2041     case DRAG_COPY:
2042     case DRAG_CLONE:
2043         // Bug in KDE cursor theme? On some distros this cursor is actually another version of a closed hand! From 'net:
2044         // "It might be a problem in the distribution as Qt uses the cursor that is provided by X.org/xcursor extension with name "size_all".
2045         //  We fixed this issue by setting the KDE cursor theme to "System theme" "
2046         QWidget::setCursor(QCursor(Qt::SizeAllCursor));
2047         break;
2048 
2049     case DRAG_RESIZE:
2050         QWidget::setCursor(QCursor(Qt::SizeHorCursor));
2051         break;
2052 
2053     case DRAG_PAN:
2054         if(MusEGlobal::config.borderlessMouse)
2055             showCursor(false); // CAUTION
2056         else
2057             QWidget::setCursor(*closedHandCursor);
2058         break;
2059 
2060     case DRAG_ZOOM:
2061         if(MusEGlobal::config.borderlessMouse)
2062             showCursor(false); // CAUTION
2063         break;
2064 
2065     case DRAG_DELETE:
2066     case DRAG_COPY_START:
2067     case DRAG_CLONE_START:
2068     case DRAG_MOVE_START:
2069     case DRAG_NEW:
2070     case DRAG_LASSO_START:
2071     case DRAG_LASSO:
2072     case DRAG_OFF:
2073         switch(_tool) {
2074         case PencilTool:
2075             QWidget::setCursor(*pencilCursor);
2076             break;
2077         case RubberTool:
2078             QWidget::setCursor(*deleteCursor);
2079             break;
2080         case GlueTool:
2081             QWidget::setCursor(*glueCursor);
2082             break;
2083         case CutTool:
2084             QWidget::setCursor(*cutterCursor);
2085             break;
2086         case MuteTool:
2087             QWidget::setCursor(*mutePartsCursor);
2088             break;
2089         case AutomationTool:
2090             QWidget::setCursor(*drawCursor);
2091             break;
2092         case DrawTool:
2093             // set for prcanvas/dcanvas as they inherit this w/o redefinition
2094             QWidget::setCursor(QCursor(Qt::ForbiddenCursor));
2095             break;
2096         case PanTool:
2097             QWidget::setCursor(*handCursor);
2098             break;
2099         case ZoomTool:
2100             QWidget::setCursor(*zoomCursor);
2101             break;
2102         default:
2103             QWidget::setCursor(QCursor(Qt::ArrowCursor));
2104             break;
2105         }
2106         break;
2107     }
2108 }
2109 
2110 //---------------------------------------------------------
2111 //   setMouseOverItemCursor
2112 //---------------------------------------------------------
2113 
setMouseOverItemCursor()2114 void Canvas::setMouseOverItemCursor()
2115 {
2116   //showCursor();
2117   QWidget::setCursor(QCursor(Qt::SizeHorCursor));
2118 }
2119 
2120 //---------------------------------------------------------
2121 //   keyPress
2122 //---------------------------------------------------------
2123 
keyPress(QKeyEvent * event)2124 void Canvas::keyPress(QKeyEvent* event)
2125       {
2126       event->ignore();
2127       }
2128 
2129 //---------------------------------------------------------
2130 //   keyRelease
2131 //---------------------------------------------------------
2132 
keyRelease(QKeyEvent * event)2133 void Canvas::keyRelease(QKeyEvent* event)
2134       {
2135       event->ignore();
2136       }
2137 
2138 //---------------------------------------------------------
2139 //   isSingleSelection
2140 //---------------------------------------------------------
2141 
isSingleSelection() const2142 bool Canvas::isSingleSelection() const
2143       {
2144       return selectionSize() == 1;
2145       }
2146 
2147 //---------------------------------------------------------
2148 //   itemsAreSelected
2149 //---------------------------------------------------------
2150 
itemsAreSelected() const2151 bool Canvas::itemsAreSelected() const
2152       {
2153       for (ciCItem i = items.begin(); i != items.end(); ++i) {
2154             if (i->second->isSelected())
2155                   return true;
2156             }
2157       return false;
2158       }
2159 
2160 //---------------------------------------------------------
2161 //   selectionSize
2162 //---------------------------------------------------------
2163 
selectionSize() const2164 int Canvas::selectionSize() const
2165       {
2166       int n = 0;
2167       for (ciCItem i = items.begin(); i != items.end(); ++i) {
2168             if (i->second->isSelected())
2169                   ++n;
2170             }
2171       return n;
2172       }
2173 
2174 //---------------------------------------------------------
2175 //   tagItems
2176 //---------------------------------------------------------
2177 
tagItems(MusECore::TagEventList * tag_list,const MusECore::EventTagOptionsStruct & options) const2178 void Canvas::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const
2179 {
2180   const bool tagSelected = options._flags & MusECore::TagSelected;
2181   const bool tagMoving   = options._flags & MusECore::TagMoving;
2182   const bool tagAllItems = options._flags & MusECore::TagAllItems;
2183   const bool tagAllParts = options._flags & MusECore::TagAllParts;
2184   const bool range       = options._flags & MusECore::TagRange;
2185   const MusECore::Pos& p0 = options._p0;
2186   const MusECore::Pos& p1 = options._p1;
2187 
2188   CItem* item;
2189   if(range)
2190   {
2191     for(ciCItem i = items.begin(); i != items.end(); ++i)
2192     {
2193       item = i->second;
2194       if(!tagAllParts && item->part() != curPart)
2195         continue;
2196       if((tagAllItems
2197           || (tagSelected && item->isSelected())
2198           || (tagMoving && item->isMoving()))
2199          && item->isObjectInRange(p0, p1))
2200       {
2201         tag_list->add(item->part(), item->event());
2202       }
2203     }
2204   }
2205   else
2206   {
2207     for(ciCItem i = items.begin(); i != items.end(); ++i)
2208     {
2209       item = i->second;
2210       if(!tagAllParts && item->part() != curPart)
2211         continue;
2212       if(tagAllItems
2213         || (tagSelected && item->isSelected())
2214         || (tagMoving && item->isMoving()))
2215       {
2216         tag_list->add(item->part(), item->event());
2217       }
2218     }
2219   }
2220 }
2221 
2222 //---------------------------------------------------------
2223 //   updateItemSelections
2224 //---------------------------------------------------------
2225 
updateItemSelections()2226 void Canvas::updateItemSelections()
2227       {
2228       bool item_selected;
2229       bool obj_selected;
2230       for (iCItem i = items.begin(); i != items.end(); ++i) {
2231             CItem* item = i->second;
2232             item_selected = item->isSelected();
2233             obj_selected = item->objectIsSelected();
2234             if (item_selected != obj_selected)
2235             {
2236               item->setSelected(obj_selected);
2237             }
2238       }
2239       redraw();
2240 }
2241 
2242 //---------------------------------------------------------
2243 //   genCanvasPopup
2244 //   Add the list of available tools to a popup menu
2245 //   menu parameter can be NULL meaning create a menu here
2246 //---------------------------------------------------------
2247 
genCanvasPopup(QMenu * menu)2248 QMenu* Canvas::genCanvasPopup(QMenu* menu)
2249       {
2250       if (canvasTools == 0)
2251             return nullptr;
2252       QMenu* r_menu = menu;
2253       if(!r_menu)
2254         r_menu = new QMenu(this);
2255       QAction* act0 = nullptr;
2256 
2257       r_menu->addAction(new MenuTitleItem(tr("Tools"), r_menu));
2258 
2259       for (unsigned i = 0; i < static_cast<unsigned>(EditToolBar::toolList.size()); ++i) {
2260             if ((canvasTools & (1 << i))==0)
2261                   continue;
2262             QAction* act = r_menu->addAction(QIcon(**EditToolBar::toolList[i].icon), tr(EditToolBar::toolList[i].tip));
2263 
2264             if (MusEGui::EditToolBar::toolShortcuts.contains(1 << i)) {
2265                 act->setShortcut(MusEGui::shortcuts[MusEGui::EditToolBar::toolShortcuts[1 << i]].key);
2266             }
2267             //
2268             act->setData(TOOLS_ID_BASE + i);
2269             act->setCheckable(true);
2270             act->setChecked((1 << i) == _tool);
2271             if (!act0)
2272                   act0 = act;
2273             }
2274       if(!menu)  // Don't interefere with supplied menu's current item
2275         r_menu->setActiveAction(act0);
2276       return r_menu;
2277       }
2278 
2279 //---------------------------------------------------------
2280 //   canvasPopup
2281 //---------------------------------------------------------
2282 
canvasPopup(int n)2283 void Canvas::canvasPopup(int n)
2284       {
2285         if(n >= TOOLS_ID_BASE)
2286         {
2287           n -= TOOLS_ID_BASE;
2288           int t = 1 << n;
2289           setTool(t);
2290           emit toolChanged(t);
2291         }
2292       }
2293 
setCurrentPart(MusECore::Part * part)2294 void Canvas::setCurrentPart(MusECore::Part* part)
2295 {
2296   curItem = nullptr;
2297   deselectAll();
2298   curPart = part;
2299   curPartId = curPart->sn();
2300   curPartChanged();
2301 }
2302 
2303 //---------------------------------------------------------
2304 //   cancelMouseOps
2305 //---------------------------------------------------------
2306 
cancelMouseOps()2307 bool Canvas::cancelMouseOps()
2308 {
2309   bool changed = false;
2310 
2311   // Make sure this is done. See mousePressEvent.
2312   showCursor();
2313   setMouseGrab(false);
2314 
2315   // Be sure to clear the moving list and especially the item moving flags!
2316   if(!moving.empty())
2317   {
2318     for(iCItem i = moving.begin(); i != moving.end(); ++i)
2319       i->second->setMoving(false);
2320     moving.clear();
2321     changed = true;
2322   }
2323 
2324   if(drag != DRAG_OFF)
2325   {
2326     drag = DRAG_OFF;
2327     changed = true;
2328   }
2329 
2330   redraw();
2331 
2332   return changed;
2333 }
2334 
2335 } // namespace MusEGui
2336 
2337