1 /*
2 * TrackContainerView.cpp - view-component for TrackContainer
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 "TrackContainerView.h"
26
27 #include <cmath>
28
29 #include <QApplication>
30 #include <QLayout>
31 #include <QMdiArea>
32 #include <QWheelEvent>
33
34 #include "TrackContainer.h"
35 #include "BBTrack.h"
36 #include "MainWindow.h"
37 #include "Mixer.h"
38 #include "FileBrowser.h"
39 #include "ImportFilter.h"
40 #include "Instrument.h"
41 #include "Song.h"
42 #include "StringPairDrag.h"
43 #include "GuiApplication.h"
44 #include "PluginFactory.h"
45
46 using namespace std;
47
TrackContainerView(TrackContainer * _tc)48 TrackContainerView::TrackContainerView( TrackContainer * _tc ) :
49 QWidget(),
50 ModelView( NULL, this ),
51 JournallingObject(),
52 SerializingObjectHook(),
53 m_currentPosition( 0, 0 ),
54 m_tc( _tc ),
55 m_trackViews(),
56 m_scrollArea( new scrollArea( this ) ),
57 m_ppt( DEFAULT_PIXELS_PER_TACT ),
58 m_rubberBand( new RubberBand( m_scrollArea ) )
59 {
60 m_tc->setHook( this );
61 //keeps the direction of the widget, undepended on the locale
62 setLayoutDirection( Qt::LeftToRight );
63 QVBoxLayout * layout = new QVBoxLayout( this );
64 layout->setMargin( 0 );
65 layout->setSpacing( 0 );
66 layout->addWidget( m_scrollArea );
67
68 QWidget * scrollContent = new QWidget;
69 m_scrollLayout = new QVBoxLayout( scrollContent );
70 m_scrollLayout->setMargin( 0 );
71 m_scrollLayout->setSpacing( 0 );
72 m_scrollLayout->setSizeConstraint( QLayout::SetMinAndMaxSize );
73
74 m_scrollArea->setWidget( scrollContent );
75
76 m_scrollArea->show();
77 m_rubberBand->hide();
78 m_rubberBand->setEnabled( false );
79
80 setAcceptDrops( true );
81
82 connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
83 this, SLOT( realignTracks() ) );
84 connect( m_tc, SIGNAL( trackAdded( Track * ) ),
85 this, SLOT( createTrackView( Track * ) ),
86 Qt::QueuedConnection );
87 }
88
89
90
91
~TrackContainerView()92 TrackContainerView::~TrackContainerView()
93 {
94 while( !m_trackViews.empty() )
95 {
96 delete m_trackViews.takeLast();
97 }
98 }
99
100
101
102
103
saveSettings(QDomDocument & _doc,QDomElement & _this)104 void TrackContainerView::saveSettings( QDomDocument & _doc,
105 QDomElement & _this )
106 {
107 MainWindow::saveWidgetState( this, _this );
108 }
109
110
111
112
loadSettings(const QDomElement & _this)113 void TrackContainerView::loadSettings( const QDomElement & _this )
114 {
115 MainWindow::restoreWidgetState( this, _this );
116 }
117
118
119
120
addTrackView(TrackView * _tv)121 TrackView * TrackContainerView::addTrackView( TrackView * _tv )
122 {
123 m_trackViews.push_back( _tv );
124 m_scrollLayout->addWidget( _tv );
125 connect( this, SIGNAL( positionChanged( const MidiTime & ) ),
126 _tv->getTrackContentWidget(),
127 SLOT( changePosition( const MidiTime & ) ) );
128 realignTracks();
129 return( _tv );
130 }
131
132
133
134
removeTrackView(TrackView * _tv)135 void TrackContainerView::removeTrackView( TrackView * _tv )
136 {
137 int index = m_trackViews.indexOf( _tv );
138 if( index != -1 )
139 {
140 m_trackViews.removeAt( index );
141
142 disconnect( _tv );
143 m_scrollLayout->removeWidget( _tv );
144
145 realignTracks();
146 if( Engine::getSong() )
147 {
148 Engine::getSong()->setModified();
149 }
150 }
151 }
152
153
154
155
moveTrackView(TrackView * trackView,int indexTo)156 void TrackContainerView::moveTrackView( TrackView * trackView, int indexTo )
157 {
158 // Can't move out of bounds
159 if ( indexTo >= m_trackViews.size() || indexTo < 0 ) { return; }
160
161 // Does not need to move to itself
162 int indexFrom = m_trackViews.indexOf( trackView );
163 if ( indexFrom == indexTo ) { return; }
164
165 BBTrack::swapBBTracks( trackView->getTrack(),
166 m_trackViews[indexTo]->getTrack() );
167
168 m_scrollLayout->removeWidget( trackView );
169 m_scrollLayout->insertWidget( indexTo, trackView );
170
171 Track * track = m_tc->m_tracks[indexFrom];
172
173 m_tc->m_tracks.remove( indexFrom );
174 m_tc->m_tracks.insert( indexTo, track );
175 m_trackViews.move( indexFrom, indexTo );
176
177 realignTracks();
178 }
179
180
181
182
moveTrackViewUp(TrackView * trackView)183 void TrackContainerView::moveTrackViewUp( TrackView * trackView )
184 {
185 int index = m_trackViews.indexOf( trackView );
186
187 moveTrackView( trackView, index - 1 );
188 }
189
190
191
192
moveTrackViewDown(TrackView * trackView)193 void TrackContainerView::moveTrackViewDown( TrackView * trackView )
194 {
195 int index = m_trackViews.indexOf( trackView );
196
197 moveTrackView( trackView, index + 1 );
198 }
199
scrollToTrackView(TrackView * _tv)200 void TrackContainerView::scrollToTrackView( TrackView * _tv )
201 {
202 if (!m_trackViews.contains(_tv))
203 {
204 qWarning("TrackContainerView::scrollToTrackView: TrackView is not owned by this");
205 }
206 else
207 {
208 int currentScrollTop = m_scrollArea->verticalScrollBar()->value();
209 int scrollAreaHeight = m_scrollArea->size().height();
210 int trackViewTop = _tv->pos().y();
211 int trackViewBottom = trackViewTop + _tv->size().height();
212
213 // displayed_location = widget_location - currentScrollTop
214 // want to make sure that the widget top has displayed location > 0,
215 // and widget bottom < scrollAreaHeight
216 // trackViewTop - scrollY > 0 && trackViewBottom - scrollY < scrollAreaHeight
217 // therefore scrollY < trackViewTop && scrollY > trackViewBottom - scrollAreaHeight
218 int newScroll = std::max( trackViewBottom-scrollAreaHeight, std::min(currentScrollTop, trackViewTop) );
219 m_scrollArea->verticalScrollBar()->setValue(newScroll);
220 }
221 }
222
223
224
225
realignTracks()226 void TrackContainerView::realignTracks()
227 {
228 QWidget * content = m_scrollArea->widget();
229 content->setFixedWidth( width()
230 - m_scrollArea->verticalScrollBar()->width() );
231 content->setFixedHeight( content->minimumSizeHint().height() );
232
233 for( trackViewList::iterator it = m_trackViews.begin();
234 it != m_trackViews.end(); ++it )
235 {
236 ( *it )->show();
237 ( *it )->update();
238 }
239 }
240
241
242
243
createTrackView(Track * _t)244 TrackView * TrackContainerView::createTrackView( Track * _t )
245 {
246 //m_tc->addJournalCheckPoint();
247
248 // Avoid duplicating track views
249 for( trackViewList::iterator it = m_trackViews.begin();
250 it != m_trackViews.end(); ++it )
251 {
252 if ( ( *it )->getTrack() == _t ) { return ( *it ); }
253 }
254
255 return _t->createView( this );
256 }
257
258
259
260
deleteTrackView(TrackView * _tv)261 void TrackContainerView::deleteTrackView( TrackView * _tv )
262 {
263 //m_tc->addJournalCheckPoint();
264
265 Track * t = _tv->getTrack();
266 removeTrackView( _tv );
267 delete _tv;
268
269 Engine::mixer()->requestChangeInModel();
270 delete t;
271 Engine::mixer()->doneChangeInModel();
272 }
273
274
275
276
trackViewAt(const int _y) const277 const TrackView * TrackContainerView::trackViewAt( const int _y ) const
278 {
279 const int abs_y = _y + m_scrollArea->verticalScrollBar()->value();
280 int y_cnt = 0;
281
282 // debug code
283 // qDebug( "abs_y %d", abs_y );
284
285 for( trackViewList::const_iterator it = m_trackViews.begin();
286 it != m_trackViews.end(); ++it )
287 {
288 const int y_cnt1 = y_cnt;
289 y_cnt += ( *it )->height();
290 if( abs_y >= y_cnt1 && abs_y < y_cnt )
291 {
292 return( *it );
293 }
294 }
295 return( NULL );
296 }
297
298
299
300
allowRubberband() const301 bool TrackContainerView::allowRubberband() const
302 {
303 return( false );
304 }
305
306
307
308
setPixelsPerTact(int _ppt)309 void TrackContainerView::setPixelsPerTact( int _ppt )
310 {
311 m_ppt = _ppt;
312
313 // tell all TrackContentWidgets to update their background tile pixmap
314 for( trackViewList::Iterator it = m_trackViews.begin();
315 it != m_trackViews.end(); ++it )
316 {
317 ( *it )->getTrackContentWidget()->updateBackground();
318 }
319 }
320
321
322
323
clearAllTracks()324 void TrackContainerView::clearAllTracks()
325 {
326 while( !m_trackViews.empty() )
327 {
328 TrackView * tv = m_trackViews.takeLast();
329 Track * t = tv->getTrack();
330 delete tv;
331 delete t;
332 }
333 }
334
335
336
337
dragEnterEvent(QDragEnterEvent * _dee)338 void TrackContainerView::dragEnterEvent( QDragEnterEvent * _dee )
339 {
340 StringPairDrag::processDragEnterEvent( _dee,
341 QString( "presetfile,pluginpresetfile,samplefile,instrument,"
342 "importedproject,soundfontfile,patchfile,vstpluginfile,projectfile,"
343 "track_%1,track_%2" ).
344 arg( Track::InstrumentTrack ).
345 arg( Track::SampleTrack ) );
346 }
347
348
349
350
stopRubberBand()351 void TrackContainerView::stopRubberBand()
352 {
353 m_rubberBand->hide();
354 m_rubberBand->setEnabled( false );
355 }
356
357
358
359
dropEvent(QDropEvent * _de)360 void TrackContainerView::dropEvent( QDropEvent * _de )
361 {
362 QString type = StringPairDrag::decodeKey( _de );
363 QString value = StringPairDrag::decodeValue( _de );
364 if( type == "instrument" )
365 {
366 InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
367 Track::create( Track::InstrumentTrack,
368 m_tc ) );
369 InstrumentLoaderThread *ilt = new InstrumentLoaderThread(
370 this, it, value );
371 ilt->start();
372 //it->toggledInstrumentTrackButton( true );
373 _de->accept();
374 }
375 else if( type == "samplefile" || type == "pluginpresetfile"
376 || type == "soundfontfile" || type == "vstpluginfile"
377 || type == "patchfile" )
378 {
379 InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
380 Track::create( Track::InstrumentTrack,
381 m_tc ) );
382 Instrument * i = it->loadInstrument(
383 pluginFactory->pluginSupportingExtension(FileItem::extension(value)).name());
384 i->loadFile( value );
385 //it->toggledInstrumentTrackButton( true );
386 _de->accept();
387 }
388 else if( type == "presetfile" )
389 {
390 DataFile dataFile( value );
391 InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
392 Track::create( Track::InstrumentTrack,
393 m_tc ) );
394 it->setSimpleSerializing();
395 it->loadSettings( dataFile.content().toElement() );
396 //it->toggledInstrumentTrackButton( true );
397 _de->accept();
398 }
399 else if( type == "importedproject" )
400 {
401 ImportFilter::import( value, m_tc );
402 _de->accept();
403 }
404
405 else if( type == "projectfile")
406 {
407 if( gui->mainWindow()->mayChangeProject(true) )
408 {
409 Engine::getSong()->loadProject( value );
410 }
411 _de->accept();
412 }
413
414 else if( type.left( 6 ) == "track_" )
415 {
416 DataFile dataFile( value.toUtf8() );
417 Track::create( dataFile.content().firstChild().toElement(), m_tc );
418 _de->accept();
419 }
420 }
421
422
423
424
resizeEvent(QResizeEvent * _re)425 void TrackContainerView::resizeEvent( QResizeEvent * _re )
426 {
427 realignTracks();
428 QWidget::resizeEvent( _re );
429 }
430
431
432
433
rubberBand() const434 RubberBand *TrackContainerView::rubberBand() const
435 {
436 return m_rubberBand;
437 }
438
439
440
441
scrollArea(TrackContainerView * _parent)442 TrackContainerView::scrollArea::scrollArea( TrackContainerView * _parent ) :
443 QScrollArea( _parent ),
444 m_trackContainerView( _parent )
445 {
446 setFrameStyle( QFrame::NoFrame );
447 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
448 setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
449 }
450
451
452
453
~scrollArea()454 TrackContainerView::scrollArea::~scrollArea()
455 {
456 }
457
458
459
460
wheelEvent(QWheelEvent * _we)461 void TrackContainerView::scrollArea::wheelEvent( QWheelEvent * _we )
462 {
463 // always pass wheel-event to parent-widget (song-editor
464 // bb-editor etc.) because they might want to use it for zooming
465 // or scrolling left/right if a modifier-key is pressed, otherwise
466 // they do not accept it and we pass it up to QScrollArea
467 m_trackContainerView->wheelEvent( _we );
468 if( !_we->isAccepted() )
469 {
470 QScrollArea::wheelEvent( _we );
471 }
472 }
473
474
475
476
InstrumentLoaderThread(QObject * parent,InstrumentTrack * it,QString name)477 InstrumentLoaderThread::InstrumentLoaderThread( QObject *parent, InstrumentTrack *it, QString name ) :
478 QThread( parent ),
479 m_it( it ),
480 m_name( name )
481 {
482 m_containerThread = thread();
483 }
484
485
486
487
run()488 void InstrumentLoaderThread::run()
489 {
490 Instrument *i = m_it->loadInstrument( m_name );
491 QObject *parent = i->parent();
492 i->setParent( 0 );
493 i->moveToThread( m_containerThread );
494 i->setParent( parent );
495 }
496