Pattern.cpp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  1. /*
  2. * Pattern.cpp - implementation of class pattern which holds notes
  3. *
  4. * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
  5. * Copyright (c) 2005-2007 Danny McRae <khjklujn/at/yahoo.com>
  6. *
  7. * This file is part of LMMS - https://lmms.io
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2 of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public
  20. * License along with this program (see COPYING); if not, write to the
  21. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  22. * Boston, MA 02110-1301 USA.
  23. *
  24. */
  25. #include "Pattern.h"
  26. #include <QApplication>
  27. #include <QTimer>
  28. #include <QMenu>
  29. #include <QMouseEvent>
  30. #include <QPainter>
  31. #include <QPushButton>
  32. #include <QTimer>
  33. #include "AudioSampleRecorder.h"
  34. #include "BBTrackContainer.h"
  35. #include "DeprecationHelper.h"
  36. #include "embed.h"
  37. #include "gui_templates.h"
  38. #include "GuiApplication.h"
  39. #include "InstrumentTrack.h"
  40. #include "PianoRoll.h"
  41. #include "RenameDialog.h"
  42. #include <limits>
  43. QPixmap * PatternView::s_stepBtnOn0 = NULL;
  44. QPixmap * PatternView::s_stepBtnOn200 = NULL;
  45. QPixmap * PatternView::s_stepBtnOff = NULL;
  46. QPixmap * PatternView::s_stepBtnOffLight = NULL;
  47. Pattern::Pattern( InstrumentTrack * _instrument_track ) :
  48. TrackContentObject( _instrument_track ),
  49. m_instrumentTrack( _instrument_track ),
  50. m_patternType( BeatPattern ),
  51. m_steps( TimePos::stepsPerBar() )
  52. {
  53. if( _instrument_track->trackContainer()
  54. == Engine::getBBTrackContainer() )
  55. {
  56. resizeToFirstTrack();
  57. }
  58. init();
  59. setAutoResize( true );
  60. }
  61. Pattern::Pattern( const Pattern& other ) :
  62. TrackContentObject( other.m_instrumentTrack ),
  63. m_instrumentTrack( other.m_instrumentTrack ),
  64. m_patternType( other.m_patternType ),
  65. m_steps( other.m_steps )
  66. {
  67. for( NoteVector::ConstIterator it = other.m_notes.begin(); it != other.m_notes.end(); ++it )
  68. {
  69. m_notes.push_back( new Note( **it ) );
  70. }
  71. init();
  72. switch( getTrack()->trackContainer()->type() )
  73. {
  74. case TrackContainer::BBContainer:
  75. setAutoResize( true );
  76. break;
  77. case TrackContainer::SongContainer:
  78. // move down
  79. default:
  80. setAutoResize( false );
  81. break;
  82. }
  83. }
  84. Pattern::~Pattern()
  85. {
  86. emit destroyedPattern( this );
  87. for( NoteVector::Iterator it = m_notes.begin();
  88. it != m_notes.end(); ++it )
  89. {
  90. delete *it;
  91. }
  92. m_notes.clear();
  93. }
  94. void Pattern::resizeToFirstTrack()
  95. {
  96. // Resize this track to be the same as existing tracks in the BB
  97. const TrackContainer::TrackList & tracks =
  98. m_instrumentTrack->trackContainer()->tracks();
  99. for(unsigned int trackID = 0; trackID < tracks.size(); ++trackID)
  100. {
  101. if(tracks.at(trackID)->type() == Track::InstrumentTrack)
  102. {
  103. if(tracks.at(trackID) != m_instrumentTrack)
  104. {
  105. unsigned int currentTCO = m_instrumentTrack->
  106. getTCOs().indexOf(this);
  107. m_steps = static_cast<Pattern *>
  108. (tracks.at(trackID)->getTCO(currentTCO))
  109. ->m_steps;
  110. }
  111. break;
  112. }
  113. }
  114. }
  115. void Pattern::init()
  116. {
  117. connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
  118. this, SLOT( changeTimeSignature() ) );
  119. saveJournallingState( false );
  120. updateLength();
  121. restoreJournallingState();
  122. }
  123. void Pattern::updateLength()
  124. {
  125. if( m_patternType == BeatPattern )
  126. {
  127. changeLength( beatPatternLength() );
  128. updateBBTrack();
  129. return;
  130. }
  131. tick_t max_length = TimePos::ticksPerBar();
  132. for( NoteVector::ConstIterator it = m_notes.begin();
  133. it != m_notes.end(); ++it )
  134. {
  135. if( ( *it )->length() > 0 )
  136. {
  137. max_length = qMax<tick_t>( max_length,
  138. ( *it )->endPos() );
  139. }
  140. }
  141. changeLength( TimePos( max_length ).nextFullBar() *
  142. TimePos::ticksPerBar() );
  143. updateBBTrack();
  144. }
  145. TimePos Pattern::beatPatternLength() const
  146. {
  147. tick_t max_length = TimePos::ticksPerBar();
  148. for( NoteVector::ConstIterator it = m_notes.begin();
  149. it != m_notes.end(); ++it )
  150. {
  151. if( ( *it )->length() < 0 )
  152. {
  153. max_length = qMax<tick_t>( max_length,
  154. ( *it )->pos() + 1 );
  155. }
  156. }
  157. if( m_steps != TimePos::stepsPerBar() )
  158. {
  159. max_length = m_steps * TimePos::ticksPerBar() /
  160. TimePos::stepsPerBar();
  161. }
  162. return TimePos( max_length ).nextFullBar() * TimePos::ticksPerBar();
  163. }
  164. Note * Pattern::addNote( const Note & _new_note, const bool _quant_pos )
  165. {
  166. Note * new_note = new Note( _new_note );
  167. if( _quant_pos && gui->pianoRoll() )
  168. {
  169. new_note->quantizePos( gui->pianoRoll()->quantization() );
  170. }
  171. instrumentTrack()->lock();
  172. m_notes.insert(std::upper_bound(m_notes.begin(), m_notes.end(), new_note, Note::lessThan), new_note);
  173. instrumentTrack()->unlock();
  174. checkType();
  175. updateLength();
  176. emit dataChanged();
  177. return new_note;
  178. }
  179. void Pattern::removeNote( Note * _note_to_del )
  180. {
  181. instrumentTrack()->lock();
  182. NoteVector::Iterator it = m_notes.begin();
  183. while( it != m_notes.end() )
  184. {
  185. if( *it == _note_to_del )
  186. {
  187. delete *it;
  188. m_notes.erase( it );
  189. break;
  190. }
  191. ++it;
  192. }
  193. instrumentTrack()->unlock();
  194. checkType();
  195. updateLength();
  196. emit dataChanged();
  197. }
  198. // returns a pointer to the note at specified step, or NULL if note doesn't exist
  199. Note * Pattern::noteAtStep( int _step )
  200. {
  201. for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end();
  202. ++it )
  203. {
  204. if( ( *it )->pos() == TimePos::stepPosition( _step )
  205. && ( *it )->length() < 0 )
  206. {
  207. return *it;
  208. }
  209. }
  210. return NULL;
  211. }
  212. void Pattern::rearrangeAllNotes()
  213. {
  214. // sort notes by start time
  215. std::sort(m_notes.begin(), m_notes.end(), Note::lessThan);
  216. }
  217. void Pattern::clearNotes()
  218. {
  219. instrumentTrack()->lock();
  220. for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end();
  221. ++it )
  222. {
  223. delete *it;
  224. }
  225. m_notes.clear();
  226. instrumentTrack()->unlock();
  227. checkType();
  228. emit dataChanged();
  229. }
  230. Note * Pattern::addStepNote( int step )
  231. {
  232. return addNote( Note( TimePos( -DefaultTicksPerBar ),
  233. TimePos::stepPosition( step ) ), false );
  234. }
  235. void Pattern::setStep( int step, bool enabled )
  236. {
  237. if( enabled )
  238. {
  239. if ( !noteAtStep( step ) )
  240. {
  241. addStepNote( step );
  242. }
  243. return;
  244. }
  245. while( Note * note = noteAtStep( step ) )
  246. {
  247. removeNote( note );
  248. }
  249. }
  250. void Pattern::splitNotes(NoteVector notes, TimePos pos)
  251. {
  252. if (notes.empty()) { return; }
  253. addJournalCheckPoint();
  254. for (int i = 0; i < notes.size(); ++i)
  255. {
  256. Note* note = notes.at(i);
  257. int leftLength = pos.getTicks() - note->pos();
  258. int rightLength = note->length() - leftLength;
  259. // Split out of bounds
  260. if (leftLength <= 0 || rightLength <= 0)
  261. {
  262. continue;
  263. }
  264. // Reduce note length
  265. note->setLength(leftLength);
  266. // Add new note with the remaining length
  267. Note newNote = Note(*note);
  268. newNote.setLength(rightLength);
  269. newNote.setPos(note->pos() + leftLength);
  270. addNote(newNote, false);
  271. }
  272. }
  273. void Pattern::setType( PatternTypes _new_pattern_type )
  274. {
  275. if( _new_pattern_type == BeatPattern ||
  276. _new_pattern_type == MelodyPattern )
  277. {
  278. m_patternType = _new_pattern_type;
  279. }
  280. }
  281. void Pattern::checkType()
  282. {
  283. NoteVector::Iterator it = m_notes.begin();
  284. while( it != m_notes.end() )
  285. {
  286. if( ( *it )->length() > 0 )
  287. {
  288. setType( MelodyPattern );
  289. return;
  290. }
  291. ++it;
  292. }
  293. setType( BeatPattern );
  294. }
  295. void Pattern::saveSettings( QDomDocument & _doc, QDomElement & _this )
  296. {
  297. _this.setAttribute( "type", m_patternType );
  298. _this.setAttribute( "name", name() );
  299. if( usesCustomClipColor() )
  300. {
  301. _this.setAttribute( "color", color().name() );
  302. }
  303. // as the target of copied/dragged pattern is always an existing
  304. // pattern, we must not store actual position, instead we store -1
  305. // which tells loadSettings() not to mess around with position
  306. if( _this.parentNode().nodeName() == "clipboard" ||
  307. _this.parentNode().nodeName() == "dnddata" )
  308. {
  309. _this.setAttribute( "pos", -1 );
  310. }
  311. else
  312. {
  313. _this.setAttribute( "pos", startPosition() );
  314. }
  315. _this.setAttribute( "muted", isMuted() );
  316. _this.setAttribute( "steps", m_steps );
  317. // now save settings of all notes
  318. for( NoteVector::Iterator it = m_notes.begin();
  319. it != m_notes.end(); ++it )
  320. {
  321. ( *it )->saveState( _doc, _this );
  322. }
  323. }
  324. void Pattern::loadSettings( const QDomElement & _this )
  325. {
  326. m_patternType = static_cast<PatternTypes>( _this.attribute( "type"
  327. ).toInt() );
  328. setName( _this.attribute( "name" ) );
  329. if( _this.hasAttribute( "color" ) )
  330. {
  331. useCustomClipColor( true );
  332. setColor( _this.attribute( "color" ) );
  333. }
  334. if( _this.attribute( "pos" ).toInt() >= 0 )
  335. {
  336. movePosition( _this.attribute( "pos" ).toInt() );
  337. }
  338. if( _this.attribute( "muted" ).toInt() != isMuted() )
  339. {
  340. toggleMute();
  341. }
  342. clearNotes();
  343. QDomNode node = _this.firstChild();
  344. while( !node.isNull() )
  345. {
  346. if( node.isElement() &&
  347. !node.toElement().attribute( "metadata" ).toInt() )
  348. {
  349. Note * n = new Note;
  350. n->restoreState( node.toElement() );
  351. m_notes.push_back( n );
  352. }
  353. node = node.nextSibling();
  354. }
  355. m_steps = _this.attribute( "steps" ).toInt();
  356. if( m_steps == 0 )
  357. {
  358. m_steps = TimePos::stepsPerBar();
  359. }
  360. checkType();
  361. updateLength();
  362. emit dataChanged();
  363. }
  364. Pattern * Pattern::previousPattern() const
  365. {
  366. return adjacentPatternByOffset(-1);
  367. }
  368. Pattern * Pattern::nextPattern() const
  369. {
  370. return adjacentPatternByOffset(1);
  371. }
  372. Pattern * Pattern::adjacentPatternByOffset(int offset) const
  373. {
  374. QVector<TrackContentObject *> tcos = m_instrumentTrack->getTCOs();
  375. int tcoNum = m_instrumentTrack->getTCONum(this);
  376. return dynamic_cast<Pattern*>(tcos.value(tcoNum + offset, NULL));
  377. }
  378. void Pattern::clear()
  379. {
  380. addJournalCheckPoint();
  381. clearNotes();
  382. }
  383. void Pattern::addSteps()
  384. {
  385. m_steps += TimePos::stepsPerBar();
  386. updateLength();
  387. emit dataChanged();
  388. }
  389. void Pattern::cloneSteps()
  390. {
  391. int oldLength = m_steps;
  392. m_steps *= 2; // cloning doubles the track
  393. for(int i = 0; i < oldLength; ++i )
  394. {
  395. Note *toCopy = noteAtStep( i );
  396. if( toCopy )
  397. {
  398. setStep( oldLength + i, true );
  399. Note *newNote = noteAtStep( oldLength + i );
  400. newNote->setKey( toCopy->key() );
  401. newNote->setLength( toCopy->length() );
  402. newNote->setPanning( toCopy->getPanning() );
  403. newNote->setVolume( toCopy->getVolume() );
  404. }
  405. }
  406. updateLength();
  407. emit dataChanged();
  408. }
  409. void Pattern::removeSteps()
  410. {
  411. int n = TimePos::stepsPerBar();
  412. if( n < m_steps )
  413. {
  414. for( int i = m_steps - n; i < m_steps; ++i )
  415. {
  416. setStep( i, false );
  417. }
  418. m_steps -= n;
  419. updateLength();
  420. emit dataChanged();
  421. }
  422. }
  423. TrackContentObjectView * Pattern::createView( TrackView * _tv )
  424. {
  425. return new PatternView( this, _tv );
  426. }
  427. void Pattern::updateBBTrack()
  428. {
  429. if( getTrack()->trackContainer() == Engine::getBBTrackContainer() )
  430. {
  431. Engine::getBBTrackContainer()->updateBBTrack( this );
  432. }
  433. if( gui && gui->pianoRoll() && gui->pianoRoll()->currentPattern() == this )
  434. {
  435. gui->pianoRoll()->update();
  436. }
  437. }
  438. bool Pattern::empty()
  439. {
  440. for( NoteVector::ConstIterator it = m_notes.begin();
  441. it != m_notes.end(); ++it )
  442. {
  443. if( ( *it )->length() != 0 )
  444. {
  445. return false;
  446. }
  447. }
  448. return true;
  449. }
  450. void Pattern::changeTimeSignature()
  451. {
  452. TimePos last_pos = TimePos::ticksPerBar() - 1;
  453. for( NoteVector::ConstIterator cit = m_notes.begin();
  454. cit != m_notes.end(); ++cit )
  455. {
  456. if( ( *cit )->length() < 0 && ( *cit )->pos() > last_pos )
  457. {
  458. last_pos = ( *cit )->pos()+TimePos::ticksPerBar() /
  459. TimePos::stepsPerBar();
  460. }
  461. }
  462. last_pos = last_pos.nextFullBar() * TimePos::ticksPerBar();
  463. m_steps = qMax<tick_t>( TimePos::stepsPerBar(),
  464. last_pos.getBar() * TimePos::stepsPerBar() );
  465. updateLength();
  466. }
  467. PatternView::PatternView( Pattern* pattern, TrackView* parent ) :
  468. TrackContentObjectView( pattern, parent ),
  469. m_pat( pattern ),
  470. m_paintPixmap(),
  471. m_noteFillColor(255, 255, 255, 220),
  472. m_noteBorderColor(255, 255, 255, 220),
  473. m_mutedNoteFillColor(100, 100, 100, 220),
  474. m_mutedNoteBorderColor(100, 100, 100, 220)
  475. {
  476. connect( gui->pianoRoll(), SIGNAL( currentPatternChanged() ),
  477. this, SLOT( update() ) );
  478. if( s_stepBtnOn0 == NULL )
  479. {
  480. s_stepBtnOn0 = new QPixmap( embed::getIconPixmap(
  481. "step_btn_on_0" ) );
  482. }
  483. if( s_stepBtnOn200 == NULL )
  484. {
  485. s_stepBtnOn200 = new QPixmap( embed::getIconPixmap(
  486. "step_btn_on_200" ) );
  487. }
  488. if( s_stepBtnOff == NULL )
  489. {
  490. s_stepBtnOff = new QPixmap( embed::getIconPixmap(
  491. "step_btn_off" ) );
  492. }
  493. if( s_stepBtnOffLight == NULL )
  494. {
  495. s_stepBtnOffLight = new QPixmap( embed::getIconPixmap(
  496. "step_btn_off_light" ) );
  497. }
  498. update();
  499. setStyle( QApplication::style() );
  500. }
  501. void PatternView::update()
  502. {
  503. ToolTip::add(this, m_pat->name());
  504. TrackContentObjectView::update();
  505. }
  506. void PatternView::openInPianoRoll()
  507. {
  508. gui->pianoRoll()->setCurrentPattern( m_pat );
  509. gui->pianoRoll()->parentWidget()->show();
  510. gui->pianoRoll()->show();
  511. gui->pianoRoll()->setFocus();
  512. }
  513. void PatternView::setGhostInPianoRoll()
  514. {
  515. gui->pianoRoll()->setGhostPattern( m_pat );
  516. gui->pianoRoll()->parentWidget()->show();
  517. gui->pianoRoll()->show();
  518. gui->pianoRoll()->setFocus();
  519. }
  520. void PatternView::resetName() { m_pat->setName(""); }
  521. void PatternView::changeName()
  522. {
  523. QString s = m_pat->name();
  524. RenameDialog rename_dlg( s );
  525. rename_dlg.exec();
  526. m_pat->setName( s );
  527. }
  528. void PatternView::constructContextMenu( QMenu * _cm )
  529. {
  530. QAction * a = new QAction( embed::getIconPixmap( "piano" ),
  531. tr( "Open in piano-roll" ), _cm );
  532. _cm->insertAction( _cm->actions()[0], a );
  533. connect( a, SIGNAL( triggered( bool ) ),
  534. this, SLOT( openInPianoRoll() ) );
  535. QAction * b = new QAction( embed::getIconPixmap( "ghost_note" ),
  536. tr( "Set as ghost in piano-roll" ), _cm );
  537. if( m_pat->empty() ) { b->setEnabled( false ); }
  538. _cm->insertAction( _cm->actions()[1], b );
  539. connect( b, SIGNAL( triggered( bool ) ),
  540. this, SLOT( setGhostInPianoRoll() ) );
  541. _cm->insertSeparator( _cm->actions()[2] );
  542. _cm->addSeparator();
  543. _cm->addAction( embed::getIconPixmap( "edit_erase" ),
  544. tr( "Clear all notes" ), m_pat, SLOT( clear() ) );
  545. _cm->addSeparator();
  546. _cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ),
  547. this, SLOT( resetName() ) );
  548. _cm->addAction( embed::getIconPixmap( "edit_rename" ),
  549. tr( "Change name" ),
  550. this, SLOT( changeName() ) );
  551. if ( m_pat->type() == Pattern::BeatPattern )
  552. {
  553. _cm->addSeparator();
  554. _cm->addAction( embed::getIconPixmap( "step_btn_add" ),
  555. tr( "Add steps" ), m_pat, SLOT( addSteps() ) );
  556. _cm->addAction( embed::getIconPixmap( "step_btn_remove" ),
  557. tr( "Remove steps" ), m_pat, SLOT( removeSteps() ) );
  558. _cm->addAction( embed::getIconPixmap( "step_btn_duplicate" ),
  559. tr( "Clone Steps" ), m_pat, SLOT( cloneSteps() ) );
  560. }
  561. }
  562. void PatternView::mousePressEvent( QMouseEvent * _me )
  563. {
  564. if( _me->button() == Qt::LeftButton &&
  565. m_pat->m_patternType == Pattern::BeatPattern &&
  566. ( fixedTCOs() || pixelsPerBar() >= 96 ) &&
  567. _me->y() > height() - s_stepBtnOff->height() )
  568. // when mouse button is pressed in beat/bassline -mode
  569. {
  570. // get the step number that was clicked on and
  571. // do calculations in floats to prevent rounding errors...
  572. float tmp = ( ( float(_me->x()) - TCO_BORDER_WIDTH ) *
  573. float( m_pat -> m_steps ) ) / float(width() - TCO_BORDER_WIDTH*2);
  574. int step = int( tmp );
  575. // debugging to ensure we get the correct step...
  576. // qDebug( "Step (%f) %d", tmp, step );
  577. if( step >= m_pat->m_steps )
  578. {
  579. qDebug( "Something went wrong in pattern.cpp: step %d doesn't exist in pattern!", step );
  580. return;
  581. }
  582. Note * n = m_pat->noteAtStep( step );
  583. if( n == NULL )
  584. {
  585. m_pat->addStepNote( step );
  586. }
  587. else // note at step found
  588. {
  589. m_pat->addJournalCheckPoint();
  590. m_pat->setStep( step, false );
  591. }
  592. Engine::getSong()->setModified();
  593. update();
  594. if( gui->pianoRoll()->currentPattern() == m_pat )
  595. {
  596. gui->pianoRoll()->update();
  597. }
  598. }
  599. else
  600. // if not in beat/bassline -mode, let parent class handle the event
  601. {
  602. TrackContentObjectView::mousePressEvent( _me );
  603. }
  604. }
  605. void PatternView::mouseDoubleClickEvent(QMouseEvent *_me)
  606. {
  607. if( _me->button() != Qt::LeftButton )
  608. {
  609. _me->ignore();
  610. return;
  611. }
  612. if( m_pat->m_patternType == Pattern::MelodyPattern || !fixedTCOs() )
  613. {
  614. openInPianoRoll();
  615. }
  616. }
  617. void PatternView::wheelEvent(QWheelEvent * we)
  618. {
  619. if(m_pat->m_patternType == Pattern::BeatPattern &&
  620. (fixedTCOs() || pixelsPerBar() >= 96) &&
  621. position(we).y() > height() - s_stepBtnOff->height())
  622. {
  623. // get the step number that was wheeled on and
  624. // do calculations in floats to prevent rounding errors...
  625. float tmp = ((float(position(we).x()) - TCO_BORDER_WIDTH) *
  626. float(m_pat -> m_steps)) / float(width() - TCO_BORDER_WIDTH*2);
  627. int step = int( tmp );
  628. if( step >= m_pat->m_steps )
  629. {
  630. return;
  631. }
  632. Note * n = m_pat->noteAtStep( step );
  633. if(!n && we->angleDelta().y() > 0)
  634. {
  635. n = m_pat->addStepNote( step );
  636. n->setVolume( 0 );
  637. }
  638. if( n != NULL )
  639. {
  640. int vol = n->getVolume();
  641. if(we->angleDelta().y() > 0)
  642. {
  643. n->setVolume( qMin( 100, vol + 5 ) );
  644. }
  645. else
  646. {
  647. n->setVolume( qMax( 0, vol - 5 ) );
  648. }
  649. Engine::getSong()->setModified();
  650. update();
  651. if( gui->pianoRoll()->currentPattern() == m_pat )
  652. {
  653. gui->pianoRoll()->update();
  654. }
  655. }
  656. we->accept();
  657. }
  658. else
  659. {
  660. TrackContentObjectView::wheelEvent(we);
  661. }
  662. }
  663. static int computeNoteRange(int minKey, int maxKey)
  664. {
  665. return (maxKey - minKey) + 1;
  666. }
  667. void PatternView::paintEvent( QPaintEvent * )
  668. {
  669. QPainter painter( this );
  670. if( !needsUpdate() )
  671. {
  672. painter.drawPixmap( 0, 0, m_paintPixmap );
  673. return;
  674. }
  675. setNeedsUpdate( false );
  676. if (m_paintPixmap.isNull() || m_paintPixmap.size() != size())
  677. {
  678. m_paintPixmap = QPixmap(size());
  679. }
  680. QPainter p( &m_paintPixmap );
  681. QColor c;
  682. bool const muted = m_pat->getTrack()->isMuted() || m_pat->isMuted();
  683. bool current = gui->pianoRoll()->currentPattern() == m_pat;
  684. bool beatPattern = m_pat->m_patternType == Pattern::BeatPattern;
  685. if( beatPattern )
  686. {
  687. // Do not paint BBTCOs how we paint pattern TCOs
  688. c = BBPatternBackground();
  689. }
  690. else
  691. {
  692. c = getColorForDisplay( painter.background().color() );
  693. }
  694. // invert the gradient for the background in the B&B editor
  695. QLinearGradient lingrad( 0, 0, 0, height() );
  696. lingrad.setColorAt( beatPattern ? 0 : 1, c.darker( 300 ) );
  697. lingrad.setColorAt( beatPattern ? 1 : 0, c );
  698. // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds
  699. p.fillRect( rect(), QColor( 0, 0, 0 ) );
  700. if( gradient() )
  701. {
  702. p.fillRect( rect(), lingrad );
  703. }
  704. else
  705. {
  706. p.fillRect( rect(), c );
  707. }
  708. // Check whether we will paint a text box and compute its potential height
  709. // This is needed so we can paint the notes underneath it.
  710. bool const drawName = !m_pat->name().isEmpty();
  711. bool const drawTextBox = !beatPattern && drawName;
  712. // TODO Warning! This might cause problems if TrackContentObjectView::paintTextLabel changes
  713. int textBoxHeight = 0;
  714. const int textTop = TCO_BORDER_WIDTH + 1;
  715. if (drawTextBox)
  716. {
  717. QFont labelFont = this->font();
  718. labelFont.setHintingPreference( QFont::PreferFullHinting );
  719. QFontMetrics fontMetrics(labelFont);
  720. textBoxHeight = fontMetrics.height() + 2 * textTop;
  721. }
  722. // Compute pixels per bar
  723. const int baseWidth = fixedTCOs() ? parentWidget()->width() - 2 * TCO_BORDER_WIDTH
  724. : width() - TCO_BORDER_WIDTH;
  725. const float pixelsPerBar = baseWidth / (float) m_pat->length().getBar();
  726. // Length of one bar/beat in the [0,1] x [0,1] coordinate system
  727. const float barLength = 1. / m_pat->length().getBar();
  728. const float tickLength = barLength / TimePos::ticksPerBar();
  729. const int x_base = TCO_BORDER_WIDTH;
  730. // melody pattern paint event
  731. NoteVector const & noteCollection = m_pat->m_notes;
  732. if( m_pat->m_patternType == Pattern::MelodyPattern && !noteCollection.empty() )
  733. {
  734. // Compute the minimum and maximum key in the pattern
  735. // so that we know how much there is to draw.
  736. int maxKey = std::numeric_limits<int>::min();
  737. int minKey = std::numeric_limits<int>::max();
  738. for (Note const * note : noteCollection)
  739. {
  740. int const key = note->key();
  741. maxKey = qMax( maxKey, key );
  742. minKey = qMin( minKey, key );
  743. }
  744. // If needed adjust the note range so that we always have paint a certain interval
  745. int const minimalNoteRange = 12; // Always paint at least one octave
  746. int const actualNoteRange = computeNoteRange(minKey, maxKey);
  747. if (actualNoteRange < minimalNoteRange)
  748. {
  749. int missingNumberOfNotes = minimalNoteRange - actualNoteRange;
  750. minKey = std::max(0, minKey - missingNumberOfNotes / 2);
  751. maxKey = maxKey + missingNumberOfNotes / 2;
  752. if (missingNumberOfNotes % 2 == 1)
  753. {
  754. // Put more range at the top to bias drawing towards the bottom
  755. ++maxKey;
  756. }
  757. }
  758. int const adjustedNoteRange = computeNoteRange(minKey, maxKey);
  759. // Transform such that [0, 1] x [0, 1] paints in the correct area
  760. float distanceToTop = textBoxHeight;
  761. // This moves the notes smoothly under the text
  762. int widgetHeight = height();
  763. int fullyAtTopAtLimit = MINIMAL_TRACK_HEIGHT;
  764. int fullyBelowAtLimit = 4 * fullyAtTopAtLimit;
  765. if (widgetHeight <= fullyBelowAtLimit)
  766. {
  767. if (widgetHeight <= fullyAtTopAtLimit)
  768. {
  769. distanceToTop = 0;
  770. }
  771. else
  772. {
  773. float const a = 1. / (fullyAtTopAtLimit - fullyBelowAtLimit);
  774. float const b = - float(fullyBelowAtLimit) / (fullyAtTopAtLimit - fullyBelowAtLimit);
  775. float const scale = a * widgetHeight + b;
  776. distanceToTop = (1. - scale) * textBoxHeight;
  777. }
  778. }
  779. int const notesBorder = 4; // Border for the notes towards the top and bottom in pixels
  780. // The relavant painting code starts here
  781. p.save();
  782. p.translate(0., distanceToTop + notesBorder);
  783. p.scale(width(), height() - distanceToTop - 2 * notesBorder);
  784. // set colour based on mute status
  785. QColor noteFillColor = muted ? getMutedNoteFillColor() : getNoteFillColor();
  786. QColor noteBorderColor = muted ? getMutedNoteBorderColor()
  787. : ( m_pat->hasColor() ? c.lighter( 200 ) : getNoteBorderColor() );
  788. bool const drawAsLines = height() < 64;
  789. if (drawAsLines)
  790. {
  791. p.setPen(noteFillColor);
  792. }
  793. else
  794. {
  795. p.setPen(noteBorderColor);
  796. p.setRenderHint(QPainter::Antialiasing);
  797. }
  798. // Needed for Qt5 although the documentation for QPainter::setPen(QColor) as it's used above
  799. // states that it should already set a width of 0.
  800. QPen pen = p.pen();
  801. pen.setWidth(0);
  802. p.setPen(pen);
  803. float const noteHeight = 1. / adjustedNoteRange;
  804. // scan through all the notes and draw them on the pattern
  805. for (Note const * currentNote : noteCollection)
  806. {
  807. // Map to 0, 1, 2, ...
  808. int mappedNoteKey = currentNote->key() - minKey;
  809. int invertedMappedNoteKey = adjustedNoteRange - mappedNoteKey - 1;
  810. float const noteStartX = currentNote->pos() * tickLength;
  811. float const noteLength = currentNote->length() * tickLength;
  812. float const noteStartY = invertedMappedNoteKey * noteHeight;
  813. QRectF noteRectF( noteStartX, noteStartY, noteLength, noteHeight);
  814. if (drawAsLines)
  815. {
  816. p.drawLine(QPointF(noteStartX, noteStartY + 0.5 * noteHeight),
  817. QPointF(noteStartX + noteLength, noteStartY + 0.5 * noteHeight));
  818. }
  819. else
  820. {
  821. p.fillRect( noteRectF, noteFillColor );
  822. p.drawRect( noteRectF );
  823. }
  824. }
  825. p.restore();
  826. }
  827. // beat pattern paint event
  828. else if( beatPattern && ( fixedTCOs() || pixelsPerBar >= 96 ) )
  829. {
  830. QPixmap stepon0;
  831. QPixmap stepon200;
  832. QPixmap stepoff;
  833. QPixmap stepoffl;
  834. const int steps = qMax( 1,
  835. m_pat->m_steps );
  836. const int w = width() - 2 * TCO_BORDER_WIDTH;
  837. // scale step graphics to fit the beat pattern length
  838. stepon0 = s_stepBtnOn0->scaled( w / steps,
  839. s_stepBtnOn0->height(),
  840. Qt::IgnoreAspectRatio,
  841. Qt::SmoothTransformation );
  842. stepon200 = s_stepBtnOn200->scaled( w / steps,
  843. s_stepBtnOn200->height(),
  844. Qt::IgnoreAspectRatio,
  845. Qt::SmoothTransformation );
  846. stepoff = s_stepBtnOff->scaled( w / steps,
  847. s_stepBtnOff->height(),
  848. Qt::IgnoreAspectRatio,
  849. Qt::SmoothTransformation );
  850. stepoffl = s_stepBtnOffLight->scaled( w / steps,
  851. s_stepBtnOffLight->height(),
  852. Qt::IgnoreAspectRatio,
  853. Qt::SmoothTransformation );
  854. for( int it = 0; it < steps; it++ ) // go through all the steps in the beat pattern
  855. {
  856. Note * n = m_pat->noteAtStep( it );
  857. // figure out x and y coordinates for step graphic
  858. const int x = TCO_BORDER_WIDTH + static_cast<int>( it * w / steps );
  859. const int y = height() - s_stepBtnOff->height() - 1;
  860. if( n )
  861. {
  862. const int vol = n->getVolume();
  863. p.drawPixmap( x, y, stepoffl );
  864. p.drawPixmap( x, y, stepon0 );
  865. p.setOpacity( sqrt( vol / 200.0 ) );
  866. p.drawPixmap( x, y, stepon200 );
  867. p.setOpacity( 1 );
  868. }
  869. else if( ( it / 4 ) % 2 )
  870. {
  871. p.drawPixmap( x, y, stepoffl );
  872. }
  873. else
  874. {
  875. p.drawPixmap( x, y, stepoff );
  876. }
  877. } // end for loop
  878. // draw a transparent rectangle over muted patterns
  879. if ( muted )
  880. {
  881. p.setBrush( mutedBackgroundColor() );
  882. p.setOpacity( 0.5 );
  883. p.drawRect( 0, 0, width(), height() );
  884. }
  885. }
  886. // bar lines
  887. const int lineSize = 3;
  888. p.setPen( c.darker( 200 ) );
  889. for( bar_t t = 1; t < m_pat->length().getBar(); ++t )
  890. {
  891. p.drawLine( x_base + static_cast<int>( pixelsPerBar * t ) - 1,
  892. TCO_BORDER_WIDTH, x_base + static_cast<int>(
  893. pixelsPerBar * t ) - 1, TCO_BORDER_WIDTH + lineSize );
  894. p.drawLine( x_base + static_cast<int>( pixelsPerBar * t ) - 1,
  895. rect().bottom() - ( lineSize + TCO_BORDER_WIDTH ),
  896. x_base + static_cast<int>( pixelsPerBar * t ) - 1,
  897. rect().bottom() - TCO_BORDER_WIDTH );
  898. }
  899. // pattern name
  900. if (drawTextBox)
  901. {
  902. paintTextLabel(m_pat->name(), p);
  903. }
  904. if( !( fixedTCOs() && beatPattern ) )
  905. {
  906. // inner border
  907. p.setPen( c.lighter( current ? 160 : 130 ) );
  908. p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH,
  909. rect().bottom() - TCO_BORDER_WIDTH );
  910. // outer border
  911. p.setPen( current ? c.lighter( 130 ) : c.darker( 300 ) );
  912. p.drawRect( 0, 0, rect().right(), rect().bottom() );
  913. }
  914. // draw the 'muted' pixmap only if the pattern was manually muted
  915. if( m_pat->isMuted() )
  916. {
  917. const int spacing = TCO_BORDER_WIDTH;
  918. const int size = 14;
  919. p.drawPixmap( spacing, height() - ( size + spacing ),
  920. embed::getIconPixmap( "muted", size, size ) );
  921. }
  922. painter.drawPixmap( 0, 0, m_paintPixmap );
  923. }