1 /*
2 * SongEditor.cpp - basic window for song-editing
3 *
4 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 *
6 * This file is part of LMMS - https://lmms.io
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of 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 GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program (see COPYING); if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
22 *
23 */
24
25 #include "SongEditor.h"
26
27 #include <QTimeLine>
28 #include <QAction>
29 #include <QKeyEvent>
30 #include <QLabel>
31 #include <QLayout>
32 #include <QMdiArea>
33 #include <QMdiSubWindow>
34 #include <QPainter>
35
36 #include "ActionGroup.h"
37 #include "AutomatableSlider.h"
38 #include "ComboBox.h"
39 #include "ConfigManager.h"
40 #include "CPULoadWidget.h"
41 #include "embed.h"
42 #include "GuiApplication.h"
43 #include "LcdSpinBox.h"
44 #include "MainWindow.h"
45 #include "MeterDialog.h"
46 #include "Mixer.h"
47 #include "TextFloat.h"
48 #include "TimeLineWidget.h"
49 #include "ToolTip.h"
50 #include "VisualizationWidget.h"
51 #include "TimeDisplayWidget.h"
52 #include "AudioDevice.h"
53 #include "PianoRoll.h"
54 #include "Track.h"
55
positionLine(QWidget * parent)56 positionLine::positionLine( QWidget * parent ) :
57 QWidget( parent )
58 {
59 setFixedWidth( 1 );
60 setAttribute( Qt::WA_NoSystemBackground, true );
61 }
62
63
64
65
paintEvent(QPaintEvent * pe)66 void positionLine::paintEvent( QPaintEvent * pe )
67 {
68 QPainter p( this );
69 p.fillRect( rect(), QColor( 255, 255, 255, 153 ) );
70 }
71
72 const QVector<double> SongEditor::m_zoomLevels =
73 { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f };
74
75
SongEditor(Song * song)76 SongEditor::SongEditor( Song * song ) :
77 TrackContainerView( song ),
78 m_song( song ),
79 m_zoomingModel(new ComboBoxModel()),
80 m_scrollBack( false ),
81 m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ),
82 m_mode(DrawMode),
83 m_origin(),
84 m_scrollPos(),
85 m_mousePos(),
86 m_rubberBandStartTrackview(0),
87 m_rubberbandStartMidipos(0),
88 m_currentZoomingValue(m_zoomingModel->value()),
89 m_trackHeadWidth(ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt()==1
90 ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT
91 : DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH),
92 m_selectRegion(false)
93 {
94 m_zoomingModel->setParent(this);
95 // create time-line
96 m_timeLine = new TimeLineWidget( m_trackHeadWidth, 32,
97 pixelsPerTact(),
98 m_song->m_playPos[Song::Mode_PlaySong],
99 m_currentPosition, this );
100 connect( this, SIGNAL( positionChanged( const MidiTime & ) ),
101 m_song->m_playPos[Song::Mode_PlaySong].m_timeLine,
102 SLOT( updatePosition( const MidiTime & ) ) );
103 connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ),
104 this, SLOT( updatePosition( const MidiTime & ) ) );
105 connect( m_timeLine, SIGNAL( regionSelectedFromPixels( int, int ) ),
106 this, SLOT( selectRegionFromPixels( int, int ) ) );
107 connect( m_timeLine, SIGNAL( selectionFinished() ),
108 this, SLOT( stopRubberBand() ) );
109
110 m_positionLine = new positionLine( this );
111
112 static_cast<QVBoxLayout *>( layout() )->insertWidget( 1, m_timeLine );
113
114
115 // add some essential widgets to global tool-bar
116 QWidget * tb = gui->mainWindow()->toolBar();
117
118 gui->mainWindow()->addSpacingToToolBar( 40 );
119
120 m_tempoSpinBox = new LcdSpinBox( 3, tb, tr( "Tempo" ) );
121 m_tempoSpinBox->setModel( &m_song->m_tempoModel );
122 m_tempoSpinBox->setLabel( tr( "TEMPO/BPM" ) );
123 ToolTip::add( m_tempoSpinBox, tr( "tempo of song" ) );
124
125 m_tempoSpinBox->setWhatsThis(
126 tr( "The tempo of a song is specified in beats per minute "
127 "(BPM). If you want to change the tempo of your "
128 "song, change this value. Every measure has four beats, "
129 "so the tempo in BPM specifies, how many measures / 4 "
130 "should be played within a minute (or how many measures "
131 "should be played within four minutes)." ) );
132
133 int tempoSpinBoxCol = gui->mainWindow()->addWidgetToToolBar( m_tempoSpinBox, 0 );
134
135 #if 0
136 toolButton * hq_btn = new toolButton( embed::getIconPixmap( "hq_mode" ),
137 tr( "High quality mode" ),
138 NULL, NULL, tb );
139 hq_btn->setCheckable( true );
140 connect( hq_btn, SIGNAL( toggled( bool ) ),
141 this, SLOT( setHighQuality( bool ) ) );
142 hq_btn->setFixedWidth( 42 );
143 gui->mainWindow()->addWidgetToToolBar( hq_btn, 1, col );
144 #endif
145
146 gui->mainWindow()->addWidgetToToolBar( new TimeDisplayWidget, 1, tempoSpinBoxCol );
147
148 gui->mainWindow()->addSpacingToToolBar( 10 );
149
150 m_timeSigDisplay = new MeterDialog( this, true );
151 m_timeSigDisplay->setModel( &m_song->m_timeSigModel );
152 gui->mainWindow()->addWidgetToToolBar( m_timeSigDisplay );
153
154 gui->mainWindow()->addSpacingToToolBar( 10 );
155
156
157 QLabel * master_vol_lbl = new QLabel( tb );
158 master_vol_lbl->setPixmap( embed::getIconPixmap( "master_volume" ) );
159
160 m_masterVolumeSlider = new AutomatableSlider( tb,
161 tr( "Master volume" ) );
162 m_masterVolumeSlider->setModel( &m_song->m_masterVolumeModel );
163 m_masterVolumeSlider->setOrientation( Qt::Vertical );
164 m_masterVolumeSlider->setPageStep( 1 );
165 m_masterVolumeSlider->setTickPosition( QSlider::TicksLeft );
166 m_masterVolumeSlider->setFixedSize( 26, 60 );
167 m_masterVolumeSlider->setTickInterval( 50 );
168 ToolTip::add( m_masterVolumeSlider, tr( "master volume" ) );
169
170 connect( m_masterVolumeSlider, SIGNAL( logicValueChanged( int ) ), this,
171 SLOT( setMasterVolume( int ) ) );
172 connect( m_masterVolumeSlider, SIGNAL( sliderPressed() ), this,
173 SLOT( showMasterVolumeFloat()) );
174 connect( m_masterVolumeSlider, SIGNAL( logicSliderMoved( int ) ), this,
175 SLOT( updateMasterVolumeFloat( int ) ) );
176 connect( m_masterVolumeSlider, SIGNAL( sliderReleased() ), this,
177 SLOT( hideMasterVolumeFloat() ) );
178
179 m_mvsStatus = new TextFloat;
180 m_mvsStatus->setTitle( tr( "Master volume" ) );
181 m_mvsStatus->setPixmap( embed::getIconPixmap( "master_volume" ) );
182
183 gui->mainWindow()->addWidgetToToolBar( master_vol_lbl );
184 gui->mainWindow()->addWidgetToToolBar( m_masterVolumeSlider );
185
186
187 gui->mainWindow()->addSpacingToToolBar( 10 );
188
189
190 QLabel * master_pitch_lbl = new QLabel( tb );
191 master_pitch_lbl->setPixmap( embed::getIconPixmap( "master_pitch" ) );
192 master_pitch_lbl->setFixedHeight( 64 );
193
194 m_masterPitchSlider = new AutomatableSlider( tb, tr( "Master pitch" ) );
195 m_masterPitchSlider->setModel( &m_song->m_masterPitchModel );
196 m_masterPitchSlider->setOrientation( Qt::Vertical );
197 m_masterPitchSlider->setPageStep( 1 );
198 m_masterPitchSlider->setTickPosition( QSlider::TicksLeft );
199 m_masterPitchSlider->setFixedSize( 26, 60 );
200 m_masterPitchSlider->setTickInterval( 12 );
201 ToolTip::add( m_masterPitchSlider, tr( "master pitch" ) );
202 connect( m_masterPitchSlider, SIGNAL( logicValueChanged( int ) ), this,
203 SLOT( setMasterPitch( int ) ) );
204 connect( m_masterPitchSlider, SIGNAL( sliderPressed() ), this,
205 SLOT( showMasterPitchFloat() ) );
206 connect( m_masterPitchSlider, SIGNAL( logicSliderMoved( int ) ), this,
207 SLOT( updateMasterPitchFloat( int ) ) );
208 connect( m_masterPitchSlider, SIGNAL( sliderReleased() ), this,
209 SLOT( hideMasterPitchFloat() ) );
210
211 m_mpsStatus = new TextFloat;
212 m_mpsStatus->setTitle( tr( "Master pitch" ) );
213 m_mpsStatus->setPixmap( embed::getIconPixmap( "master_pitch" ) );
214
215 gui->mainWindow()->addWidgetToToolBar( master_pitch_lbl );
216 gui->mainWindow()->addWidgetToToolBar( m_masterPitchSlider );
217
218 gui->mainWindow()->addSpacingToToolBar( 10 );
219
220 // create widget for visualization- and cpu-load-widget
221 QWidget * vc_w = new QWidget( tb );
222 QVBoxLayout * vcw_layout = new QVBoxLayout( vc_w );
223 vcw_layout->setMargin( 0 );
224 vcw_layout->setSpacing( 0 );
225
226 //vcw_layout->addStretch();
227 vcw_layout->addWidget( new VisualizationWidget(
228 embed::getIconPixmap( "output_graph" ), vc_w ) );
229
230 vcw_layout->addWidget( new CPULoadWidget( vc_w ) );
231 vcw_layout->addStretch();
232
233 gui->mainWindow()->addWidgetToToolBar( vc_w );
234
235 static_cast<QVBoxLayout *>( layout() )->insertWidget( 0, m_timeLine );
236
237 m_leftRightScroll = new QScrollBar( Qt::Horizontal, this );
238 m_leftRightScroll->setMinimum( 0 );
239 m_leftRightScroll->setMaximum( 0 );
240 m_leftRightScroll->setSingleStep( 1 );
241 m_leftRightScroll->setPageStep( 20 );
242 static_cast<QVBoxLayout *>( layout() )->addWidget( m_leftRightScroll );
243 connect( m_leftRightScroll, SIGNAL( valueChanged( int ) ),
244 this, SLOT( scrolled( int ) ) );
245 connect( m_song, SIGNAL( lengthChanged( int ) ),
246 this, SLOT( updateScrollBar( int ) ) );
247 connect(m_leftRightScroll, SIGNAL(valueChanged(int)),this, SLOT(updateRubberband()));
248 connect(contentWidget()->verticalScrollBar(), SIGNAL(valueChanged(int)),this, SLOT(updateRubberband()));
249 connect(m_timeLine, SIGNAL(selectionFinished()), this, SLOT(stopSelectRegion()));
250
251
252 // Set up zooming model
253 for( float const & zoomLevel : m_zoomLevels )
254 {
255 m_zoomingModel->addItem( QString( "%1\%" ).arg( zoomLevel * 100 ) );
256 }
257 m_zoomingModel->setInitValue(
258 m_zoomingModel->findText( "100%" ) );
259 connect( m_zoomingModel, SIGNAL( dataChanged() ),
260 this, SLOT( zoomingChanged() ) );
261
262 setFocusPolicy( Qt::StrongFocus );
263 setFocus();
264 }
265
266
267
268
~SongEditor()269 SongEditor::~SongEditor()
270 {
271 }
272
saveSettings(QDomDocument & doc,QDomElement & element)273 void SongEditor::saveSettings( QDomDocument& doc, QDomElement& element )
274 {
275 MainWindow::saveWidgetState( parentWidget(), element );
276 }
277
loadSettings(const QDomElement & element)278 void SongEditor::loadSettings( const QDomElement& element )
279 {
280 MainWindow::restoreWidgetState(parentWidget(), element);
281 }
282
283
284
285
setHighQuality(bool hq)286 void SongEditor::setHighQuality( bool hq )
287 {
288 Engine::mixer()->changeQuality( Mixer::qualitySettings(
289 hq ? Mixer::qualitySettings::Mode_HighQuality :
290 Mixer::qualitySettings::Mode_Draft ) );
291 }
292
293
294
295
scrolled(int new_pos)296 void SongEditor::scrolled( int new_pos )
297 {
298 update();
299 emit positionChanged( m_currentPosition = MidiTime( new_pos, 0 ) );
300 }
301
302
303
304
selectRegionFromPixels(int xStart,int xEnd)305 void SongEditor::selectRegionFromPixels(int xStart, int xEnd)
306 {
307 if (!m_selectRegion)
308 {
309 m_selectRegion = true;
310
311 //deselect all tcos
312 for (auto &it : findChildren<selectableObject *>()) { it->setSelected(false); }
313
314 rubberBand()->setEnabled(true);
315 rubberBand()->show();
316
317 //we save the position of scrollbars, mouse position and zooming level
318 m_origin = QPoint(xStart, 0);
319 m_scrollPos = QPoint(m_leftRightScroll->value(), contentWidget()->verticalScrollBar()->value());
320 m_currentZoomingValue = zoomingModel()->value();
321
322 //calculate the song position where the mouse was clicked
323 m_rubberbandStartMidipos = MidiTime((xStart - m_trackHeadWidth)
324 / pixelsPerTact() * MidiTime::ticksPerTact())
325 + m_currentPosition;
326 m_rubberBandStartTrackview = 0;
327 }
328 //the current mouse position within the borders of song editor
329 m_mousePos = QPoint(qMax(m_trackHeadWidth, qMin(xEnd, width()))
330 , std::numeric_limits<int>::max());
331 updateRubberband();
332 }
333
334
335
336
stopSelectRegion()337 void SongEditor::stopSelectRegion()
338 {
339 m_selectRegion = false;
340 }
341
342
343
344
updateRubberband()345 void SongEditor::updateRubberband()
346 {
347 if (rubberBandActive())
348 {
349 int originX = m_origin.x();
350
351 //take care of the zooming
352 if (m_currentZoomingValue != m_zoomingModel->value())
353 {
354 originX = m_trackHeadWidth + (originX - m_trackHeadWidth)
355 * m_zoomLevels[m_zoomingModel->value()] / m_zoomLevels[m_currentZoomingValue];
356 }
357
358 //take care of the scrollbar position
359 int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerTact();
360 int vs = contentWidget()->verticalScrollBar()->value() - m_scrollPos.y();
361
362 //the adjusted origin point
363 QPoint origin = QPoint(qMax(originX - hs, m_trackHeadWidth), m_origin.y() - vs);
364
365 //paint the rubber band rect
366 rubberBand()->setGeometry(QRect(origin,
367 contentWidget()->mapFromParent(QPoint(m_mousePos.x(), m_mousePos.y()))
368 ).normalized());
369
370 //the index of the TrackView the mouse is hover
371 int rubberBandTrackview = trackIndexFromSelectionPoint(m_mousePos.y());
372
373 //the miditime the mouse is hover
374 MidiTime rubberbandMidipos = MidiTime((qMin(m_mousePos.x(), width()) - m_trackHeadWidth)
375 / pixelsPerTact() * MidiTime::ticksPerTact())
376 + m_currentPosition;
377
378 //are tcos in the rect of selection?
379 for (auto &it : findChildren<selectableObject *>())
380 {
381 TrackContentObjectView * tco = dynamic_cast<TrackContentObjectView*>(it);
382 if (tco)
383 {
384 auto indexOfTrackView = trackViews().indexOf(tco->getTrackView());
385 bool isBeetweenRubberbandViews = indexOfTrackView >= qMin(m_rubberBandStartTrackview, rubberBandTrackview)
386 && indexOfTrackView <= qMax(m_rubberBandStartTrackview, rubberBandTrackview);
387 bool isBeetweenRubberbandMidiPos = tco->getTrackContentObject()->endPosition() >= qMin(m_rubberbandStartMidipos, rubberbandMidipos)
388 && tco->getTrackContentObject()->startPosition() <= qMax(m_rubberbandStartMidipos, rubberbandMidipos);
389 it->setSelected(isBeetweenRubberbandViews && isBeetweenRubberbandMidiPos);
390 }
391 }
392 }
393 }
394
395
396
397
setEditMode(EditMode mode)398 void SongEditor::setEditMode( EditMode mode )
399 {
400 m_mode = mode;
401 }
402
setEditModeDraw()403 void SongEditor::setEditModeDraw()
404 {
405 setEditMode(DrawMode);
406 }
407
setEditModeSelect()408 void SongEditor::setEditModeSelect()
409 {
410 setEditMode(SelectMode);
411 }
412
413
414
415
keyPressEvent(QKeyEvent * ke)416 void SongEditor::keyPressEvent( QKeyEvent * ke )
417 {
418 bool isShiftPressed = ke->modifiers() & Qt::ShiftModifier;
419 if( isShiftPressed &&
420 ( ke->key() == Qt::Key_Insert || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) )
421 {
422 m_song->insertBar();
423 }
424 else if( isShiftPressed &&
425 ( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) )
426 {
427 m_song->removeBar();
428 }
429 else if( ke->key() == Qt::Key_Left )
430 {
431 tick_t t = m_song->currentTick() - MidiTime::ticksPerTact();
432 if( t >= 0 )
433 {
434 m_song->setPlayPos( t, Song::Mode_PlaySong );
435 }
436 }
437 else if( ke->key() == Qt::Key_Right )
438 {
439 tick_t t = m_song->currentTick() + MidiTime::ticksPerTact();
440 if( t < MaxSongLength )
441 {
442 m_song->setPlayPos( t, Song::Mode_PlaySong );
443 }
444 }
445 else if( ke->key() == Qt::Key_Home )
446 {
447 m_song->setPlayPos( 0, Song::Mode_PlaySong );
448 }
449 else
450 {
451 QWidget::keyPressEvent( ke );
452 }
453 }
454
455
456
457
wheelEvent(QWheelEvent * we)458 void SongEditor::wheelEvent( QWheelEvent * we )
459 {
460 if( we->modifiers() & Qt::ControlModifier )
461 {
462 int z = m_zoomingModel->value();
463
464 if( we->delta() > 0 )
465 {
466 z++;
467 }
468 else if( we->delta() < 0 )
469 {
470 z--;
471 }
472 z = qBound( 0, z, m_zoomingModel->size() - 1 );
473 // update combobox with zooming-factor
474 m_zoomingModel->setValue( z );
475
476 // update timeline
477 m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->
478 setPixelsPerTact( pixelsPerTact() );
479 // and make sure, all TCO's are resized and relocated
480 realignTracks();
481 }
482 else if( (we->modifiers() & Qt::ShiftModifier) || we->orientation() == Qt::Horizontal )
483 {
484 m_leftRightScroll->setValue( m_leftRightScroll->value() -
485 we->delta() / 30 );
486 }
487 else
488 {
489 we->ignore();
490 return;
491 }
492 we->accept();
493 }
494
495
496
closeEvent(QCloseEvent * ce)497 void SongEditor::closeEvent( QCloseEvent * ce )
498 {
499 if( parentWidget() )
500 {
501 parentWidget()->hide();
502 }
503 else
504 {
505 hide();
506 }
507 ce->ignore();
508 }
509
510
511
512
mousePressEvent(QMouseEvent * me)513 void SongEditor::mousePressEvent(QMouseEvent *me)
514 {
515 if (allowRubberband())
516 {
517 //we save the position of scrollbars, mouse position and zooming level
518 m_scrollPos = QPoint(m_leftRightScroll->value(), contentWidget()->verticalScrollBar()->value());
519 m_origin = contentWidget()->mapFromParent(QPoint(me->pos().x(), me->pos().y()));
520 m_currentZoomingValue = zoomingModel()->value();
521
522 //paint the rubberband
523 rubberBand()->setEnabled(true);
524 rubberBand()->setGeometry(QRect(m_origin, QSize()));
525 rubberBand()->show();
526
527 //the trackView(index) and the miditime where the mouse was clicked
528 m_rubberBandStartTrackview = trackIndexFromSelectionPoint(me->y());
529 m_rubberbandStartMidipos = MidiTime((me->x() - m_trackHeadWidth)
530 / pixelsPerTact() * MidiTime::ticksPerTact())
531 + m_currentPosition;
532 }
533 QWidget::mousePressEvent(me);
534 }
535
536
537
538
mouseMoveEvent(QMouseEvent * me)539 void SongEditor::mouseMoveEvent(QMouseEvent *me)
540 {
541 m_mousePos = me->pos();
542 updateRubberband();
543 QWidget::mouseMoveEvent(me);
544 }
545
546
547
548
mouseReleaseEvent(QMouseEvent * me)549 void SongEditor::mouseReleaseEvent(QMouseEvent *me)
550 {
551 rubberBand()->hide();
552 rubberBand()->setEnabled(false);
553 QWidget::mouseReleaseEvent(me);
554 }
555
556
557
558
setMasterVolume(int new_val)559 void SongEditor::setMasterVolume( int new_val )
560 {
561 updateMasterVolumeFloat( new_val );
562
563 if( !m_mvsStatus->isVisible() && !m_song->m_loadingProject
564 && m_masterVolumeSlider->showStatus() )
565 {
566 m_mvsStatus->moveGlobal( m_masterVolumeSlider,
567 QPoint( m_masterVolumeSlider->width() + 2, -2 ) );
568 m_mvsStatus->setVisibilityTimeOut( 1000 );
569 }
570 Engine::mixer()->setMasterGain( new_val / 100.0f );
571 }
572
573
574
575
showMasterVolumeFloat(void)576 void SongEditor::showMasterVolumeFloat( void )
577 {
578 m_mvsStatus->moveGlobal( m_masterVolumeSlider,
579 QPoint( m_masterVolumeSlider->width() + 2, -2 ) );
580 m_mvsStatus->show();
581 updateMasterVolumeFloat( m_song->m_masterVolumeModel.value() );
582 }
583
584
585
586
updateMasterVolumeFloat(int new_val)587 void SongEditor::updateMasterVolumeFloat( int new_val )
588 {
589 m_mvsStatus->setText( tr( "Value: %1%" ).arg( new_val ) );
590 }
591
592
593
594
hideMasterVolumeFloat(void)595 void SongEditor::hideMasterVolumeFloat( void )
596 {
597 m_mvsStatus->hide();
598 }
599
600
601
602
setMasterPitch(int new_val)603 void SongEditor::setMasterPitch( int new_val )
604 {
605 updateMasterPitchFloat( new_val );
606 if( m_mpsStatus->isVisible() == false && m_song->m_loadingProject == false
607 && m_masterPitchSlider->showStatus() )
608 {
609 m_mpsStatus->moveGlobal( m_masterPitchSlider,
610 QPoint( m_masterPitchSlider->width() + 2, -2 ) );
611 m_mpsStatus->setVisibilityTimeOut( 1000 );
612 }
613 }
614
615
616
617
showMasterPitchFloat(void)618 void SongEditor::showMasterPitchFloat( void )
619 {
620 m_mpsStatus->moveGlobal( m_masterPitchSlider,
621 QPoint( m_masterPitchSlider->width() + 2, -2 ) );
622 m_mpsStatus->show();
623 updateMasterPitchFloat( m_song->m_masterPitchModel.value() );
624 }
625
626
627
628
updateMasterPitchFloat(int new_val)629 void SongEditor::updateMasterPitchFloat( int new_val )
630 {
631 m_mpsStatus->setText( tr( "Value: %1 semitones").arg( new_val ) );
632
633 }
634
635
636
637
hideMasterPitchFloat(void)638 void SongEditor::hideMasterPitchFloat( void )
639 {
640 m_mpsStatus->hide();
641 }
642
643
644
645
updateScrollBar(int len)646 void SongEditor::updateScrollBar( int len )
647 {
648 m_leftRightScroll->setMaximum( len );
649 }
650
651
652
653
animateScroll(QScrollBar * scrollBar,int newVal,bool smoothScroll)654 static inline void animateScroll( QScrollBar *scrollBar, int newVal, bool smoothScroll )
655 {
656 if( smoothScroll == false )
657 {
658 scrollBar->setValue( newVal );
659 }
660 else
661 {
662 // do smooth scroll animation using QTimeLine
663 QTimeLine *t = scrollBar->findChild<QTimeLine *>();
664 if( t == NULL )
665 {
666 t = new QTimeLine( 600, scrollBar );
667 t->setFrameRange( scrollBar->value(), newVal );
668 t->connect( t, SIGNAL( finished() ), SLOT( deleteLater() ) );
669
670 scrollBar->connect( t, SIGNAL( frameChanged( int ) ), SLOT( setValue( int ) ) );
671
672 t->start();
673 }
674 else
675 {
676 // smooth scrolling is still active, therefore just update the end frame
677 t->setEndFrame( newVal );
678 }
679 }
680 }
681
682
683
684
updatePosition(const MidiTime & t)685 void SongEditor::updatePosition( const MidiTime & t )
686 {
687 int widgetWidth, trackOpWidth;
688 if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() )
689 {
690 widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT;
691 trackOpWidth = TRACK_OP_WIDTH_COMPACT;
692 }
693 else
694 {
695 widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH;
696 trackOpWidth = TRACK_OP_WIDTH;
697 }
698
699 if( ( m_song->isPlaying() && m_song->m_playMode == Song::Mode_PlaySong
700 && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollEnabled) ||
701 m_scrollBack == true )
702 {
703 m_smoothScroll = ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt();
704 const int w = width() - widgetWidth
705 - trackOpWidth
706 - contentWidget()->verticalScrollBar()->width(); // width of right scrollbar
707 if( t > m_currentPosition + w * MidiTime::ticksPerTact() /
708 pixelsPerTact() )
709 {
710 animateScroll( m_leftRightScroll, t.getTact(), m_smoothScroll );
711 }
712 else if( t < m_currentPosition )
713 {
714 animateScroll( m_leftRightScroll, t.getTact(), m_smoothScroll );
715 }
716 m_scrollBack = false;
717 }
718
719 const int x = m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->
720 markerX( t ) + 8;
721 if( x >= trackOpWidth + widgetWidth -1 )
722 {
723 m_positionLine->show();
724 m_positionLine->move( x, m_timeLine->height() );
725 }
726 else
727 {
728 m_positionLine->hide();
729 }
730
731 m_positionLine->setFixedHeight( height() );
732 }
733
734
735
736
updatePositionLine()737 void SongEditor::updatePositionLine()
738 {
739 m_positionLine->setFixedHeight( height() );
740 }
741
742
743
744
zoomingChanged()745 void SongEditor::zoomingChanged()
746 {
747 setPixelsPerTact( m_zoomLevels[m_zoomingModel->value()] * DEFAULT_PIXELS_PER_TACT );
748
749 m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->
750 setPixelsPerTact( pixelsPerTact() );
751 realignTracks();
752 updateRubberband();
753 }
754
755
756
757
allowRubberband() const758 bool SongEditor::allowRubberband() const
759 {
760 return m_mode == SelectMode;
761 }
762
763
764
765
trackIndexFromSelectionPoint(int yPos)766 int SongEditor::trackIndexFromSelectionPoint(int yPos)
767 {
768 const TrackView * tv = trackViewAt(yPos - m_timeLine->height());
769 return tv ? indexOfTrackView(tv)
770 : yPos < m_timeLine->height() ? 0
771 : trackViews().count();
772 }
773
774
775
776
indexOfTrackView(const TrackView * tv)777 int SongEditor::indexOfTrackView(const TrackView *tv)
778 {
779 return static_cast<int>(std::distance(trackViews().begin(),
780 std::find(trackViews().begin(), trackViews().end(), tv)));
781 }
782
783
784
785
zoomingModel() const786 ComboBoxModel *SongEditor::zoomingModel() const
787 {
788 return m_zoomingModel;
789 }
790
791
792
793
SongEditorWindow(Song * song)794 SongEditorWindow::SongEditorWindow(Song* song) :
795 Editor(Engine::mixer()->audioDev()->supportsCapture()),
796 m_editor(new SongEditor(song))
797 {
798 setWindowTitle( tr( "Song-Editor" ) );
799 setWindowIcon( embed::getIconPixmap( "songeditor" ) );
800
801 setCentralWidget(m_editor);
802 setAcceptDrops(true);
803 m_toolBar->setAcceptDrops(true);
804 connect(m_toolBar, SIGNAL(dragEntered(QDragEnterEvent*)), m_editor, SLOT(dragEnterEvent(QDragEnterEvent*)));
805 connect(m_toolBar, SIGNAL(dropped(QDropEvent*)), m_editor, SLOT(dropEvent(QDropEvent*)));
806
807 // Set up buttons
808 m_playAction->setToolTip(tr("Play song (Space)"));
809 m_recordAction->setToolTip(tr("Record samples from Audio-device"));
810 m_recordAccompanyAction->setToolTip(tr( "Record samples from Audio-device while playing song or BB track"));
811 m_stopAction->setToolTip(tr( "Stop song (Space)" ));
812
813 m_playAction->setWhatsThis(
814 tr("Click here, if you want to play your whole song. "
815 "Playing will be started at the song-position-marker (green). "
816 "You can also move it while playing."));
817 m_stopAction->setWhatsThis(
818 tr("Click here, if you want to stop playing of your song. "
819 "The song-position-marker will be set to the start of your song."));
820
821
822 // Track actions
823 DropToolBar *trackActionsToolBar = addDropToolBarToTop(tr("Track actions"));
824
825 m_addBBTrackAction = new QAction(embed::getIconPixmap("add_bb_track"),
826 tr("Add beat/bassline"), this);
827
828 m_addSampleTrackAction = new QAction(embed::getIconPixmap("add_sample_track"),
829 tr("Add sample-track"), this);
830
831 m_addAutomationTrackAction = new QAction(embed::getIconPixmap("add_automation"),
832 tr("Add automation-track"), this);
833
834 connect(m_addBBTrackAction, SIGNAL(triggered()), m_editor->m_song, SLOT(addBBTrack()));
835 connect(m_addSampleTrackAction, SIGNAL(triggered()), m_editor->m_song, SLOT(addSampleTrack()));
836 connect(m_addAutomationTrackAction, SIGNAL(triggered()), m_editor->m_song, SLOT(addAutomationTrack()));
837
838 trackActionsToolBar->addAction( m_addBBTrackAction );
839 trackActionsToolBar->addAction( m_addSampleTrackAction );
840 trackActionsToolBar->addAction( m_addAutomationTrackAction );
841
842
843 // Edit actions
844 DropToolBar *editActionsToolBar = addDropToolBarToTop(tr("Edit actions"));
845
846 ActionGroup* editModeGroup = new ActionGroup(this);
847 m_drawModeAction = editModeGroup->addAction(embed::getIconPixmap("edit_draw"), tr("Draw mode"));
848 m_selectModeAction = editModeGroup->addAction(embed::getIconPixmap("edit_select"), tr("Edit mode (select and move)"));
849
850 m_drawModeAction->setChecked(true);
851
852 connect(m_drawModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeDraw()));
853 connect(m_selectModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeSelect()));
854
855 editActionsToolBar->addAction( m_drawModeAction );
856 editActionsToolBar->addAction( m_selectModeAction );
857
858 DropToolBar *timeLineToolBar = addDropToolBarToTop(tr("Timeline controls"));
859 m_editor->m_timeLine->addToolButtons(timeLineToolBar);
860
861
862 DropToolBar *zoomToolBar = addDropToolBarToTop(tr("Zoom controls"));
863
864 QLabel * zoom_lbl = new QLabel( m_toolBar );
865 zoom_lbl->setPixmap( embed::getIconPixmap( "zoom" ) );
866
867 // setup zooming-stuff
868 m_zoomingComboBox = new ComboBox( m_toolBar );
869 m_zoomingComboBox->setFixedSize( 80, 22 );
870 m_zoomingComboBox->move( 580, 4 );
871 m_zoomingComboBox->setModel(m_editor->m_zoomingModel);
872
873 zoomToolBar->addWidget( zoom_lbl );
874 zoomToolBar->addWidget( m_zoomingComboBox );
875
876 connect(song, SIGNAL(projectLoaded()), this, SLOT(adjustUiAfterProjectLoad()));
877 connect(this, SIGNAL(resized()), m_editor, SLOT(updatePositionLine()));
878 }
879
sizeHint() const880 QSize SongEditorWindow::sizeHint() const
881 {
882 return {600, 300};
883 }
884
885
886
887
resizeEvent(QResizeEvent * event)888 void SongEditorWindow::resizeEvent(QResizeEvent *event)
889 {
890 emit resized();
891 }
892
893
changeEvent(QEvent * event)894 void SongEditorWindow::changeEvent(QEvent *event)
895 {
896 QWidget::changeEvent(event);
897 if (event->type() == QEvent::WindowStateChange)
898 {
899 m_editor->realignTracks();
900 }
901 }
902
903
play()904 void SongEditorWindow::play()
905 {
906 emit playTriggered();
907 if( Engine::getSong()->playMode() != Song::Mode_PlaySong )
908 {
909 Engine::getSong()->playSong();
910 }
911 else
912 {
913 Engine::getSong()->togglePause();
914 }
915 }
916
917
record()918 void SongEditorWindow::record()
919 {
920 m_editor->m_song->record();
921 }
922
923
recordAccompany()924 void SongEditorWindow::recordAccompany()
925 {
926 m_editor->m_song->playAndRecord();
927 }
928
929
stop()930 void SongEditorWindow::stop()
931 {
932 m_editor->m_song->stop();
933 gui->pianoRoll()->stopRecording();
934 }
935
adjustUiAfterProjectLoad()936 void SongEditorWindow::adjustUiAfterProjectLoad()
937 {
938 // make sure to bring us to front as the song editor is the central
939 // widget in a song and when just opening a song in order to listen to
940 // it, it's very annyoing to manually bring up the song editor each time
941 gui->mainWindow()->workspace()->setActiveSubWindow(
942 qobject_cast<QMdiSubWindow *>( parentWidget() ) );
943 m_editor->scrolled(0);
944 }
945