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