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