1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: scrollscale.cpp,v 1.2.2.2 2009/11/04 17:43:25 lunar_shuttle Exp $
5 // (C) Copyright 1999 Werner Schweer (ws@seh.de)
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; version 2 of
10 // the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 //
21 //=========================================================
22
23 #include <stdio.h>
24 #include "muse_math.h"
25
26 #include <QBoxLayout>
27 #include <QLabel>
28 #include <QResizeEvent>
29 #include <QScrollBar>
30 #include <QSlider>
31 #include <QToolButton>
32 #include <QToolTip>
33 #include <QStyle>
34
35 #include "scrollscale.h"
36 #include "icons.h"
37
38 namespace MusEGui {
39
40
41 //---------------------------------------------------------
42 // stepScale
43 // increase/decrease scale by single step
44 //---------------------------------------------------------
stepScale(bool up)45 void ScrollScale::stepScale ( bool up )
46 {
47 setMag(scale2mag(up ? scaleVal + 1 : scaleVal - 1));
48 }
49
50 //---------------------------------------------------------
51 // setScale
52 // "val" - slider value in range 0-convertQuickZoomLevelToMag(zoomLevels-1)
53 //---------------------------------------------------------
54
setScale(int val,int pos_offset)55 void ScrollScale::setScale ( int val, int pos_offset )
56 {
57 int off = offset();
58 int old_scale_val = scaleVal;
59
60 scaleVal = mag2scale(val);
61
62 //fprintf(stderr, "scaleMin %d scaleMax %d val=%d emit scaleVal=%d\n", scaleMin, scaleMax, val, scaleVal);
63 emit scaleChanged ( scaleVal );
64 if ( !noScale )
65 setRange ( minVal, maxVal );
66
67 int i = ( scroll->orientation() == Qt::Horizontal ) ? width() : height();
68 int pos, pmax;
69 if ( scaleVal < 1 )
70 {
71 pos = ( off-scaleVal/2 ) / ( -scaleVal );
72 pmax = ( maxVal-scaleVal-1 ) / ( -scaleVal ) - i;
73 }
74 else
75 {
76 pos = off * scaleVal;
77 pmax = maxVal * scaleVal - i;
78 }
79
80 // Zoom at cursor support...
81 if(pos_offset != 0)
82 {
83 double oscale = old_scale_val;
84 double nscale = scaleVal;
85 if(old_scale_val < 1)
86 oscale = 1.0 / -old_scale_val;
87 if(scaleVal < 1)
88 nscale = 1.0 / -scaleVal;
89 double scale_fact = nscale / oscale;
90 int pos_diff = (int)((double)pos_offset * scale_fact - (double)pos_offset + 0.5); // 0.5 for round-off
91 pos += pos_diff;
92 }
93
94 if(pos > pmax)
95 pos = pmax;
96 setPos(pos);
97 }
98
99 //---------------------------------------------------------
100 // setMag
101 //---------------------------------------------------------
102
setMag(int cs,int pos_offset)103 void ScrollScale::setMag ( int cs, int pos_offset )
104 {
105 scale->blockSignals(true);
106 scale->setValue ( cs );
107 scale->blockSignals(false);
108 setScale ( cs, pos_offset );
109 }
110
111 //---------------------------------------------------------
112 // setRange
113 // min,max ticks
114 //---------------------------------------------------------
115
setRange(int min,int max)116 void ScrollScale::setRange ( int min, int max )
117 {
118 // if ((min != minVal) && (max != maxVal))
119 // return;
120 minVal = min;
121 maxVal = max;
122 int i = ( scroll->orientation() == Qt::Horizontal ) ? width() : height();
123
124 if ( !noScale )
125 {
126 if ( scaleVal < 1 )
127 {
128 min = minVal / ( -scaleVal );
129 max = ( maxVal-scaleVal-1 ) / ( -scaleVal ) - i;
130 }
131 else
132 {
133 min = minVal * scaleVal;
134 max = maxVal * scaleVal - i;
135 }
136 }
137 else
138 max -= i;
139 if ( max < 0 )
140 max = 0;
141 if ( min < 0 )
142 min = 0;
143 if ( min > max )
144 max = min;
145
146 scroll->setRange ( min, max );
147
148 // qt doesn't check this...
149 if ( scroll->value() < min )
150 scroll->setValue ( min );
151 if ( scroll->value() > max )
152 scroll->setValue ( max );
153 scroll->setSingleStep(20);
154 scroll->setPageStep(i);
155 }
156
157 //---------------------------------------------------------
158 // setPos
159 // pos in pixel
160 //---------------------------------------------------------
161
setPos(unsigned pos)162 void ScrollScale::setPos ( unsigned pos )
163 {
164 scroll->setValue ( pos );
165 }
166
167 //---------------------------------------------------------
168 // setPosNoLimit
169 // pos in pixel
170 //---------------------------------------------------------
171
setPosNoLimit(unsigned pos)172 void ScrollScale::setPosNoLimit ( unsigned pos )
173 {
174 //printf ( "ScrollScale::setPosNoLimit pos:%d scaleVal:%d offset ticks:%d\n", pos, scaleVal, pos2offset ( pos ) );
175
176 if((int)pos > scroll->maximum())
177 scroll->setMaximum(pos);
178 scroll->setValue(pos);
179 }
180
181 //---------------------------------------------------------
182 // resizeEvent
183 //---------------------------------------------------------
184
resizeEvent(QResizeEvent * ev)185 void ScrollScale::resizeEvent ( QResizeEvent* ev)
186 {
187 QWidget::resizeEvent(ev);
188 emit scaleChanged ( scaleVal );
189 if ( !noScale )
190 setRange ( minVal, maxVal );
191 }
192
193 //---------------------------------------------------------
194 // ScrollScale
195 //---------------------------------------------------------
196
ScrollScale(int s1,int s2,int cs,int max_,Qt::Orientation o,QWidget * parent,int min_,bool inv,double bas)197 ScrollScale::ScrollScale ( int s1, int s2, int cs, int max_, Qt::Orientation o,
198 QWidget* parent, int min_, bool inv, double bas )
199 : QWidget ( parent )
200 {
201 noScale = false;
202 _page = 0;
203 _pages = 1;
204 pageButtons = false;
205 showMagFlag = true;
206 scaleMin = s1;
207 scaleMax = s2;
208 minVal = min_;
209 maxVal = max_;
210 up = nullptr;
211 down = nullptr;
212 logbase = bas;
213 invers = inv;
214 scaleVal = 0;
215
216 scaleVal = cs;
217 const int cur = scale2mag(cs);
218
219 //fprintf(stderr, "ScrollScale: cs:%d cur:%f\n", cs, cur);
220 scale = new QSlider (o);
221 scale->setObjectName("ScrollScaleZoomSlider");
222 // Added by Tim. For some reason focus was on.
223 // It messes up tabbing, and really should have a shortcut instead.
224 scale->setFocusPolicy(Qt::NoFocus);
225 scale->setMinimum(0);
226 scale->setMaximum(convertQuickZoomLevelToMag(zoomLevels-1));
227 scale->setPageStep(1);
228 scale->setValue(cur);
229
230 scroll = new QScrollBar ( o );
231 //scroll->setFocusPolicy(Qt::NoFocus); // Tim.
232
233 emit scaleChanged ( scaleVal );
234 if ( !noScale )
235 setRange ( minVal, maxVal );
236
237 if ( o == Qt::Horizontal )
238 {
239 box = new QBoxLayout ( QBoxLayout::LeftToRight);
240 scale->setMaximumWidth ( 70 );
241 scroll->setMinimumWidth ( 50 );
242 }
243 else
244 {
245 box = new QBoxLayout ( QBoxLayout::TopToBottom);
246 scroll->setMinimumHeight ( 50 );
247 scale->setMaximumHeight ( 70 );
248 }
249 box->setContentsMargins(0, 0, 0, 0);
250 box->setSpacing(0);
251 box->addWidget ( scroll, 10 );
252
253 int w = style()->pixelMetric(QStyle::PM_ScrollBarExtent);;
254 scaleUp = new QToolButton;
255 scaleUp->setObjectName("ScrollScaleZoomButton");
256 scaleUp->setFocusPolicy(Qt::NoFocus);
257 scaleUp->setMaximumSize(w, w);
258 scaleUp->setIcon (*plusSVGIcon);
259 scaleUp->setToolTip(tr("Increase zoom level"));
260 connect(scaleUp, &QToolButton::clicked, this, [this](){ stepScale(true); });
261 scaleDown = new QToolButton;
262 scaleDown->setFocusPolicy(Qt::NoFocus);
263 scaleDown->setObjectName("ScrollScaleZoomButton");
264 scaleDown->setMaximumSize(w, w);
265 scaleDown->setIcon (*minusSVGIcon);
266 scaleDown->setToolTip(tr("Decrease zoom level"));
267 connect(scaleDown, &QToolButton::clicked, this, [this](){ stepScale(false); });
268
269 box->addSpacing(4);
270 box->addWidget(scaleDown);
271 box->addWidget ( scale, 5 );
272 box->addWidget(scaleUp);
273
274 setLayout(box);
275 connect ( scale, SIGNAL ( valueChanged ( int ) ), SLOT ( setScale ( int ) ) );
276 connect ( scroll, SIGNAL ( valueChanged ( int ) ), SIGNAL ( scrollChanged ( int ) ) );
277 }
278
279 //---------------------------------------------------------
280 // setPageButtons
281 //---------------------------------------------------------
282
setPageButtons(bool flag)283 void ScrollScale::setPageButtons ( bool flag )
284 {
285 if ( flag == pageButtons )
286 return;
287
288 if ( flag )
289 {
290 if ( up == nullptr )
291 {
292 up = new QToolButton;
293 up->setIcon ( QIcon(":/svg/up_vee.svg") );
294 down = new QToolButton;
295 down->setIcon ( QIcon(":/svg/down_vee.svg") );
296 pageNo = new QLabel;
297 QString s;
298 s.setNum ( _page+1 );
299 pageNo->setText ( s );
300 down->setToolTip(tr ( "next page" ) );
301 up->setToolTip(tr ( "previous page" ) );
302 pageNo->setToolTip(tr ( "current page number" ) );
303 box->insertWidget ( 1, up );
304 box->insertWidget ( 2, down );
305 box->insertSpacing ( 3, 5 );
306 box->insertWidget ( 4, pageNo );
307 box->insertSpacing ( 5, 5 );
308 connect ( up, SIGNAL ( clicked() ), SLOT ( pageUp() ) );
309 connect ( down, SIGNAL ( clicked() ), SLOT ( pageDown() ) );
310 }
311 up->show();
312 down->show();
313 pageNo->show();
314 if ( _page == ( _pages-1 ) )
315 down->setEnabled ( false );
316 if ( _page == 0 )
317 up->setEnabled ( false );
318 }
319 else
320 {
321 up->hide();
322 down->hide();
323 }
324 pageButtons = flag;
325 }
326
327 //---------------------------------------------------------
328 // showMag
329 //---------------------------------------------------------
330
showMag(bool flag)331 void ScrollScale::showMag ( bool flag )
332 {
333 showMagFlag = flag;
334 if ( flag )
335 scale->show();
336 else
337 scale->hide();
338 box->activate();
339 }
340
341 //---------------------------------------------------------
342 // offset
343 //---------------------------------------------------------
offset() const344 int ScrollScale::offset() const
345 {
346 return pos2offset ( scroll->value() );
347 }
348
349 //---------------------------------------------------------
350 // pos2offset
351 //---------------------------------------------------------
pos2offset(int pos) const352 int ScrollScale::pos2offset ( int pos ) const
353 {
354 if ( scaleVal < 1 )
355 return pos * ( -scaleVal ) + scaleVal/2;
356 else
357 return pos / scaleVal;
358 }
359
360 //---------------------------------------------------------
361 // offset2pos
362 //---------------------------------------------------------
363
offset2pos(int off) const364 int ScrollScale::offset2pos ( int off ) const
365 {
366 if ( scaleVal < 1 )
367 return ( off-scaleVal/2 ) / ( -scaleVal );
368 else
369 return off * scaleVal;
370 }
371
372 //---------------------------------------------------------
373 // mag2scale
374 //---------------------------------------------------------
375
mag2scale(int mag) const376 int ScrollScale::mag2scale(int mag) const
377 {
378 int mag_max = convertQuickZoomLevelToMag(zoomLevels-1);
379 if(mag < 0)
380 mag = 0;
381 else if(mag > mag_max)
382 mag = mag_max;
383 if ( invers )
384 mag = mag_max - mag;
385 double min, max;
386 if ( scaleMin < 0 )
387 min = 1.0/ ( -scaleMin );
388 else
389 min = double ( scaleMin );
390
391 if ( scaleMax < 0 )
392 max = 1.0/ ( -scaleMax );
393 else
394 max = double ( scaleMax );
395
396 double diff = max-min;
397 double fkt = double ( mag ) /double(mag_max);
398 double v = ( pow ( logbase, fkt )-1 ) / ( logbase-1 );
399 double scale;
400 if ( invers )
401 scale = max - v * diff;
402 else
403 scale = min + v * diff;
404
405 #if 0
406 if ( scaleMax > scaleMin )
407 {
408 if ( scale < scaleMin )
409 scale = scaleMin;
410 else if ( scale > scaleMax )
411 scale = scaleMax;
412 }
413 else
414 {
415 if ( scale < scaleMax )
416 scale = scaleMax;
417 else if ( scale > scaleMin )
418 scale = scaleMin;
419 }
420 #endif
421
422 int scale_val;
423 if ( scale < 1.0 )
424 // Floor, rather than simply casting 1.0/scale as a negative int,
425 // was required here due to the unique nature of our negative numbers,
426 // so that the reciprocal scale2mag() matches this mag2scale().
427 // Tested OK so far, loading and saving, with newly opened windows as well. Tim.
428 scale_val = floor ( 1.0 / ( -scale ) );
429 else
430 scale_val = int ( scale );
431 if ( scale_val == -1 ) // nur so
432 scale_val = 1;
433 return scale_val;
434 }
435
436 //---------------------------------------------------------
437 // scale2mag
438 //---------------------------------------------------------
439
scale2mag(int scale) const440 int ScrollScale::scale2mag(int scale) const
441 {
442 double min, max;
443 if ( scaleMin < 0 )
444 min = 1.0/ ( -scaleMin );
445 else
446 min = double ( scaleMin );
447
448 if ( scaleMax < 0 )
449 max = 1.0/ ( -scaleMax );
450 else
451 max = double ( scaleMax );
452
453 double cmag = ( scale < 0 ) ? ( 1.0/ ( -scale ) ) : double ( scale );
454 double diff = max-min;
455
456 const int mag_max = convertQuickZoomLevelToMag(zoomLevels-1);
457
458 // Do a log in the given logbase (see Change of Base Formula).
459 // Choice of 'log()' base (10, 2 natural etc.) is not supposed to matter.
460 const double fkt = log10( (cmag - min) * (logbase - 1) / diff + 1 ) / log10(logbase);
461 // const double fkt = log( (cmag - min) * (logbase - 1) / diff + 1 ) / log(logbase);
462 // Round up so that the reciprocal function scale2mag() matches.
463 const double cur = ceil( fkt * mag_max );
464
465 return cur;
466 }
467
468 //---------------------------------------------------------
469 // setOffset
470 // val in tick
471 //---------------------------------------------------------
472
setOffset(int val)473 void ScrollScale::setOffset ( int val )
474 {
475 int i = ( scroll->orientation() == Qt::Horizontal ) ? width() : height();
476 int pos, max;
477
478 if ( scaleVal < 1 )
479 {
480 pos = ( val-scaleVal/2 ) / ( -scaleVal );
481 max = ( maxVal-scaleVal-1 ) / ( -scaleVal ) - i;
482 }
483 else
484 {
485 pos = val * scaleVal;
486 max = maxVal * scaleVal - i;
487 }
488 if ( pos > max )
489 {
490 int min;
491 if ( scaleVal < 1 )
492 {
493 maxVal = ( pos + width() ) * ( -scaleVal );
494 min = ( minVal-scaleVal/2 ) / ( -scaleVal );
495 max = ( maxVal-scaleVal/2 ) / ( -scaleVal ) - i;
496 }
497 else
498 {
499 maxVal = ( pos + width() + scaleVal/2 ) /scaleVal;
500 min = minVal * scaleVal;
501 max = maxVal * scaleVal - i;
502 }
503
504 if ( max < 0 )
505 max = 0;
506 if ( min < 0 )
507 min = 0;
508 if ( min > max )
509 max = min;
510 scroll->setRange ( min, max );
511 }
512
513 setPos ( pos );
514 }
515
516 //---------------------------------------------------------
517 // pageUp
518 // goto previous page
519 //---------------------------------------------------------
520
pageUp()521 void ScrollScale::pageUp()
522 {
523 if ( _page )
524 {
525 --_page;
526 emit newPage ( _page );
527 QString s;
528 s.setNum ( _page+1 );
529 pageNo->setText ( s );
530 if ( _page == 0 )
531 up->setEnabled ( false );
532 if ( _page == ( _pages-2 ) )
533 down->setEnabled ( true );
534 }
535 }
536
537 //---------------------------------------------------------
538 // pageDown
539 // goto next page
540 //---------------------------------------------------------
541
pageDown()542 void ScrollScale::pageDown()
543 {
544 if ( _page + 1 < _pages )
545 {
546 ++_page;
547 emit newPage ( _page );
548 QString s;
549 s.setNum ( _page+1 );
550 pageNo->setText ( s );
551 if ( _page == ( _pages-1 ) )
552 down->setEnabled ( false );
553 if ( _page == 1 )
554 up->setEnabled ( true );
555 }
556 }
557
558 //---------------------------------------------------------
559 // setPages
560 //---------------------------------------------------------
561
setPages(int n)562 void ScrollScale::setPages ( int n )
563 {
564 _pages = n;
565 if ( _page >= _pages )
566 {
567 _page = _pages-1;
568 emit newPage ( _page );
569 QString s;
570 s.setNum ( _page+1 );
571 pageNo->setText ( s );
572 }
573 up->setEnabled ( _page );
574 down->setEnabled ( _page < ( _pages-1 ) );
575 }
576
pos() const577 int ScrollScale::pos() const
578 {
579 return scroll->value();
580 }
581
mag() const582 int ScrollScale::mag() const
583 {
584 return scale->value();
585 }
586
587 /**
588 * Hardcoded hackish function that corresponds to the values used for the scrollscales in PianoRoll and DrumEditor
589 * since I couldn't easily create any inverse function from the [0,1024]-range to detect where a zoom actually occurs
590 * (mg)
591 */
getQuickZoomLevel(int mag)592 int ScrollScale::getQuickZoomLevel(int mag)
593 {
594 if (mag == 0)
595 return 0;
596
597 for (int i=0; i<zoomLevels-1; i++) {
598 int val1 = ScrollScale::convertQuickZoomLevelToMag(i);
599 int val2 = ScrollScale::convertQuickZoomLevelToMag(i + 1);
600 if (mag > val1 && mag <= val2)
601 return i + 1;
602 }
603
604 return -1;
605 }
606
607 /**
608 * Function returning the boundary values for a zoom change, hardcoded corresponding to the values used in PianoRoll
609 * and DrumEditor
610 */
convertQuickZoomLevelToMag(int zoomlevel)611 int ScrollScale::convertQuickZoomLevelToMag(int zoomlevel)
612 {
613 int vals[] = {
614 0, 1, 15, 30, 46, 62, 80, 99, 119, 140,
615 163, 187, 214, 242, 274, 308, 346, 388, 436, 491,
616 555, 631, 726, 849, 1024, 1200, 1300, 1400, 1500, 1600, 1700, 1800,
617 1900, 2100, 2200, 2300, 2400, 2500 };
618
619 return vals[zoomlevel];
620 }
621
scaleMinimum() const622 int ScrollScale::scaleMinimum() const { return scaleMin; }
scaleMaximum() const623 int ScrollScale::scaleMaximum() const { return scaleMax; }
624
setScaleMinimum(int min)625 void ScrollScale::setScaleMinimum(int min)
626 {
627 if(scaleMin == min)
628 return;
629 scaleMin = min;
630
631 if(scaleVal < scaleMin)
632 {
633 scaleVal = scaleMin;
634 emit scaleChanged ( scaleVal );
635 if ( !noScale )
636 setRange ( minVal, maxVal );
637 }
638 repaint();
639 }
640
setScaleMaximum(int max)641 void ScrollScale::setScaleMaximum(int max)
642 {
643 if(scaleMax == max)
644 return;
645 scaleMax = max;
646
647 if(scaleVal > scaleMax)
648 {
649 scaleVal = scaleMax;
650 emit scaleChanged ( scaleVal );
651 if ( !noScale )
652 setRange ( minVal, maxVal );
653 }
654 repaint();
655 }
656
setScaleRange(int min,int max)657 void ScrollScale::setScaleRange(int min, int max)
658 {
659 if(scaleMin == min && scaleMax == max)
660 return;
661 scaleMin = min;
662 scaleMax = max;
663
664 if(scaleVal < scaleMin || scaleVal > scaleMax)
665 {
666 if(scaleVal < scaleMin)
667 scaleVal = scaleMin;
668 else if(scaleVal > scaleMax)
669 scaleVal = scaleMax;
670 emit scaleChanged ( scaleVal );
671 if ( !noScale )
672 setRange ( minVal, maxVal );
673 }
674 repaint();
675 }
676
677 } // namespace MusEGui
678