76 , m_bSequenceChanged( true )
77 , m_pScrollView( pScrollView )
78 , m_pSongEditorPanel( pSongEditorPanel )
80 , m_pHydrogen( nullptr )
81 , m_pAudioEngine( nullptr )
83 , m_pBackgroundPixmap( nullptr )
84 , m_pSequencePixmap( nullptr )
94 connect(
m_pScrollView->verticalScrollBar(), SIGNAL( valueChanged(
int ) ),
this, SLOT(
scrolled(
int ) ) );
95 connect(
m_pScrollView->horizontalScrollBar(), SIGNAL( valueChanged(
int ) ),
this, SLOT(
scrolled(
int ) ) );
97 setAttribute(Qt::WA_OpaquePaintEvent);
98 setFocusPolicy (Qt::StrongFocus);
107 int nInitialHeight = 10;
109 this->resize( QSize( nInitialWidth, nInitialHeight ) );
161 const int nScroll = pScrollArea->verticalScrollBar()->value();
162 int nHeight = pScrollArea->height();
167 if ( pPlayingPatterns->size() == 0 ) {
177 std::vector<Pattern *> currentPatterns;
178 for (
int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
179 currentPatterns.push_back( pPlayingPatterns->get( ii ) );
183 std::vector<int> playingRows;
184 for (
Pattern *pPattern : currentPatterns ) {
185 playingRows.push_back( pSongPatterns->
index( pPattern ) );
190 if ( playingRows.size() == 0 ) {
195 for (
int r : playingRows ) {
199 if ( pnPatternInView ) {
200 *pnPatternInView = r;
218 std::sort( playingRows.begin(), playingRows.end() );
221 int nAboveMax = 0, nAbovePattern = -1, nAboveClosestPattern = -1,
222 nBelowMax = 0, nBelowPattern = -1, nBelowClosestPattern = -1;
224 for (
int nBottomIdx = 0; nBottomIdx < playingRows.size(); nBottomIdx++) {
231 if ( nTop < nBottom - nHeight ) {
233 assert( nTopIdx <= nBottomIdx && nTopIdx < playingRows.size() );
238 int nPatternsInViewport = nBottomIdx - nTopIdx +1;
239 if ( nBottom < nScroll ) {
242 if ( nPatternsInViewport >= nAboveMax ) {
243 nAboveMax = nPatternsInViewport;
246 nAbovePattern = playingRows[ nTopIdx ];
247 nAboveClosestPattern = playingRows[ nBottomIdx ];
252 if ( nPatternsInViewport > nBelowMax ) {
253 nBelowMax = nPatternsInViewport;
256 nBelowPattern = playingRows[ nBottomIdx ];
257 nBelowClosestPattern = playingRows[ nTopIdx ];
265 enum { Up, Down } direction = Down;
266 if ( nAboveMax != 0) {
267 if ( nAboveMax > nBelowMax ) {
270 }
else if ( nBelowMax > nAboveMax ) {
275 assert( nAboveY <= nScroll && nScroll <= nBelowY );
276 if ( nScroll - nAboveY < nBelowY - nScroll ) {
283 assert( nBelowMax != 0 );
288 if ( direction == Up ) {
289 if ( pnPatternInView ) {
290 *pnPatternInView = nAboveClosestPattern;
294 if ( pnPatternInView ) {
295 *pnPatternInView = nBelowClosestPattern;
340 bool bPatternIsActive = pSong->isPatternActive( nColumn, nRow );
342 if ( bPatternIsActive && ! bActivate || ! bPatternIsActive && bActivate ) {
350 std::vector<PatternList*>* pColumns =
m_pHydrogen->
getSong()->getPatternGroupVector();
352 for (
int nRow = 0; nRow < pPatternList->
size(); nRow++ ) {
354 for (
int nCol = 0; nCol < pColumns->size(); nCol++ ) {
356 for ( uint i = 0; i < pColumn->
size(); i++) {
357 if ( pColumn->
get(i) == pPattern ) {
376 std::vector< QPoint > addCells, deleteCells, mergeCells;
378 deleteCells.push_back( cell );
381 tr(
"Delete selected cells" ) ) );
400 bool bWrotePattern =
false;
406 if ( bWrotePattern ) {
407 nMinX = std::min( nMinX, cell.x() );
408 nMinY = std::min( nMinY, cell.y() );
412 bWrotePattern =
true;
415 if ( !bWrotePattern) {
419 positionNode.
write_int(
"column", nMinX );
422 QApplication::clipboard()->setText( doc.toString() );
430 int nDeltaColumn = 0, nDeltaRow = 0;
432 int nPatterns = pSong->getPatternList()->size();
435 if ( ! doc.setContent( QApplication::clipboard()->text() ) ) {
443 XMLNode selection = doc.firstChildElement(
"patternSelection" );
444 if ( ! selection.isNull() ) {
446 std::vector< QPoint > addCells, deleteCells, mergeCells;
448 XMLNode cellList = selection.firstChildElement(
"cellList" );
449 if ( cellList.isNull() ) {
453 XMLNode positionNode = selection.firstChildElement(
"sourcePosition" );
458 if ( !positionNode.isNull() ) {
464 if ( cellList.hasChildNodes() ) {
465 for (
XMLNode cellNode = cellList.firstChildElement(
"cell" );
467 cellNode = cellNode.nextSiblingElement() ) {
469 int nRow = cellNode.read_int(
"y",
m_nCursorRow ) + nDeltaRow;
470 if ( nCol >= 0 && nRow >= 0 && nRow < nPatterns ) {
472 QPoint p = QPoint( nCol, nRow );
475 addCells.push_back( p );
478 mergeCells.push_back( p );
484 tr(
"Paste cells" ) ) );
498 auto pSong = pHydrogen->getSong();
499 const int nBlockSize = 5, nWordSize = 5;
501 bool bIsSelectionKey =
false;
502 bool bUnhideCursor =
true;
504 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
512 PatternList *pPatternList = pSong->getPatternList();
514 bool bSelectionKey =
false;
520 if ( bIsSelectionKey ) {
522 }
else if ( ev->key() == Qt::Key_Delete ) {
531 }
else if ( ev->matches( QKeySequence::MoveToNextChar ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextChar ) ) ) {
537 }
else if ( ev->matches( QKeySequence::MoveToNextWord ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextWord ) ) ) {
541 }
else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectEndOfLine ) ) ) {
545 }
else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousChar ) ) ) {
551 }
else if ( ev->matches( QKeySequence::MoveToPreviousWord ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousWord ) ) ) {
555 }
else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectStartOfLine ) ) ) {
559 }
else if ( ev->matches( QKeySequence::MoveToNextLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextLine ) ) ) {
560 if ( m_nCursorRow < pPatternList->size()-1 ) {
564 }
else if ( ev->matches( QKeySequence::MoveToEndOfBlock ) || ( bSelectionKey = ev->matches( QKeySequence::SelectEndOfBlock ) ) ) {
567 }
else if ( ev->matches( QKeySequence::MoveToNextPage ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextPage ) ) ) {
569 QWidget *pParent =
dynamic_cast< QWidget *
>( parent() );
577 }
else if ( ev->matches( QKeySequence::MoveToEndOfDocument ) || ( bSelectionKey = ev->matches( QKeySequence::SelectEndOfDocument ) ) ) {
580 }
else if ( ev->matches( QKeySequence::MoveToPreviousLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousLine ) ) ) {
585 }
else if ( ev->matches( QKeySequence::MoveToStartOfBlock ) || ( bSelectionKey = ev->matches( QKeySequence::SelectStartOfBlock ) ) ) {
589 }
else if ( ev->matches( QKeySequence::MoveToPreviousPage ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousPage ) ) ) {
590 QWidget *pParent =
dynamic_cast< QWidget *
>( parent() );
598 }
else if ( ev->matches( QKeySequence::MoveToStartOfDocument ) || ( bSelectionKey = ev->matches( QKeySequence::SelectStartOfDocument ) ) ) {
601 }
else if ( ev->matches( QKeySequence::SelectAll ) ) {
603 bSelectionKey =
true;
604 bUnhideCursor =
false;
609 }
else if ( ev->matches( QKeySequence::Deselect ) ) {
611 bSelectionKey =
true;
612 bUnhideCursor =
false;
618 }
else if ( ev->matches( QKeySequence::Copy ) ) {
619 bUnhideCursor =
false;
622 }
else if ( ev->matches( QKeySequence::Paste ) ) {
623 bUnhideCursor =
false;
625 }
else if ( ev->matches( QKeySequence::Cut ) ) {
626 bUnhideCursor =
false;
629 }
else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
638 pHydrogenApp->setHideKeyboardCursor(
true );
640 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
641 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
642 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
647 if ( bUnhideCursor ) {
648 pHydrogenApp->setHideKeyboardCursor(
false );
651 if ( bSelectionKey ) {
666 m_pScrollView->ensureVisible( cursorCentre.x(), cursorCentre.y() );
669 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
670 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
671 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
684 if ( ev->reason() == Qt::TabFocusReason || ev->reason() == Qt::BacktabFocusReason ) {
721 int nAx = a.x(), nBx = b.x();
725 int nAy = a.y(), nBy = b.y();
742 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
744 pHydrogenApp->setHideKeyboardCursor(
true );
748 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
749 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
750 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
755 if ( ev->button() == Qt::LeftButton ) {
762 }
else if ( ev->button() == Qt::RightButton ) {
768 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
769 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
770 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
778 if ( ev->modifiers() == Qt::ControlModifier ) {
784 if ( QKeyEvent *pEv =
dynamic_cast<QKeyEvent*
>( ev ) ) {
787 if ( pEv->key() == Qt::Key_Control ) {
797 setCursor( QCursor( Qt::DragCopyCursor ) );
798 }
else if ( !
m_bCopyNotMove && cursor().shape() != Qt::DragMoveCursor ) {
799 setCursor( QCursor( Qt::DragMoveCursor ) );
811 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
829 if (
m_nCursorRow >= pSong->getPatternList()->size() ) {
839 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
840 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
841 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
863 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
864 PatternList *pPatternList = pSong->getPatternList();
865 int nMaxPattern = pPatternList->
size();
869 if ( offset == QPoint( 0, 0 ) ) {
872 std::vector< QPoint > addCells, deleteCells, mergeCells;
879 deleteCells.push_back( cell );
881 QPoint newCell = cell + offset;
883 if ( newCell.x() >= 0 && newCell.y() >= 0 && newCell.y() < nMaxPattern ) {
885 addCells.push_back( newCell );
888 mergeCells.push_back( newCell );
895 ? tr(
"Copy selected cells" )
896 : tr(
"Move selected cells" ) ) ) );
903 if ( ev->button() == Qt::LeftButton ) {
915 }
else if ( ev->button() == Qt::RightButton ) {
933 for ( QPoint cell : deleteCells ) {
938 for ( QPoint cell : addCells ) {
943 for ( QPoint cell : selectCells ) {
962 if ( bCellBoundaryCrossed ) {
981 QRect updateRect( nX + nOffset -2, 0, 4, height() );
982 update( updateRect );
983 if ( fDiff > 1.0 || fDiff < -1.0 ) {
986 updateRect.translate( -fDiff, 0 );
987 update( updateRect );
1027 QPainter painter(
this);
1031 QColor patternColor( 0, 0, 0 );
1038 .marginsRemoved( QMargins( 2, 4, 1 , 3 ) );
1039 painter.fillRect( r, patternColor );
1047 painter.drawLine( nX + nOffset, 0, nX + nOffset, height() );
1056 QPen p( pPref->getColorTheme()->m_cursorColor );
1058 painter.setPen( p );
1059 painter.setRenderHint( QPainter::Antialiasing );
1080 if ( ! hasFocus() ) {
1081 color.setAlpha( 125 );
1084 int nStartX =
m_pScrollView->horizontalScrollBar()->value();
1085 int nEndX = std::min( nStartX +
m_pScrollView->viewport()->size().width(), width() );
1092 painter.setPen( pen );
1093 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nEndX, nStartY ) );
1094 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nStartX, nEndY ) );
1095 painter.drawLine( QPoint( nEndX, nStartY ), QPoint( nEndX, nEndY ) );
1096 painter.drawLine( QPoint( nEndX, nEndY ), QPoint( nStartX, nEndY ) );
1122 uint nPatterns = pSong->getPatternList()->size();
1124 int nMaxPatternSequence = pPref->getMaxBars();
1126 static int nOldHeight = -1;
1129 if (nOldHeight != nNewHeight) {
1131 if (nNewHeight == 0) {
1142 this->resize( QSize( width(), nNewHeight ) );
1150 for (
int ii = 0; ii < nPatterns + 1; ii++) {
1151 if ( ( ii % 2 ) == 0 &&
1152 ii != nSelectedPatternNumber ) {
1158 if ( ii == nSelectedPatternNumber ) {
1160 pPref->getColorTheme()->m_songEditor_selectedRowColor );
1163 pPref->getColorTheme()->m_songEditor_alternateRowColor );
1167 p.setPen( QPen( pPref->getColorTheme()->m_songEditor_lineColor, 1,
1171 for (
float ii = 0; ii <= nMaxPatternSequence + 1; ii++) {
1177 for (uint i = 0; i < nPatterns; i++) {
1180 p.drawLine( 0, y, (nMaxPatternSequence *
m_nGridWidth), y );
1197 PatternList *pPatternList = pSong->getPatternList();
1198 std::vector< PatternList* > *pColumns = pSong->getPatternGroupVector();
1200 for (
int nColumn = 0; nColumn < pColumns->size(); nColumn++ ) {
1204 for ( uint nPat = 0; nPat < pColumn->
size(); nPat++ ) {
1205 Pattern *pPattern = (*pColumn)[ nPat ];
1206 int y = pPatternList->
index( pPattern );
1215 pVCell->
m_fWidth = (float) pVPattern->get_length() / nMaxLength;
1226 if ( rawOffset.y() < 0 ) {
1229 if ( rawOffset.x() < 0 ) {
1232 int x_off = (rawOffset.x() + x_bias) / (
int)
m_nGridWidth;
1234 return QPoint( x_off, y_off );
1252 it.second.m_bDrawnVirtual, it.second.m_fWidth );
1261 it.second.m_bDrawnVirtual, it.second.m_fWidth );
1276 PatternList *pPatternList = pSong->getPatternList();
1278 QColor patternColor;
1288 int nSteps = pPatternList->
size();
1295 int nHue = ( (nNumber % nSteps) * (300 / nSteps) + 213) % 300;
1296 patternColor.setHsv( nHue , 156 , 249);
1298 int nIndex = nNumber % pPref->getVisiblePatternColors();
1302 patternColor = pPref->getPatternColors()[ nIndex ].toHsv();
1305 if (
true == bInvertColour ) {
1306 patternColor = patternColor.darker(200);
1311 if ( bIsSelected ) {
1312 patternColor = patternColor.darker( 130 );
1315 patternColor.setAlpha( 230 );
1327 borderColor = pPref->getColorTheme()->m_selectionHighlightColor;
1329 borderColor = pPref->getColorTheme()->m_selectionInactiveColor;
1332 borderColor = QColor( 0, 0, 0 );
1334 p.setPen( borderColor );
1340 std::vector<SelectionIndex> elems;
1344 if ( ! it.second.m_bDrawnVirtual ) {
1345 elems.push_back( it.first );
1363 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1366 pSong->writeTempPatternList( filename );
1368 std::vector<PatternList*> *pPatternGroupsVect = pSong->getPatternGroupVector();
1369 for (uint i = 0; i < pPatternGroupsVect->size(); i++) {
1370 PatternList *pPatternList = (*pPatternGroupsVect)[i];
1371 pPatternList->
clear();
1372 delete pPatternList;
1374 pPatternGroupsVect->clear();
1413 , m_pBackgroundPixmap( nullptr )
1414 , m_nRowHovered( -1 )
1423 setAttribute(Qt::WA_OpaquePaintEvent);
1425 setAcceptDrops(
true);
1426 setMouseTracking(
true );
1430 m_pLineEdit =
new QLineEdit(
"Inline Pattern Name",
this );
1464 QScrollArea *pScrollArea =
dynamic_cast< QScrollArea *
>( parentWidget()->parentWidget() );
1465 assert( pScrollArea );
1473 qreal pixelRatio = devicePixelRatio();
1475 height() * pixelRatio );
1543 if ( pSong ==
nullptr ) {
1547 auto pPatternList = pSong->getPatternList();
1549 if ( nRow < 0 || nRow >= (
int)pPatternList->size() ) {
1550 ERRORLOG( QString(
"Row [%1] out of bound" ).arg( nRow ) );
1554 if ( ( ev->button() == Qt::MiddleButton ||
1555 ( ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::RightButton ) ||
1556 ( ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton ) ||
1557 ev->pos().x() < 15 ) &&
1565 AudioEngine::State::Playing ) ) {
1575 if (ev->button() == Qt::RightButton) {
1581 ERRORLOG(
"A dialog is still opened. It needs to be closed first." );
1616 PatternList *pPatternList = pSong->getPatternList();
1618 if ( row >= (
int)pPatternList->
size() ) {
1634 PatternList *pPatternList = pSong->getPatternList();
1666 auto pSongEditor = pHydrogenApp->getSongEditorPanel()->getSongEditor();
1668 QPainter painter(
this);
1669 qreal pixelRatio = devicePixelRatio();
1677 pixelRatio * ev->rect().x(),
1678 pixelRatio * ev->rect().y(),
1679 pixelRatio * ev->rect().width(),
1680 pixelRatio * ev->rect().height()
1686 if ( ( ! pHydrogenApp->hideKeyboardCursor() &&
1687 pSongEditor->hasFocus() ) ||
1689 QColor colorHighlight = pPref->getColorTheme()->m_highlightColor;
1695 pen.setColor( pPref->getColorTheme()->m_highlightColor);
1698 pen.setColor( pPref->getColorTheme()->m_cursorColor );
1702 painter.setRenderHint( QPainter::Antialiasing );
1704 painter.setPen( pen );
1705 painter.drawRoundedRect( QRect( 1, nStartY + 1,
m_nWidth - 2,
1724 Song::PatternMode::Stacked ) {
1740 QFont boldTextFont( pPref->getLevel2FontFamily(),
getPointSize( pPref->getFontSize() ) );
1741 boldTextFont.setBold(
true );
1750 if ( pSong ==
nullptr ) {
1755 const auto pPatternList = pSong->getPatternList();
1756 int nPatterns = pPatternList->size();
1764 if (newHeight == 0) {
1768 qreal pixelRatio = devicePixelRatio();
1771 this->resize(
m_nWidth, newHeight );
1774 QColor backgroundColor = pPref->getColorTheme()->m_songEditor_backgroundColor.darker( 120 );
1775 QColor backgroundColorSelected = pPref->getColorTheme()->m_songEditor_selectedRowColor.darker( 114 );
1776 QColor backgroundColorAlternate =
1777 pPref->getColorTheme()->m_songEditor_alternateRowColor.darker( 132 );
1778 QColor backgroundColorVirtual =
1779 pPref->getColorTheme()->m_songEditor_virtualRowColor;
1786 p.fillRect( QRect( 0, 0, width(), 1 ), pPref->getColorTheme()->m_windowColor );
1788 p.setFont( boldTextFont );
1789 for (
int ii = 0; ii < nPatterns; ii++ ) {
1792 if ( ii == nSelectedPattern ) {
1794 backgroundColorSelected,
false );
1796 const auto pPattern = pPatternList->get( ii );
1797 if ( pPattern !=
nullptr && pPattern->isVirtual() ) {
1799 backgroundColorVirtual,
1802 else if ( ( ii % 2 ) == 0 ) {
1809 backgroundColorAlternate,
1815 std::unique_ptr<PatternDisplayInfo[]> PatternArray{
new PatternDisplayInfo[nPatterns]};
1821 for (
int i = 0; i < nPatterns; i++ ) {
1823 if ( pPattern ==
nullptr ) {
1827 if ( pPlayingPatterns->index( pPattern ) != -1 ) {
1828 PatternArray[i].bActive =
true;
1830 PatternArray[i].bActive =
false;
1834 PatternArray[i].bNext =
true;
1836 PatternArray[i].bNext =
false;
1839 PatternArray[i].sPatternName = pPattern->
get_name();
1844 for (
int i = 0; i < nPatterns; i++ ) {
1845 if ( i == nSelectedPattern ) {
1846 p.setPen( pPref->getColorTheme()->m_songEditor_selectedRowTextColor );
1849 p.setPen( pPref->getColorTheme()->m_songEditor_textColor );
1855 Qt::AlignVCenter, PatternArray[i].sPatternName);
1858 if ( PatternArray[i].bNext && PatternArray[i].bActive) {
1861 else if ( PatternArray[i].bNext ) {
1864 else if (PatternArray[i].bActive) {
1886 if ( pSong ==
nullptr ) {
1890 PatternList *pPatternList = pSong->getPatternList();
1891 if ( pPatternList ==
nullptr ) {
1899 QListWidget* pPatternListWidget = pDialog->patternList;
1900 pPatternListWidget->setSortingEnabled(1);
1904 std::map<QString, Pattern*> patternNameMap;
1906 for (
const auto& pPattern : *pPatternList ) {
1907 QString sPatternName = pPattern->
get_name();
1909 if ( sPatternName == pPatternClicked->get_name() ) {
1914 patternNameMap[ sPatternName ] = pPattern;
1916 QListWidgetItem* pNewItem =
1917 new QListWidgetItem( sPatternName, pPatternListWidget);
1918 pPatternListWidget->insertItem( 0, pNewItem );
1920 if ( pPatternClicked->get_virtual_patterns()->find( pPattern ) !=
1921 pPatternClicked->get_virtual_patterns()->end() ) {
1923 pNewItem->setSelected(
true );
1927 if ( pDialog->exec() == QDialog::Accepted ) {
1928 pPatternClicked->virtual_patterns_clear();
1929 for (
int ii = 0; ii < pPatternListWidget->count(); ++ii ) {
1930 QListWidgetItem* pListItem = pPatternListWidget->item( ii );
1931 if ( pListItem !=
nullptr && pListItem->isSelected() ) {
1932 if ( patternNameMap.find( pListItem->text() ) !=
1933 patternNameMap.end() ) {
1934 pPatternClicked->virtual_patterns_add(
1935 patternNameMap[ pListItem->text() ] );
1938 ERRORLOG( QString(
"Selected pattern [%1] could not be retrieved" )
1939 .arg( pListItem->text() ) );
1959 auto pSong = pHydrogen->getSong();
1961 if ( pSong ==
nullptr ) {
1974 fd.setAcceptMode( QFileDialog::AcceptOpen );
1975 fd.setFileMode( QFileDialog::ExistingFile );
1977 fd.setDirectory( sPath );
1978 fd.setWindowTitle( QString( tr(
"Open Pattern to Replace " )
1979 .append( pPattern->
get_name() ) ) );
1981 if (fd.exec() != QDialog::Accepted) {
1985 QString patternPath = fd.selectedFiles().first();
1987 QString prevPatternPath =
1989 pHydrogen->getLastLoadedDrumkitName() );
1990 if ( prevPatternPath.isEmpty() ) {
1991 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save pattern to temporary directory.") );
1996 if ( !pSong->writeTempPatternList( sequencePath ) ) {
1997 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export sequence.") );
2026 auto pCommonStrings = pHydrogenApp->getCommonStrings();
2028 auto pSong = pHydrogen->getSong();
2029 auto pPattern = pSong->getPatternList()->get(
m_nRowClicked );
2032 pSong, pHydrogen->getLastLoadedDrumkitName() );
2033 if ( sPath.isEmpty() ) {
2034 if ( QMessageBox::information(
this,
"Hydrogen", tr(
"The pattern-file exists. \nOverwrite the existing pattern?"),
2035 pCommonStrings->getButtonOk(),
2036 pCommonStrings->getButtonCancel(),
2037 nullptr, 1 ) != 0 ) {
2042 pSong, pHydrogen->getLastLoadedDrumkitName() );
2045 if ( sPath.isEmpty() ) {
2046 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export pattern.") );
2051 pHydrogenApp->showStatusBarMessage( tr(
"Pattern saved." ) );
2053 pHydrogen->getSoundLibraryDatabase()->updatePatterns();
2073 auto pPattern = pHydrogen->getSong()->getPatternList()->get(
m_nRowClicked );
2088 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
2089 PatternList *patternList = pSong->getPatternList();
2091 pattern->
set_name( newPatternName );
2092 pattern->
set_info( newPatternInfo );
2104 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
2105 PatternList *patternList = pSong->getPatternList();
2107 pattern->
set_name( oldPatternName );
2121 auto pPattern = pSong->getPatternList()->get(
m_nRowClicked );
2123 QString patternPath =
2126 if ( patternPath.isEmpty() ) {
2127 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save pattern to temporary directory.") );
2132 if ( !pSong->writeTempPatternList( sequencePath ) ) {
2133 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export sequence.") );
2152 PatternList *pPatternList = pSong->getPatternList();
2158 if ( dialog->exec() == QDialog::Accepted ) {
2162 if ( filePath.isEmpty() ) {
2163 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save pattern to temporary directory.") );
2186 if ( dialog->exec() == QDialog::Accepted ) {
2204 PatternList *pPatternList = pSong->getPatternList();
2206 std::vector<PatternList*> *pColumns = pSong->getPatternGroupVector();
2209 int nColumn, nColumnIndex;
2210 bool bHasPattern =
false;
2211 int fromVal = pRange->
fromVal - 1;
2212 int toVal = pRange->
toVal;
2215 int nDelta = toVal - pColumns->size() + 1;
2217 for (
int i = 0; i < nDelta; i++ ) {
2219 pColumns->push_back( pColumn );
2223 for ( nColumn = fromVal; nColumn < toVal; nColumn++ ) {
2226 pColumn = ( *pColumns )[ nColumn ];
2230 bHasPattern =
false;
2233 for ( nColumnIndex = 0; pColumn && nColumnIndex < (int)pColumn->
size(); nColumnIndex++) {
2235 if ( pColumn->
get( nColumnIndex ) == pPattern ) {
2241 if ( pRange->
bInsert && !bHasPattern ) {
2242 pColumn->
add( pPattern);
2244 else if ( !pRange->
bInsert && bHasPattern ) {
2245 pColumn->
del( pPattern);
2250 for (
int i = pColumns->size() - 1; i != 0 ; i-- ) {
2252 int nSize = pList->
size();
2254 pColumns->erase( pColumns->begin() + i );
2273 if ( event->mimeData()->hasFormat(
"text/plain") ) {
2274 event->acceptProposedAction();
2283 QString sText =
event->mimeData()->text();
2284 const QMimeData* mimeData =
event->mimeData();
2286 int nTargetPattern = 0;
2292 if( sText.startsWith(
"Songs:") || sText.startsWith(
"move instrument:") || sText.startsWith(
"importInstrument:")){
2293 event->acceptProposedAction();
2297 if ( sText.startsWith(
"move pattern:") ) {
2298 QStringList tokens = sText.split(
":" );
2301 int nSourcePattern = tokens[1].toInt(&bOK);
2306 if ( nSourcePattern == nTargetPattern ) {
2307 event->acceptProposedAction();
2314 event->acceptProposedAction();
2316 else if( sText.startsWith(
"file://") && mimeData->hasUrls() )
2319 PatternList *pPatternList = pSong->getPatternList();
2320 QList<QUrl> urlList = mimeData->urls();
2322 int successfullyAddedPattern = 0;
2324 for (
int i = 0; i < urlList.size(); i++)
2326 QString patternFilePath = urlList.at(i).toLocalFile();
2327 if( patternFilePath.endsWith(
".h2pattern") )
2341 successfullyAddedPattern++;
2345 ERRORLOG( QString(
"Error loading pattern %1").arg(patternFilePath) );
2352 QStringList tokens = sText.split(
"::" );
2353 QString sPatternName = tokens.at( 1 );
2356 Pattern *pPattern = pSong->getPatternList()->get( nTargetPattern );
2359 QString oldPatternName = pPattern->
get_name();
2363 if( QString( tokens.at(0) ).contains(
"drag pattern" )) drag =
true;
2376 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
2377 PatternList *pPatternList = pSong->getPatternList();
2383 if ( nSourcePattern < nTargetPattern) {
2384 for (
int nPatr = nSourcePattern; nPatr < nTargetPattern; nPatr++) {
2386 pPatternList->
replace( nPatr, pPattern );
2388 pPatternList->
replace( nTargetPattern, pSourcePattern );
2391 for (
int nPatr = nSourcePattern; nPatr > nTargetPattern; nPatr--) {
2393 pPatternList->
replace( nPatr, pPattern );
2395 pPatternList->
replace( nTargetPattern, pSourcePattern );
2423 if (!(event->buttons() & Qt::LeftButton)) {
2430 PatternList *pPatternList = pSong->getPatternList();
2432 if ( row >= (
int)pPatternList->
size() ) {
2435 Pattern *pPattern = pPatternList->
get( row );
2436 QString sName =
"<unknown>";
2440 QString sText = QString(
"move pattern:%1:%2").arg( row ).arg( sName );
2442 QDrag *pDrag =
new QDrag(
this);
2443 QMimeData *pMimeData =
new QMimeData;
2445 pMimeData->setText( sText );
2446 pDrag->setMimeData( pMimeData);
2450 pDrag->exec( Qt::CopyAction | Qt::MoveAction );
2453 QWidget::mouseMoveEvent(event);
2476 , m_bRightBtnPressed( false )
2477 , m_nActiveBpmWidgetColumn( -1 )
2478 , m_nHoveredColumn( -1 )
2492 setAttribute(Qt::WA_OpaquePaintEvent);
2493 setMouseTracking(
true );
2505 qreal pixelRatio = devicePixelRatio();
2513 connect(
m_pTimer, &QTimer::timeout, [=]() {
2548 if ( SONG_EDITOR_MIN_GRID_WIDTH <= width && SONG_EDITOR_MAX_GRID_WIDTH >= width )
2565 auto pSong = pHydrogen->getSong();
2566 auto pTimeline = pHydrogen->getTimeline();
2567 auto tagVector = pTimeline->getAllTags();
2569 QColor textColor( pPref->
getColorTheme()->m_songEditor_textColor );
2570 QColor textColorAlpha( textColor );
2571 textColorAlpha.setAlpha( 45 );
2573 QColor backgroundColor = pPref->
getColorTheme()->m_songEditor_alternateRowColor.darker( 115 );
2574 QColor backgroundInactiveColor = pPref->
getColorTheme()->m_midLightColor;
2575 QColor backgroundColorTempoMarkers = backgroundColor.darker( 120 );
2577 QColor colorHighlight = pPref->
getColorTheme()->m_highlightColor;
2579 QColor lineColor = pPref->
getColorTheme()->m_songEditor_lineColor;
2580 QColor lineColorAlpha( lineColor );
2581 lineColorAlpha.setAlpha( 45 );
2584 qreal pixelRatio = devicePixelRatio();
2599 p.fillRect( 0, 0, width(), height(), backgroundColorTempoMarkers );
2600 p.fillRect( 0, 25, nActiveWidth, height() - 25, backgroundColor );
2601 p.fillRect( nActiveWidth, 25, width() - nActiveWidth, height() - 25,
2602 backgroundInactiveColor );
2605 int nMaxPatternSequence = pPref->
getMaxBars();
2607 QColor textColorGrid( textColor );
2608 textColorGrid.setAlpha( 200 );
2609 p.setPen( QPen( textColorGrid, 1, Qt::SolidLine ) );
2610 for (
int ii = 0; ii < nMaxPatternSequence + 1; ii++) {
2613 if ( ( ii % 4 ) == 0 ) {
2614 p.drawLine( x, height() - 14, x, height() - 1);
2617 p.drawLine( x, height() - 6, x, height() - 1);
2622 p.setPen( textColor );
2623 for (uint i = 0; i < nMaxPatternSequence + 1; i += 4) {
2626 sprintf( tmp,
"%d", i + 1 );
2628 p.drawText( x, height() / 2 + 3,
m_nGridWidth, height() / 2 - 7,
2629 Qt::AlignHCenter, tmp );
2631 p.drawText( x + 2, height() / 2 + 3,
m_nGridWidth * 3.5, height() / 2 - 7,
2632 Qt::AlignLeft, tmp );
2642 for (
const auto& ttag : tagVector ){
2647 p.fillRect( rect, pPref->
getColorTheme()->m_highlightColor.darker( 135 ) );
2648 p.drawText( rect, Qt::AlignCenter,
"T");
2655 if ( ! pHydrogen->isTimelineEnabled() ) {
2656 p.setPen( textColorAlpha );
2658 QColor tempoMarkerGridColor( textColor );
2659 tempoMarkerGridColor.setAlpha( 170 );
2660 p.setPen( tempoMarkerGridColor );
2662 for (uint ii = 0; ii < nMaxPatternSequence + 1; ii++) {
2665 p.drawLine( x, 1, x, 4 );
2666 p.drawLine( x, height() / 2 - 5, x, height() / 2 );
2670 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
2671 for (
const auto& ttempoMarker : tempoMarkerVector ){
2675 p.setPen( QColor(35, 39, 51) );
2676 p.drawLine( 0, 0, width(), 0 );
2677 p.drawLine( 0, height() - 25, width(), height() - 25 );
2678 p.drawLine( 0, height(), width(), height() );
2685 if ( ! pTimeline->isFirstTempoMarkerSpecial() ) {
2692 if ( pTimeline->getAllTempoMarkers().size() == 1 ) {
2717 QWidget::leaveEvent( ev );
2724 int nColumn = std::max(
xToColumn( ev->x() ), 0 );
2727 if ( ev->y() > 22 ) {
2748 }
else if ( ev->buttons() & Qt::RightButton ) {
2765 if ( ev->type() == QEvent::ToolTip ) {
2766 const auto helpEv =
dynamic_cast<QHelpEvent*
>(ev);
2767 showToolTip( helpEv->pos(), helpEv->globalPos() );
2771 return QWidget::event( ev );
2792 if ( nValue == 0 ) {
2800 auto pTimeline = pHydrogen->getTimeline();
2802 const int nColumn = std::max(
xToColumn( pos.x() ), 0 );
2804 if ( pHydrogen->isTimelineEnabled() &&
2805 pTimeline->isFirstTempoMarkerSpecial() &&
2808 const QString sBpm =
2809 pTimeline->getTempoMarkerAtColumn( nColumn )->getPrettyString( -1 );
2810 QToolTip::showText( globalPos, QString(
"%1: %2" )
2811 .arg( tr(
"The tempo set in the BPM widget will be used as a default for the beginning of the song. Left-click to overwrite it." ) )
2812 .arg( sBpm ),
this );
2816 if ( pTimeline->hasColumnTempoMarker( nColumn ) ) {
2817 const QString sBpm =
2818 pTimeline->getTempoMarkerAtColumn( nColumn )->getPrettyString( -1 );
2819 QToolTip::showText( globalPos, sBpm,
this );
2824 if ( pTimeline->hasColumnTag( nColumn ) ) {
2825 QToolTip::showText( globalPos,
2826 pTimeline->getTagAtColumn( nColumn ),
this );
2839 bool bTempoMarkerPresent =
2855 auto pCoreActionController = pHydrogen->getCoreActionController();
2857 int nColumn = std::max(
xToColumn( ev->x() ), 0 );
2859 if (ev->button() == Qt::LeftButton ) {
2860 if ( ev->y() > 22 ) {
2869 pCoreActionController->activateSongMode(
true );
2883 else if ( ev->button() == Qt::MiddleButton ) {
2886 else if (ev->button() == Qt::RightButton && ev->y() >= 26) {
2917 auto pSongEditor = pHydrogenApp->getSongEditorPanel()->getSongEditor();
2920 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
2930 QColor textColor( pPref->getColorTheme()->m_songEditor_textColor );
2931 QColor textColorAlpha( textColor );
2932 textColorAlpha.setAlpha( 45 );
2933 QColor highlightColor = pPref->getColorTheme()->m_highlightColor;
2934 QColor colorHovered( highlightColor );
2935 colorHovered.setAlpha( 200 );
2936 QColor backgroundColor = pPref->getColorTheme()->m_songEditor_alternateRowColor.darker( 115 );
2937 QColor backgroundColorTempoMarkers = backgroundColor.darker( 120 );
2942 QPainter painter(
this);
2943 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
2944 qreal pixelRatio = devicePixelRatio();
2949 pixelRatio * ev->rect().x(),
2950 pixelRatio * ev->rect().y(),
2951 pixelRatio * ev->rect().width(),
2952 pixelRatio * ev->rect().height()
2957 int nCurrentTempoMarkerColumn = -1;
2958 for (
const auto& tempoMarker : tempoMarkerVector ) {
2959 if ( tempoMarker->nColumn >
m_nColumn ) {
2962 nCurrentTempoMarkerColumn = tempoMarker->nColumn;
2964 if ( nCurrentTempoMarkerColumn == -1 &&
2965 tempoMarkerVector.size() != 0 ) {
2966 auto pTempoMarker = tempoMarkerVector[ tempoMarkerVector.size() - 1 ];
2967 if ( pTempoMarker !=
nullptr ) {
2968 nCurrentTempoMarkerColumn = pTempoMarker->nColumn;
2971 if ( nCurrentTempoMarkerColumn != -1 ) {
2972 auto pTempoMarker = pTimeline->getTempoMarkerAtColumn( nCurrentTempoMarkerColumn );
2973 if ( pTempoMarker !=
nullptr ) {
2977 painter.fillRect( rect, backgroundColorTempoMarkers );
2987 painter.drawLine( nX + nShaftOffset, 0, nX + nShaftOffset, height() );
3005 painter.fillRect( x, 1, 1, 4,
3006 backgroundColorTempoMarkers );
3007 painter.fillRect( x, height() / 2 - 5, 1, 4,
3008 backgroundColorTempoMarkers );
3014 backgroundColorTempoMarkers );
3017 painter.setPen( QPen( highlightColor, 1 ) );
3019 painter.drawLine( x, 1, x, 4 );
3020 painter.drawLine( x, height() / 2 - 5, x, height() / 2 - 1 );
3024 bool bTagPresent =
false;
3032 QFont font2( pPref->getApplicationFontFamily(), 5 );
3033 painter.setFont( font2 );
3035 painter.fillRect( rect, pPref->getColorTheme()->m_highlightColor );
3036 painter.setPen( pPref->getColorTheme()->m_highlightedTextColor );
3037 painter.drawText( rect, Qt::AlignCenter,
"T");
3039 painter.setFont( font );
3047 bool bTempoMarkerPresent =
false;
3048 if ( ! bTagPresent &&
3060 if ( pTimeline->hasColumnTempoMarker( nColumn ) ||
3061 ( pTimeline->isFirstTempoMarkerSpecial() &&
3064 auto pTempoMarker = pTimeline->getTempoMarkerAtColumn( nColumn );
3065 if ( pTempoMarker !=
nullptr ) {
3067 const bool bEmphasize = pTempoMarker->nColumn == nCurrentTempoMarkerColumn;
3070 painter.fillRect( rect, backgroundColorTempoMarkers );
3074 painter.setPen( QPen( colorHovered, 1 ) );
3076 painter.setPen( QPen( highlightColor, 1 ) );
3078 painter.drawRect( rect );
3080 painter.drawLine( rect.x(), 2, rect.x() +
m_nGridWidth / 2, 2 );
3082 bTempoMarkerPresent =
true;
3092 ! bTempoMarkerPresent ) ) ) ||
3094 ! bTempoMarkerPresent ) ) {
3098 color = colorHovered;
3100 color = highlightColor;
3104 painter.setPen( p );
3118 QRect hoveringRect( nCursorX, 6,
m_nGridWidth - 5, 12 );
3119 painter.fillRect( hoveringRect, backgroundColorTempoMarkers );
3120 painter.drawRect( hoveringRect );
3122 painter.drawRect( nCursorX, height() / 2 - 1 -
m_nTagHeight,
3128 if ( ! pHydrogenApp->hideKeyboardCursor() && pSongEditor->hasFocus() ) {
3129 int nCursorX =
columnToX( pSongEditor->getCursorColumn() ) + 2;
3131 QColor cursorColor = pPref->getColorTheme()->m_cursorColor;
3133 QPen p( cursorColor );
3135 painter.setPen( p );
3136 painter.setRenderHint( QPainter::Antialiasing );
3140 painter.drawLine( nCursorX, height() / 2 + 3,
3142 painter.drawLine( nCursorX, height() / 2 + 4,
3143 nCursorX, height() / 2 + 5 );
3144 painter.drawLine( nCursorX +
m_nGridWidth - 3, height() / 2 + 4,
3146 painter.drawLine( nCursorX, height() - 4,
3148 painter.drawLine( nCursorX, height() - 6,
3149 nCursorX, height() - 5 );
3150 painter.drawLine( nCursorX +
m_nGridWidth - 3, height() - 6,
3162 painter.drawLine( x + nShaftOffset, 0, x + nShaftOffset, height() / 2 + 1 );
3164 x + nShaftOffset, height() );
3167 if ( nPunchInPos <= nPunchOutPos ) {
3168 const int xIn =
columnToX( nPunchInPos );
3169 const int xOut =
columnToX( nPunchOutPos + 1 );
3170 painter.fillRect( xIn, 30, xOut-xIn+1, 12, QColor(200, 100, 100, 100) );
3171 QPen pen(QColor(200, 100, 100));
3172 painter.setPen(pen);
3173 painter.drawRect( xIn, 30, xOut-xIn+1, 12 );
3178 assert( pTempoMarker );
3182 auto weight = QFont::Normal;
3184 weight = QFont::Bold;
3187 const QFont font( pPref->getApplicationFontFamily(),
3190 const int x =
columnToX( pTempoMarker->nColumn );
3191 int nWidth = QFontMetrics( font ).size(
3192 Qt::TextSingleLine, pTempoMarker->getPrettyString( 2 ) ).width();
3196 const int nCoveredNeighborColumns =
3197 static_cast<int>(std::floor(
static_cast<float>(nWidth) /
3199 for (
int ii = 1; ii <= nCoveredNeighborColumns; ++ii ) {
3200 if ( pTimeline->hasColumnTempoMarker( pTempoMarker->nColumn + ii ) ) {
3206 QRect rect( x, 6, nWidth, 12 );
3208 return std::move( rect );
3212 assert( pTempoMarker );
3216 auto pSong = pHydrogen->getSong();
3217 auto pTimeline = pHydrogen->getTimeline();
3221 if ( pTempoMarker->nColumn == 0 && pTimeline->isFirstTempoMarkerSpecial() &&
3222 ! pHydrogen->isTimelineEnabled() ) {
3226 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
3233 QColor textColor( pPref->getColorTheme()->m_songEditor_textColor );
3235 if ( pTempoMarker->nColumn == 0 && pTimeline->isFirstTempoMarkerSpecial() ) {
3236 textColor = textColor.darker( 150 );
3239 if ( ! pHydrogen->isTimelineEnabled() ) {
3240 QColor textColorAlpha( textColor );
3241 textColorAlpha.setAlpha( 45 );
3242 painter.setPen( textColorAlpha );
3244 QColor tempoMarkerGridColor( textColor );
3245 tempoMarkerGridColor.setAlpha( 170 );
3246 painter.setPen( tempoMarkerGridColor );
3249 painter.drawLine( rect.x(), 2, rect.x() +
m_nGridWidth / 2, 2 );
3251 QColor tempoMarkerColor( textColor );
3252 if ( ! pHydrogen->isTimelineEnabled() ) {
3253 tempoMarkerColor.setAlpha( 45 );
3255 painter.setPen( tempoMarkerColor );
3258 font.setBold(
true );
3260 painter.setFont( font );
3261 painter.drawText( rect, Qt::AlignLeft | Qt::AlignVCenter,
3262 pTempoMarker->getPrettyString( 2 ) );
3265 font.setBold(
false );
3267 painter.setFont( font );
3274 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
3281 float fTick =
static_cast<float>(
m_nColumn);
3283 if ( pPatternGroupVector->size() >
m_nColumn &&
3284 pPatternGroupVector->at(
m_nColumn )->size() > 0 ) {
3285 int nLength = pPatternGroupVector->at(
m_nColumn )->longest_pattern_length();
3297 else if ( fTick < 0 ) {
3313 update( updateRect );
3314 if ( fDiff > 1.0 || fDiff < -1.0 ) {
3317 updateRect.translate( -fDiff, 0 );
3318 update( updateRect );
3322 if ( pSongEditorPanel !=
nullptr ) {
3324 pSongEditorPanel->getPlaybackTrackWaveDisplay()->updatePosition( fTick );
3325 pSongEditorPanel->getAutomationPathView()->updatePosition( fTick );
3334 return static_cast<int>(
3340 fTick *
static_cast<float>(nGridWidth) -
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
int operator<(QPoint a, QPoint b)
static const uint SONG_EDITOR_MAX_GRID_WIDTH
static const uint SONG_EDITOR_MIN_GRID_WIDTH
Custom file dialog checking whether the user has write access to the selected folder before allowing ...
const PatternList * getNextPatterns() const
@ Playing
Transport is rolling.
void unlock()
Mutex unlocking of the AudioEngine.
void lock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
const PatternList * getPlayingPatterns() const
const std::shared_ptr< TransportPosition > getTransportPosition() const
bool locateToColumn(int nPatternGroup)
Relocates transport to the beginning of a particular column/Pattern group.
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
static QString savePatternNew(const QString &fileName, Pattern *pattern, std::shared_ptr< Song > song, const QString &drumkitName)
save the given pattern to <user_data_path>/pattern/<drumkitName>/<fileName>.h2pattern will NOT overwr...
static QString savePatternOver(const QString &fileName, Pattern *pattern, std::shared_ptr< Song > song, const QString &drumkitName)
save the given pattern to <user_data_path>/pattern/<drumkitName>/<fileName>.h2pattern will overwrite ...
static QString savePatternTmp(const QString &fileName, Pattern *pattern, std::shared_ptr< Song > song, const QString &drumkitName)
save the given pattern under <Tmp_directory> with a unique filename built from <fileName> will overwr...
static const QString patterns_filter_name
static bool dir_readable(const QString &path, bool silent=false)
returns true if the given path is a readable regular directory
static QString tmp_file_path(const QString &base)
touch a temporary file under tmp_dir() and return it's path.
static QString patterns_dir()
returns user patterns path
bool isTimelineEnabled() const
Convenience function checking whether using the Timeline tempo is set in the Preferences,...
bool getIsExportSessionActive() const
std::shared_ptr< Song > getSong() const
Get the current song.
void toggleNextPattern(int nPatternNumber)
Wrapper around AudioEngine::toggleNextPattern().
Song::ActionMode getActionMode() const
Song::Mode getMode() const
int getSelectedPatternNumber() const
void updateVirtualPatterns()
Processes the patterns added to any virtual ones in the #PatternList of the current Song and ensure b...
std::shared_ptr< Timeline > getTimeline() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Song::PatternMode getPatternMode() const
void updateSelectedPattern(bool bNeedsLock=true)
Updates the selected pattern to the one recorded note will be inserted to.
AudioEngine * getAudioEngine() const
void setIsModified(bool bIsModified)
Wrapper around Song::setIsModified() that checks whether a song is set.
bool isPatternEditorLocked() const
Convenience function checking whether using the Pattern Editor is locked in the song settings and the...
void setSelectedPatternNumber(int nPat, bool bNeedsLock=true, bool bForce=false)
Sets m_nSelectedPatternNumber.
QString getLastLoadedDrumkitName() const
CoreActionController * getCoreActionController() const
PatternList is a collection of patterns.
bool check_name(QString patternName, Pattern *ignore=NULL)
check if a pattern with name patternName already exists in this list
void add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
int index(const Pattern *pattern) const
get the index of the pattern within the patterns
Pattern * replace(int idx, Pattern *pattern)
replace the pattern at a given index with a new one
QString find_unused_pattern_name(QString sourceName, Pattern *ignore=NULL)
find unused patternName
Pattern * del(int idx)
remove the pattern at a given index, does not delete it
int longest_pattern_length(bool bIncludeVirtuals=true) const
Get the length of the longest pattern in the list.
void clear()
empty the pattern list
int size() const
returns the numbers of patterns
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
const QString & get_info() const
get the category of the pattern
void set_name(const QString &name)
get the name of the pattern
const QString & get_name() const
set the category of the pattern
const virtual_patterns_t * get_flattened_virtual_patterns() const
const QString & get_category() const
set the length of the pattern
void set_info(const QString &info)
get the info of the pattern
int get_length() const
set the denominator of the pattern
void set_category(const QString &category)
set the info of the pattern
static Pattern * load_file(const QString &pattern_path, std::shared_ptr< InstrumentList > instruments)
load a pattern from a file
Manager for User Preferences File (singleton)
unsigned getSongEditorGridHeight()
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
const QString & getApplicationFontFamily() const
const std::shared_ptr< ColorTheme > getColorTheme() const
QString getLastOpenPatternDirectory() const
void setPunchInPos(unsigned pos)
int getMaxPatternColors() const
void setLastOpenPatternDirectory(QString sPath)
FontTheme::FontSize getFontSize() const
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ GeneralTab
Any option in the General tab appeared.
@ AppearanceTab
Any option in the Appearance tab excluding colors, font size, or font family.
@ Font
Either the font size or font family have changed.
@ Colors
At least one of the colors has changed.
void setPunchOutPos(unsigned pos)
unsigned getSongEditorGridWidth()
ActionMode
Defines the type of user interaction experienced in the SongEditor.
@ selectMode
Holding a pressed left mouse key will draw a rectangle to select a group of Notes.
@ drawMode
Holding a pressed left mouse key will draw/delete patterns in all grid cells encountered.
XMLDoc is a subclass of QDomDocument with read and write methods.
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
XMLNode is a subclass of QDomNode with read and write values methods.
int read_int(const QString &node, int default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads an integer stored into a child node
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
void write_int(const QString &node, const int value)
write an integer into a child node
void setHideKeyboardCursor(bool bHidden)
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
void preferencesChanged(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
SongEditorPanel * getSongEditorPanel()
void action_file_export_pattern_as(int nPatternRow=-1)
Pattern Properties Dialog.
void paintSelection(QPainter *painter)
Paint selection-related elements (ie lasso)
bool isLasso() const
Is there an ongoing lasso gesture?
void addToSelection(Elem e)
void mouseReleaseEvent(QMouseEvent *ev)
void clearSelection(bool bCheck=true)
bool keyPressEvent(QKeyEvent *ev)
Key press event filter.
bool isMouseGesture() const
Is there a mouse gesture in progress?
void updateKeyboardCursorPosition(QRect cursor)
Update the keyboard cursor.
bool isSelected(Elem e) const
Is an element in the set of currently selected elements?
void mousePressEvent(QMouseEvent *ev)
bool isMoving() const
Is there an ongoing (and incomplete) selection movement gesture?
QPoint movingOffset() const
During a selection "move" gesture, return the current movement position relative to the start positio...
void mouseMoveEvent(QMouseEvent *ev)
static constexpr int nPlayheadHeight
static void drawPlayhead(QPainter *p, int x, int y, bool bHovered=false)
static constexpr int nPlayheadWidth
static void drawListBackground(QPainter *p, QRect rect, QColor background, bool bHovered)
Draws the background of a row in both the pattern list of the SongEditor and the instrument list in t...
static void drawStackedIndicator(QPainter *p, int x, int y, Skin::Stacked stacked)
static void setPlayheadPen(QPainter *p, bool bHovered=false)
static QString getImagePath()
static int getPlayheadShaftOffset()
void updateAll()
Update and redraw all...
SongEditorPositionRuler * getSongEditorPositionRuler() const
void highlightPatternEditorLocked(bool bUseRedBackground)
Turns the background color of m_pPatternEditorLockedBtn red to signal the user her last action was no...
SongEditor * getSongEditor() const
SongEditorPatternList * getSongEditorPatternList() const
void updatePlaybackTrackIfNecessary()
virtual void timelineUpdateEvent(int nValue) override
void setRowSelection(RowSelection rowSelection)
QPixmap m_playingPattern_empty_Pixmap
virtual void mousePressEvent(QMouseEvent *ev) override
Single click, select the next pattern.
QPixmap m_playingPattern_on_Pixmap
QPixmap m_playingPattern_off_Pixmap
void patternPopup_properties()
virtual void mouseDoubleClickEvent(QMouseEvent *ev) override
virtual void stackedModeActivationEvent(int nValue) override
virtual void leaveEvent(QEvent *ev)
void fillRangeWithPattern(FillRange *r, int nPattern)
H2Core::Pattern * m_pPatternBeingEdited
virtual void mouseMoveEvent(QMouseEvent *event) override
void movePatternLine(int, int)
virtual void patternModifiedEvent() override
void onPreferencesChanged(H2Core::Preferences::Changes changes)
int m_nRowHovered
Specifies the row the mouse cursor is currently hovered over.
void patternPopup_export()
void acceptPatternPropertiesDialogSettings(QString newPatternName, QString newPatternInfo, QString newPatternCategory, int patternNr)
virtual void songModeActivationEvent() override
void togglePattern(int)
Start/stop playing a pattern in "pattern mode".
H2Core::AudioEngine * m_pAudioEngine
DragScroller * m_pDragScroller
virtual void dragEnterEvent(QDragEnterEvent *event) override
drag & drop
static const uint m_nInitialHeight
void patternPopup_duplicate()
void patternPopup_delete()
void inlineEditPatternName(int row)
void patternPopup_virtualPattern()
virtual void playingPatternsChangedEvent() override
QTimer * m_pHighlightLockedTimer
SongEditorPatternList(QWidget *parent)
QPixmap * m_pBackgroundPixmap
virtual void relocationEvent() override
QPoint __drag_start_position
virtual void paintEvent(QPaintEvent *ev) override
void inlineEditingEntered()
virtual void dropEvent(QDropEvent *event) override
virtual void selectedPatternChangedEvent() override
void invalidateBackground()
void revertPatternPropertiesDialogSettings(QString oldPatternName, QString oldPatternInfo, QString oldPatternCategory, int patternNr)
virtual void patternEditorLockedEvent() override
virtual void nextPatternsChangedEvent() override
H2Core::Hydrogen * m_pHydrogen
void inlineEditingFinished()
bool m_bBackgroundInvalid
virtual void timelineUpdateEvent(int nValue) override
virtual void mouseMoveEvent(QMouseEvent *ev) override
float m_fTick
Cached position of the playhead.
static constexpr uint m_nHeight
virtual void timelineActivationEvent() override
virtual void mousePressEvent(QMouseEvent *ev) override
HoveredRow
Indicated the part of the widget the cursor is hovering over.
@ TempoMarker
Upper half until the lower end of the tempo marker text.
@ None
Cursor is not hovering the widget.
@ Tag
Still part of the upper half, but only the last m_nTagHeight pixels.
int columnToX(int nColumn) const
Calculates the position in pixel required to the painter for a particular nColumn of the grid.
int m_nColumn
Cached and coarsed-grained position of the playhead.
void showTagWidget(int nColumn)
void drawTempoMarker(std::shared_ptr< const H2Core::Timeline::TempoMarker > pTempoMarker, bool bEmphasize, QPainter &painter)
static int tickToColumn(float fTick, uint nGridWidth)
void showBpmWidget(int nColumn)
virtual void patternModifiedEvent() override
void onPreferencesChanged(H2Core::Preferences::Changes changes)
virtual void songModeActivationEvent() override
H2Core::AudioEngine * m_pAudioEngine
virtual void leaveEvent(QEvent *ev) override
void showToolTip(const QPoint &pos, const QPoint &globalPos)
virtual void tempoChangedEvent(int) override
virtual void mouseReleaseEvent(QMouseEvent *ev) override
virtual bool event(QEvent *ev) override
void setGridWidth(uint width)
int xToColumn(int nX) const
SongEditorPositionRuler(QWidget *parent)
virtual void songSizeChangedEvent() override
virtual void playingPatternsChangedEvent() override
virtual void updateSongEvent(int) override
QPixmap * m_pBackgroundPixmap
virtual void relocationEvent() override
int m_nActiveColumns
Area covering the length of the song columns.
int m_nActiveBpmWidgetColumn
virtual void paintEvent(QPaintEvent *ev) override
void invalidateBackground()
QRect calcTempoMarkerRect(std::shared_ptr< const H2Core::Timeline::TempoMarker > pTempoMarker, bool bEmphasize=false) const
virtual void jackTimebaseStateChangedEvent() override
~SongEditorPositionRuler()
H2Core::Hydrogen * m_pHydrogen
bool m_bBackgroundInvalid
virtual void mouseDragUpdateEvent(QMouseEvent *ev) override
virtual void mouseMoveEvent(QMouseEvent *ev) override
float m_fTick
Cached position of the playhead.
virtual void keyReleaseEvent(QKeyEvent *ev) override
virtual void mousePressEvent(QMouseEvent *ev) override
virtual void updateModifiers(QInputEvent *ev)
virtual void selectionMoveEndEvent(QInputEvent *ev) override
virtual QRect getKeyboardCursorRect() override
Calculate screen space occupied by keyboard cursor.
void drawPattern(int pos, int number, bool invertColour, double width)
QPoint columnRowToXy(QPoint p)
void modifyPatternCellsAction(std::vector< QPoint > &addCells, std::vector< QPoint > &deleteCells, std::vector< QPoint > &selectCells)
Modify many pattern cells at once, for use in a single efficient undo/redo action.
QPoint m_currentMousePosition
void updatePosition(float fTick)
QPixmap * m_pSequencePixmap
void setPatternActive(int nColumn, int nRow, bool bActivate)
SongEditorPanel * m_pSongEditorPanel
bool m_bSequenceChanged
Pattern sequence or selection has changed, so must be redrawn.
QPoint xyToColumnRow(QPoint p)
virtual void patternModifiedEvent() override
void onPreferencesChanged(H2Core::Preferences::Changes changes)
static constexpr int nMargin
virtual std::vector< SelectionIndex > elementsIntersecting(QRect r) override
Find list of elements which intersect a rectangular area.
virtual void updateWidget() override
Selection or selection-related visual elements have changed, widget needs to be updated.
virtual void focusInEvent(QFocusEvent *ev) override
H2Core::AudioEngine * m_pAudioEngine
bool m_bDrawingActiveCell
In "draw" mode, whether we're activating pattern cells ("drawing") or deactivating ("erasing") is set...
virtual void leaveEvent(QEvent *ev) override
virtual void keyPressEvent(QKeyEvent *ev) override
virtual void mouseReleaseEvent(QMouseEvent *ev) override
QScrollArea * m_pScrollView
void setGridWidth(uint width)
virtual void mouseDragStartEvent(QMouseEvent *ev) override
void copy()
Copy a selection of cells to an XML representation in the clipboard.
virtual void focusOutEvent(QFocusEvent *ev) override
Selection< QPoint > m_selection
SongEditor(QWidget *parent, QScrollArea *pScrollView, SongEditorPanel *pSongEditorPanel)
int yScrollTarget(QScrollArea *pScrollArea, int *pnPatternInView)
Calculate a target Y scroll value for tracking a playing song.
QPixmap * m_pBackgroundPixmap
QPoint m_previousMousePosition
Mouse position during selection gestures (used to detect crossing cell boundaries)
std::map< QPoint, GridCell > m_gridCells
virtual void mouseDragEndEvent(QMouseEvent *ev) override
virtual void relocationEvent() override
void clearThePatternSequenceVector(QString filename)
virtual void mouseClickEvent(QMouseEvent *ev) override
virtual void paintEvent(QPaintEvent *ev) override
void togglePatternActive(int nColumn, int nRow)
void drawFocus(QPainter &painter)
void invalidateBackground()
virtual void patternEditorLockedEvent() override
QPoint m_previousGridOffset
virtual void enterEvent(QEvent *ev) override
H2Core::Hydrogen * m_pHydrogen
QPoint movingGridOffset() const
Quantise the selection move offset to the sequence grid.
bool m_bBackgroundInvalid
void updateEditorandSetTrue()
#define MAX_NOTES
Maximum number of notes.
@ EVENT_PATTERN_MODIFIED
A pattern was added, deleted, or modified.