96 connect(
m_pScrollView->verticalScrollBar(), SIGNAL( valueChanged(
int ) ),
this, SLOT(
scrolled(
int ) ) );
97 connect(
m_pScrollView->horizontalScrollBar(), SIGNAL( valueChanged(
int ) ),
this, SLOT(
scrolled(
int ) ) );
99 setAttribute(Qt::WA_OpaquePaintEvent);
100 setFocusPolicy (Qt::StrongFocus);
162 const int nScroll = pScrollArea->verticalScrollBar()->value();
163 int nHeight = pScrollArea->height();
168 if ( pPlayingPatterns->size() == 0 ) {
178 std::vector<Pattern *> currentPatterns;
179 for (
int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
180 currentPatterns.push_back( pPlayingPatterns->get( ii ) );
184 std::vector<int> playingRows;
185 for (
Pattern *pPattern : currentPatterns ) {
186 playingRows.push_back( pSongPatterns->
index( pPattern ) );
191 if ( playingRows.size() == 0 ) {
196 for (
int r : playingRows ) {
200 if ( pnPatternInView ) {
201 *pnPatternInView = r;
219 std::sort( playingRows.begin(), playingRows.end() );
222 int nAboveMax = 0, nAbovePattern = -1, nAboveClosestPattern = -1,
223 nBelowMax = 0, nBelowPattern = -1, nBelowClosestPattern = -1;
225 for (
int nBottomIdx = 0; nBottomIdx < playingRows.size(); nBottomIdx++) {
232 if ( nTop < nBottom - nHeight ) {
234 assert( nTopIdx <= nBottomIdx && nTopIdx < playingRows.size() );
239 int nPatternsInViewport = nBottomIdx - nTopIdx +1;
240 if ( nBottom < nScroll ) {
243 if ( nPatternsInViewport >= nAboveMax ) {
244 nAboveMax = nPatternsInViewport;
247 nAbovePattern = playingRows[ nTopIdx ];
248 nAboveClosestPattern = playingRows[ nBottomIdx ];
253 if ( nPatternsInViewport > nBelowMax ) {
254 nBelowMax = nPatternsInViewport;
257 nBelowPattern = playingRows[ nBottomIdx ];
258 nBelowClosestPattern = playingRows[ nTopIdx ];
266 enum { Up, Down } direction = Down;
267 if ( nAboveMax != 0) {
268 if ( nAboveMax > nBelowMax ) {
271 }
else if ( nBelowMax > nAboveMax ) {
276 assert( nAboveY <= nScroll && nScroll <= nBelowY );
277 if ( nScroll - nAboveY < nBelowY - nScroll ) {
284 assert( nBelowMax != 0 );
289 if ( direction == Up ) {
290 if ( pnPatternInView ) {
291 *pnPatternInView = nAboveClosestPattern;
295 if ( pnPatternInView ) {
296 *pnPatternInView = nBelowClosestPattern;
340 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
341 bool bPatternIsActive = pSong->isPatternActive( nColumn, nRow );
343 if ( bPatternIsActive && ! bActivate || ! bPatternIsActive && bActivate ) {
351 std::vector<PatternList*>* pColumns =
m_pHydrogen->getSong()->getPatternGroupVector();
353 for (
int nRow = 0; nRow < pPatternList->
size(); nRow++ ) {
355 for (
int nCol = 0; nCol < pColumns->size(); nCol++ ) {
357 for ( uint i = 0; i < pColumn->
size(); i++) {
358 if ( pColumn->
get(i) == pPattern ) {
359 m_selection.addToSelection( QPoint( nCol, nRow ) );
377 std::vector< QPoint > addCells, deleteCells, mergeCells;
379 deleteCells.push_back( cell );
382 tr(
"Delete selected cells" ) ) );
401 bool bWrotePattern =
false;
407 if ( bWrotePattern ) {
408 nMinX = std::min( nMinX, cell.x() );
409 nMinY = std::min( nMinY, cell.y() );
413 bWrotePattern =
true;
416 if ( !bWrotePattern) {
420 positionNode.
write_int(
"column", nMinX );
423 QApplication::clipboard()->setText( doc.toString() );
431 int nDeltaColumn = 0, nDeltaRow = 0;
433 int nPatterns = pSong->getPatternList()->size();
436 if ( ! doc.setContent( QApplication::clipboard()->text() ) ) {
444 XMLNode selection = doc.firstChildElement(
"patternSelection" );
445 if ( ! selection.isNull() ) {
447 std::vector< QPoint > addCells, deleteCells, mergeCells;
449 XMLNode cellList = selection.firstChildElement(
"cellList" );
450 if ( cellList.isNull() ) {
454 XMLNode positionNode = selection.firstChildElement(
"sourcePosition" );
459 if ( !positionNode.isNull() ) {
465 if ( cellList.hasChildNodes() ) {
466 for (
XMLNode cellNode = cellList.firstChildElement(
"cell" );
468 cellNode = cellNode.nextSiblingElement() ) {
470 int nRow = cellNode.read_int(
"y",
m_nCursorRow ) + nDeltaRow;
471 if ( nCol >= 0 && nRow >= 0 && nRow < nPatterns ) {
473 QPoint p = QPoint( nCol, nRow );
476 addCells.push_back( p );
479 mergeCells.push_back( p );
485 tr(
"Paste cells" ) ) );
499 auto pSong = pHydrogen->getSong();
500 const int nBlockSize = 5, nWordSize = 5;
502 bool bIsSelectionKey =
false;
503 bool bUnhideCursor =
true;
505 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
513 PatternList *pPatternList = pSong->getPatternList();
515 bool bSelectionKey =
false;
521 if ( bIsSelectionKey ) {
523 }
else if ( ev->key() == Qt::Key_Delete ) {
532 }
else if ( ev->matches( QKeySequence::MoveToNextChar ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextChar ) ) ) {
538 }
else if ( ev->matches( QKeySequence::MoveToNextWord ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextWord ) ) ) {
542 }
else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectEndOfLine ) ) ) {
546 }
else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousChar ) ) ) {
552 }
else if ( ev->matches( QKeySequence::MoveToPreviousWord ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousWord ) ) ) {
556 }
else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectStartOfLine ) ) ) {
560 }
else if ( ev->matches( QKeySequence::MoveToNextLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextLine ) ) ) {
565 }
else if ( ev->matches( QKeySequence::MoveToEndOfBlock ) || ( bSelectionKey = ev->matches( QKeySequence::SelectEndOfBlock ) ) ) {
568 }
else if ( ev->matches( QKeySequence::MoveToNextPage ) || ( bSelectionKey = ev->matches( QKeySequence::SelectNextPage ) ) ) {
570 QWidget *pParent =
dynamic_cast< QWidget *
>( parent() );
578 }
else if ( ev->matches( QKeySequence::MoveToEndOfDocument ) || ( bSelectionKey = ev->matches( QKeySequence::SelectEndOfDocument ) ) ) {
581 }
else if ( ev->matches( QKeySequence::MoveToPreviousLine ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousLine ) ) ) {
586 }
else if ( ev->matches( QKeySequence::MoveToStartOfBlock ) || ( bSelectionKey = ev->matches( QKeySequence::SelectStartOfBlock ) ) ) {
590 }
else if ( ev->matches( QKeySequence::MoveToPreviousPage ) || ( bSelectionKey = ev->matches( QKeySequence::SelectPreviousPage ) ) ) {
591 QWidget *pParent =
dynamic_cast< QWidget *
>( parent() );
599 }
else if ( ev->matches( QKeySequence::MoveToStartOfDocument ) || ( bSelectionKey = ev->matches( QKeySequence::SelectStartOfDocument ) ) ) {
602 }
else if ( ev->matches( QKeySequence::SelectAll ) ) {
604 bSelectionKey =
true;
605 bUnhideCursor =
false;
610 }
else if ( ev->matches( QKeySequence::Deselect ) ) {
612 bSelectionKey =
true;
613 bUnhideCursor =
false;
619 }
else if ( ev->matches( QKeySequence::Copy ) ) {
620 bUnhideCursor =
false;
623 }
else if ( ev->matches( QKeySequence::Paste ) ) {
624 bUnhideCursor =
false;
626 }
else if ( ev->matches( QKeySequence::Cut ) ) {
627 bUnhideCursor =
false;
630 }
else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
639 pHydrogenApp->setHideKeyboardCursor(
true );
641 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
642 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
643 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
648 if ( bUnhideCursor ) {
649 pHydrogenApp->setHideKeyboardCursor(
false );
652 if ( bSelectionKey ) {
667 m_pScrollView->ensureVisible( cursorCentre.x(), cursorCentre.y() );
670 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
671 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
672 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
685 if ( ev->reason() == Qt::TabFocusReason || ev->reason() == Qt::BacktabFocusReason ) {
722 int nAx = a.x(), nBx = b.x();
726 int nAy = a.y(), nBy = b.y();
745 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
747 pHydrogenApp->setHideKeyboardCursor(
true );
751 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
752 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
753 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
758 if ( ev->button() == Qt::LeftButton ) {
765 }
else if ( ev->button() == Qt::RightButton ) {
771 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
772 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
773 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
781 if ( ev->modifiers() == Qt::ControlModifier ) {
787 if ( QKeyEvent *pEv =
dynamic_cast<QKeyEvent*
>( ev ) ) {
790 if ( pEv->key() == Qt::Key_Control ) {
800 setCursor( QCursor( Qt::DragCopyCursor ) );
801 }
else if ( !
m_bCopyNotMove && cursor().shape() != Qt::DragMoveCursor ) {
802 setCursor( QCursor( Qt::DragMoveCursor ) );
816 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
834 if (
m_nCursorRow >= pSong->getPatternList()->size() ) {
844 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
845 pHydrogenApp->getSongEditorPanel()->getSongEditorPatternList()->update();
846 pHydrogenApp->getSongEditorPanel()->getSongEditorPositionRuler()->update();
868 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
869 PatternList *pPatternList = pSong->getPatternList();
870 int nMaxPattern = pPatternList->
size();
874 if ( offset == QPoint( 0, 0 ) ) {
877 std::vector< QPoint > addCells, deleteCells, mergeCells;
884 deleteCells.push_back( cell );
886 QPoint newCell = cell + offset;
888 if ( newCell.x() >= 0 && newCell.y() >= 0 && newCell.y() < nMaxPattern ) {
890 addCells.push_back( newCell );
893 mergeCells.push_back( newCell );
900 ? tr(
"Copy selected cells" )
901 : tr(
"Move selected cells" ) ) ) );
911 if ( ev->button() == Qt::LeftButton ) {
923 }
else if ( ev->button() == Qt::RightButton ) {
941 for ( QPoint cell : deleteCells ) {
946 for ( QPoint cell : addCells ) {
951 for ( QPoint cell : selectCells ) {
971 if ( bCellBoundaryCrossed ) {
990 QRect updateRect( nX + nOffset -2, 0, 4, height() );
991 update( updateRect );
992 if ( fDiff > 1.0 || fDiff < -1.0 ) {
995 updateRect.translate( -fDiff, 0 );
996 update( updateRect );
1036 QPainter painter(
this);
1040 QColor patternColor( 0, 0, 0 );
1047 .marginsRemoved( QMargins( 2, 4, 1 , 3 ) );
1048 painter.fillRect( r, patternColor );
1056 painter.drawLine( nX + nOffset, 0, nX + nOffset, height() );
1065 QPen p( pPref->getColorTheme()->m_cursorColor );
1067 painter.setPen( p );
1068 painter.setRenderHint( QPainter::Antialiasing );
1089 if ( ! hasFocus() ) {
1090 color.setAlpha( 125 );
1093 int nStartX =
m_pScrollView->horizontalScrollBar()->value();
1094 int nEndX = std::min( nStartX +
m_pScrollView->viewport()->size().width(), width() );
1101 painter.setPen( pen );
1102 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nEndX, nStartY ) );
1103 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nStartX, nEndY ) );
1104 painter.drawLine( QPoint( nEndX, nStartY ), QPoint( nEndX, nEndY ) );
1105 painter.drawLine( QPoint( nEndX, nEndY ), QPoint( nStartX, nEndY ) );
1113#ifdef H2CORE_HAVE_QT6
1133 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
1135 uint nPatterns = pSong->getPatternList()->size();
1136 int nSelectedPatternNumber =
m_pHydrogen->getSelectedPatternNumber();
1137 int nMaxPatternSequence = pPref->getMaxBars();
1139 static int nOldHeight = -1;
1142 if (nOldHeight != nNewHeight) {
1145 WARNINGLOG( QString(
"nNewHeight [%1] below minimum one [%2]" )
1157 this->resize( QSize( width(), nNewHeight ) );
1165 for (
int ii = 0; ii < nPatterns + 1; ii++) {
1166 if ( ( ii % 2 ) == 0 &&
1167 ii != nSelectedPatternNumber ) {
1173 if ( ii == nSelectedPatternNumber ) {
1175 pPref->getColorTheme()->m_songEditor_selectedRowColor );
1178 pPref->getColorTheme()->m_songEditor_alternateRowColor );
1182 p.setPen( QPen( pPref->getColorTheme()->m_songEditor_lineColor, 1,
1186 for (
float ii = 0; ii <= nMaxPatternSequence + 1; ii++) {
1192 for (uint i = 0; i < nPatterns; i++) {
1195 p.drawLine( 0, y, (nMaxPatternSequence *
m_nGridWidth), y );
1212 PatternList *pPatternList = pSong->getPatternList();
1213 std::vector< PatternList* > *pColumns = pSong->getPatternGroupVector();
1215 for (
int nColumn = 0; nColumn < pColumns->size(); nColumn++ ) {
1219 for ( uint nPat = 0; nPat < pColumn->
size(); nPat++ ) {
1220 Pattern *pPattern = (*pColumn)[ nPat ];
1221 int y = pPatternList->
index( pPattern );
1230 pVCell->
m_fWidth = (float) pVPattern->get_length() / nMaxLength;
1241 if ( rawOffset.y() < 0 ) {
1244 if ( rawOffset.x() < 0 ) {
1247 int x_off = (rawOffset.x() + x_bias) / (int)
m_nGridWidth;
1249 return QPoint( x_off, y_off );
1265 if ( !
m_selection.isSelected( QPoint( it.first.x(), it.first.y() ) ) ) {
1267 it.second.m_bDrawnVirtual, it.second.m_fWidth );
1274 if (
m_selection.isSelected( QPoint( it.first.x(), it.first.y() ) ) ) {
1276 it.second.m_bDrawnVirtual, it.second.m_fWidth );
1291 PatternList *pPatternList = pSong->getPatternList();
1293 QColor patternColor;
1303 int nSteps = pPatternList->
size();
1310 int nHue = ( (nNumber % nSteps) * (300 / nSteps) + 213) % 300;
1311 patternColor.setHsv( nHue , 156 , 249);
1313 int nIndex = nNumber % pPref->getVisiblePatternColors();
1317 patternColor = pPref->getPatternColors()[ nIndex ].toHsv();
1320 if (
true == bInvertColour ) {
1321 patternColor = patternColor.darker(200);
1324 bool bIsSelected =
m_selection.isSelected( QPoint( nPos, nNumber ) );
1326 if ( bIsSelected ) {
1327 patternColor = patternColor.darker( 130 );
1330 patternColor.setAlpha( 230 );
1342 borderColor = pPref->getColorTheme()->m_selectionHighlightColor;
1344 borderColor = pPref->getColorTheme()->m_selectionInactiveColor;
1347 borderColor = QColor( 0, 0, 0 );
1349 p.setPen( borderColor );
1355 std::vector<SelectionIndex> elems;
1359 if ( ! it.second.m_bDrawnVirtual ) {
1360 elems.push_back( it.first );
1378 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1381 pSong->writeTempPatternList( filename );
1383 std::vector<PatternList*> *pPatternGroupsVect = pSong->getPatternGroupVector();
1384 for (uint i = 0; i < pPatternGroupsVect->size(); i++) {
1385 PatternList *pPatternList = (*pPatternGroupsVect)[i];
1386 pPatternList->
clear();
1387 delete pPatternList;
1389 pPatternGroupsVect->clear();
1438 setAttribute(Qt::WA_OpaquePaintEvent);
1440 setAcceptDrops(
true);
1441 setMouseTracking(
true );
1445 m_pLineEdit =
new QLineEdit(
"Inline Pattern Name",
this );
1479 QScrollArea *pScrollArea =
dynamic_cast< QScrollArea *
>( parentWidget()->parentWidget() );
1480 assert( pScrollArea );
1488 qreal pixelRatio = devicePixelRatio();
1490 height() * pixelRatio );
1560 if ( pSong ==
nullptr ) {
1564 auto pPatternList = pSong->getPatternList();
1566 if ( nRow < 0 || nRow >= (
int)pPatternList->size() ) {
1567 ERRORLOG( QString(
"Row [%1] out of bound" ).arg( nRow ) );
1571 if ( ( ev->button() == Qt::MiddleButton ||
1572 ( ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::RightButton ) ||
1573 ( ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton ) ||
1574 pEv->position().x() < 15 ) &&
1592 if (ev->button() == Qt::RightButton) {
1598 ERRORLOG(
"A dialog is still opened. It needs to be closed first." );
1634 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
1635 PatternList *pPatternList = pSong->getPatternList();
1637 if ( row >= (
int)pPatternList->
size() ) {
1652 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
1653 PatternList *pPatternList = pSong->getPatternList();
1685 auto pSongEditor = pHydrogenApp->getSongEditorPanel()->getSongEditor();
1687 QPainter painter(
this);
1688 qreal pixelRatio = devicePixelRatio();
1696 pixelRatio * ev->rect().x(),
1697 pixelRatio * ev->rect().y(),
1698 pixelRatio * ev->rect().width(),
1699 pixelRatio * ev->rect().height()
1705 if ( ( ! pHydrogenApp->hideKeyboardCursor() &&
1706 pSongEditor->hasFocus() ) ||
1708 QColor colorHighlight = pPref->getColorTheme()->m_highlightColor;
1714 pen.setColor( pPref->getColorTheme()->m_highlightColor);
1717 pen.setColor( pPref->getColorTheme()->m_cursorColor );
1721 painter.setRenderHint( QPainter::Antialiasing );
1723 painter.setPen( pen );
1724 painter.drawRoundedRect( QRect( 1, nStartY + 1,
m_nWidth - 2,
1759 QFont boldTextFont( pPref->getLevel2FontFamily(),
getPointSize( pPref->getFontSize() ) );
1760 boldTextFont.setBold(
true );
1768 const std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
1769 if ( pSong ==
nullptr ) {
1774 const auto pPatternList = pSong->getPatternList();
1775 int nPatterns = pPatternList->size();
1776 int nSelectedPattern =
m_pHydrogen->getSelectedPatternNumber();
1783 if (newHeight == 0) {
1787 qreal pixelRatio = devicePixelRatio();
1790 this->resize(
m_nWidth, newHeight );
1793 QColor backgroundColor = pPref->getColorTheme()->m_songEditor_backgroundColor.darker( 120 );
1794 QColor backgroundColorSelected = pPref->getColorTheme()->m_songEditor_selectedRowColor.darker( 114 );
1795 QColor backgroundColorAlternate =
1796 pPref->getColorTheme()->m_songEditor_alternateRowColor.darker( 132 );
1797 QColor backgroundColorVirtual =
1798 pPref->getColorTheme()->m_songEditor_virtualRowColor;
1805 p.fillRect( QRect( 0, 0, width(), 1 ), pPref->getColorTheme()->m_windowColor );
1807 p.setFont( boldTextFont );
1808 for (
int ii = 0; ii < nPatterns; ii++ ) {
1811 if ( ii == nSelectedPattern ) {
1813 backgroundColorSelected,
false );
1815 const auto pPattern = pPatternList->get( ii );
1816 if ( pPattern !=
nullptr && pPattern->isVirtual() ) {
1818 backgroundColorVirtual,
1821 else if ( ( ii % 2 ) == 0 ) {
1828 backgroundColorAlternate,
1834 std::unique_ptr<PatternDisplayInfo[]> PatternArray{
new PatternDisplayInfo[nPatterns]};
1840 for (
int i = 0; i < nPatterns; i++ ) {
1842 if ( pPattern ==
nullptr ) {
1846 if ( pPlayingPatterns->index( pPattern ) != -1 ) {
1847 PatternArray[i].bActive =
true;
1849 PatternArray[i].bActive =
false;
1852 if (
m_pAudioEngine->getNextPatterns()->index( pPattern ) != -1 ) {
1853 PatternArray[i].bNext =
true;
1855 PatternArray[i].bNext =
false;
1858 PatternArray[i].sPatternName = pPattern->
get_name();
1863 for (
int i = 0; i < nPatterns; i++ ) {
1864 if ( i == nSelectedPattern ) {
1865 p.setPen( pPref->getColorTheme()->m_songEditor_selectedRowTextColor );
1868 p.setPen( pPref->getColorTheme()->m_songEditor_textColor );
1874 Qt::AlignVCenter, PatternArray[i].sPatternName);
1877 if ( PatternArray[i].bNext && PatternArray[i].bActive) {
1880 else if ( PatternArray[i].bNext ) {
1883 else if (PatternArray[i].bActive) {
1904 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
1905 if ( pSong ==
nullptr ) {
1909 PatternList *pPatternList = pSong->getPatternList();
1910 if ( pPatternList ==
nullptr ) {
1918 QListWidget* pPatternListWidget = pDialog->patternList;
1919 pPatternListWidget->setSortingEnabled(1);
1923 std::map<QString, Pattern*> patternNameMap;
1925 for (
const auto& pPattern : *pPatternList ) {
1926 QString sPatternName = pPattern->get_name();
1928 if ( sPatternName == pPatternClicked->get_name() ) {
1933 patternNameMap[ sPatternName ] = pPattern;
1935 QListWidgetItem* pNewItem =
1936 new QListWidgetItem( sPatternName, pPatternListWidget);
1937 pPatternListWidget->insertItem( 0, pNewItem );
1939 if ( pPatternClicked->get_virtual_patterns()->find( pPattern ) !=
1940 pPatternClicked->get_virtual_patterns()->end() ) {
1942 pNewItem->setSelected(
true );
1946 if ( pDialog->exec() == QDialog::Accepted ) {
1947 pPatternClicked->virtual_patterns_clear();
1948 for (
int ii = 0; ii < pPatternListWidget->count(); ++ii ) {
1949 QListWidgetItem* pListItem = pPatternListWidget->item( ii );
1950 if ( pListItem !=
nullptr && pListItem->isSelected() ) {
1951 if ( patternNameMap.find( pListItem->text() ) !=
1952 patternNameMap.end() ) {
1953 pPatternClicked->virtual_patterns_add(
1954 patternNameMap[ pListItem->text() ] );
1957 ERRORLOG( QString(
"Selected pattern [%1] could not be retrieved" )
1958 .arg( pListItem->text() ) );
1978 auto pSong = pHydrogen->getSong();
1980 if ( pSong ==
nullptr ) {
1993 fd.setAcceptMode( QFileDialog::AcceptOpen );
1994 fd.setFileMode( QFileDialog::ExistingFile );
1996 fd.setDirectory( sPath );
1997 fd.setWindowTitle( QString( tr(
"Open Pattern to Replace " )
1998 .append( pPattern->
get_name() ) ) );
2000 if (fd.exec() != QDialog::Accepted) {
2004 QString patternPath = fd.selectedFiles().first();
2006 QString prevPatternPath =
2008 pHydrogen->getLastLoadedDrumkitName() );
2009 if ( prevPatternPath.isEmpty() ) {
2010 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save pattern to temporary directory.") );
2015 if ( !pSong->writeTempPatternList( sequencePath ) ) {
2016 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export sequence.") );
2045 auto pCommonStrings = pHydrogenApp->getCommonStrings();
2047 auto pSong = pHydrogen->getSong();
2048 auto pPattern = pSong->getPatternList()->get(
m_nRowClicked );
2051 pSong, pHydrogen->getLastLoadedDrumkitName() );
2052 if ( sPath.isEmpty() ) {
2053 if ( QMessageBox::information(
2055 tr(
"The pattern-file exists. \nOverwrite the existing pattern?"),
2056 QMessageBox::Ok | QMessageBox::Cancel,
2057 QMessageBox::Cancel ) != QMessageBox::Ok ) {
2062 pSong, pHydrogen->getLastLoadedDrumkitName() );
2065 if ( sPath.isEmpty() ) {
2066 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export pattern.") );
2071 pHydrogenApp->showStatusBarMessage( tr(
"Pattern saved." ) );
2073 pHydrogen->getSoundLibraryDatabase()->updatePatterns();
2093 auto pPattern = pHydrogen->getSong()->getPatternList()->get(
m_nRowClicked );
2108 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
2109 PatternList *patternList = pSong->getPatternList();
2111 pattern->
set_name( newPatternName );
2112 pattern->
set_info( newPatternInfo );
2124 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
2125 PatternList *patternList = pSong->getPatternList();
2127 pattern->
set_name( oldPatternName );
2141 auto pPattern = pSong->getPatternList()->get(
m_nRowClicked );
2143 QString patternPath =
2146 if ( patternPath.isEmpty() ) {
2147 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save pattern to temporary directory.") );
2152 if ( !pSong->writeTempPatternList( sequencePath ) ) {
2153 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not export sequence.") );
2172 PatternList *pPatternList = pSong->getPatternList();
2178 if ( dialog->exec() == QDialog::Accepted ) {
2182 if ( filePath.isEmpty() ) {
2183 QMessageBox::warning(
this,
"Hydrogen", tr(
"Could not save pattern to temporary directory.") );
2206 if ( dialog->exec() == QDialog::Accepted ) {
2223 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
2224 PatternList *pPatternList = pSong->getPatternList();
2226 std::vector<PatternList*> *pColumns = pSong->getPatternGroupVector();
2229 int nColumn, nColumnIndex;
2230 bool bHasPattern =
false;
2231 int fromVal = pRange->
fromVal - 1;
2232 int toVal = pRange->
toVal;
2235 int nDelta = toVal - pColumns->size() + 1;
2237 for (
int i = 0; i < nDelta; i++ ) {
2239 pColumns->push_back( pColumn );
2243 for ( nColumn = fromVal; nColumn < toVal; nColumn++ ) {
2246 pColumn = ( *pColumns )[ nColumn ];
2250 bHasPattern =
false;
2253 for ( nColumnIndex = 0; pColumn && nColumnIndex < (int)pColumn->
size(); nColumnIndex++) {
2255 if ( pColumn->
get( nColumnIndex ) == pPattern ) {
2261 if ( pRange->
bInsert && !bHasPattern ) {
2262 pColumn->
add( pPattern);
2264 else if ( !pRange->
bInsert && bHasPattern ) {
2265 pColumn->
del( pPattern);
2270 for (
int i = pColumns->size() - 1; i != 0 ; i-- ) {
2272 int nSize = pList->
size();
2274 pColumns->erase( pColumns->begin() + i );
2293 if ( event->mimeData()->hasFormat(
"text/plain") ) {
2294 event->acceptProposedAction();
2301 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
2303 QString sText =
event->mimeData()->text();
2304 const QMimeData* mimeData =
event->mimeData();
2306 auto pEv =
static_cast<DropEvent*
>( event );
2308 int nTargetPattern = 0;
2314 if( sText.startsWith(
"Songs:") || sText.startsWith(
"move instrument:") || sText.startsWith(
"importInstrument:")){
2315 event->acceptProposedAction();
2319 if ( sText.startsWith(
"move pattern:") ) {
2320 QStringList tokens = sText.split(
":" );
2323 int nSourcePattern = tokens[1].toInt(&bOK);
2328 if ( nSourcePattern == nTargetPattern ) {
2329 event->acceptProposedAction();
2336 event->acceptProposedAction();
2338 else if( sText.startsWith(
"file://") && mimeData->hasUrls() )
2341 PatternList *pPatternList = pSong->getPatternList();
2342 QList<QUrl> urlList = mimeData->urls();
2344 int successfullyAddedPattern = 0;
2346 for (
int i = 0; i < urlList.size(); i++)
2348 QString patternFilePath = urlList.at(i).toLocalFile();
2349 if( patternFilePath.endsWith(
".h2pattern") )
2363 successfullyAddedPattern++;
2367 ERRORLOG( QString(
"Error loading pattern %1").arg(patternFilePath) );
2374 QStringList tokens = sText.split(
"::" );
2375 QString sPatternName = tokens.at( 1 );
2378 Pattern *pPattern = pSong->getPatternList()->get( nTargetPattern );
2381 QString oldPatternName = pPattern->
get_name();
2385 if( QString( tokens.at(0) ).contains(
"drag pattern" )) drag =
true;
2398 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
2399 PatternList *pPatternList = pSong->getPatternList();
2405 if ( nSourcePattern < nTargetPattern) {
2406 for (
int nPatr = nSourcePattern; nPatr < nTargetPattern; nPatr++) {
2408 pPatternList->
replace( nPatr, pPattern );
2410 pPatternList->
replace( nTargetPattern, pSourcePattern );
2413 for (
int nPatr = nSourcePattern; nPatr > nTargetPattern; nPatr--) {
2415 pPatternList->
replace( nPatr, pPattern );
2417 pPatternList->
replace( nTargetPattern, pSourcePattern );
2438 auto pEv =
static_cast<MouseEvent*
>( event );
2447 if (!(event->buttons() & Qt::LeftButton)) {
2453 std::shared_ptr<Song> pSong =
m_pHydrogen->getSong();
2454 PatternList *pPatternList = pSong->getPatternList();
2456 if ( row >= (
int)pPatternList->
size() ) {
2459 Pattern *pPattern = pPatternList->
get( row );
2460 QString sName =
"<unknown>";
2464 QString sText = QString(
"move pattern:%1:%2").arg( row ).arg( sName );
2466 QDrag *pDrag =
new QDrag(
this);
2467 QMimeData *pMimeData =
new QMimeData;
2469 pMimeData->setText( sText );
2470 pDrag->setMimeData( pMimeData);
2474 pDrag->exec( Qt::CopyAction | Qt::MoveAction );
2477 QWidget::mouseMoveEvent(event);
2516 setAttribute(Qt::WA_OpaquePaintEvent);
2517 setMouseTracking(
true );
2529 qreal pixelRatio = devicePixelRatio();
2537 connect(
m_pTimer, &QTimer::timeout, [=]() {
2589 auto pSong = pHydrogen->getSong();
2590 auto pTimeline = pHydrogen->getTimeline();
2591 auto tagVector = pTimeline->getAllTags();
2593 QColor textColor( pPref->
getColorTheme()->m_songEditor_textColor );
2594 QColor textColorAlpha( textColor );
2595 textColorAlpha.setAlpha( 45 );
2597 QColor backgroundColor = pPref->
getColorTheme()->m_songEditor_alternateRowColor.darker( 115 );
2598 QColor backgroundInactiveColor = pPref->
getColorTheme()->m_midLightColor;
2599 QColor backgroundColorTempoMarkers = backgroundColor.darker( 120 );
2601 QColor colorHighlight = pPref->
getColorTheme()->m_highlightColor;
2603 QColor lineColor = pPref->
getColorTheme()->m_songEditor_lineColor;
2604 QColor lineColorAlpha( lineColor );
2605 lineColorAlpha.setAlpha( 45 );
2608 qreal pixelRatio = devicePixelRatio();
2623 p.fillRect( 0, 0, width(), height(), backgroundColorTempoMarkers );
2624 p.fillRect( 0, 25, nActiveWidth, height() - 25, backgroundColor );
2625 p.fillRect( nActiveWidth, 25, width() - nActiveWidth, height() - 25,
2626 backgroundInactiveColor );
2629 int nMaxPatternSequence = pPref->
getMaxBars();
2631 QColor textColorGrid( textColor );
2632 textColorGrid.setAlpha( 200 );
2633 p.setPen( QPen( textColorGrid, 1, Qt::SolidLine ) );
2634 for (
int ii = 0; ii < nMaxPatternSequence + 1; ii++) {
2637 if ( ( ii % 4 ) == 0 ) {
2638 p.drawLine( x, height() - 14, x, height() - 1);
2641 p.drawLine( x, height() - 6, x, height() - 1);
2646 p.setPen( textColor );
2647 for (uint i = 0; i < nMaxPatternSequence + 1; i += 4) {
2650 sprintf( tmp,
"%d", i + 1 );
2652 p.drawText( x, height() / 2 + 3,
m_nGridWidth, height() / 2 - 7,
2653 Qt::AlignHCenter, tmp );
2655 p.drawText( x + 2, height() / 2 + 3,
m_nGridWidth * 3.5, height() / 2 - 7,
2656 Qt::AlignLeft, tmp );
2666 for (
const auto& ttag : tagVector ){
2671 p.fillRect( rect, pPref->
getColorTheme()->m_highlightColor.darker( 135 ) );
2672 p.drawText( rect, Qt::AlignCenter,
"T");
2679 if ( ! pHydrogen->isTimelineEnabled() ) {
2680 p.setPen( textColorAlpha );
2682 QColor tempoMarkerGridColor( textColor );
2683 tempoMarkerGridColor.setAlpha( 170 );
2684 p.setPen( tempoMarkerGridColor );
2686 for (uint ii = 0; ii < nMaxPatternSequence + 1; ii++) {
2689 p.drawLine( x, 1, x, 4 );
2690 p.drawLine( x, height() / 2 - 5, x, height() / 2 );
2694 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
2695 for (
const auto& ttempoMarker : tempoMarkerVector ){
2699 p.setPen( QColor(35, 39, 51) );
2700 p.drawLine( 0, 0, width(), 0 );
2701 p.drawLine( 0, height() - 25, width(), height() - 25 );
2702 p.drawLine( 0, height(), width(), height() );
2709 if ( ! pTimeline->isFirstTempoMarkerSpecial() ) {
2716 if ( pTimeline->getAllTempoMarkers().size() == 1 ) {
2741 QWidget::leaveEvent( ev );
2750 int nColumn = std::max(
xToColumn( pEv->position().x() ), 0 );
2753 if ( pEv->position().y() > 22 ) {
2755 }
else if ( pEv->position().y() > 22 - 1 -
m_nTagHeight ) {
2774 }
else if ( ev->buttons() & Qt::RightButton ) {
2791 if ( ev->type() == QEvent::ToolTip ) {
2792 const auto helpEv =
dynamic_cast<QHelpEvent*
>(ev);
2793 showToolTip( helpEv->pos(), helpEv->globalPos() );
2797 return QWidget::event( ev );
2818 if ( nValue == 0 ) {
2826 auto pTimeline = pHydrogen->getTimeline();
2828 const int nColumn = std::max(
xToColumn( pos.x() ), 0 );
2830 if ( pHydrogen->isTimelineEnabled() &&
2831 pTimeline->isFirstTempoMarkerSpecial() &&
2834 const QString sBpm =
2835 pTimeline->getTempoMarkerAtColumn( nColumn )->getPrettyString( -1 );
2836 QToolTip::showText( globalPos, QString(
"%1: %2" )
2837 .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." ) )
2838 .arg( sBpm ),
this );
2842 if ( pTimeline->hasColumnTempoMarker( nColumn ) ) {
2843 const QString sBpm =
2844 pTimeline->getTempoMarkerAtColumn( nColumn )->getPrettyString( -1 );
2845 QToolTip::showText( globalPos, sBpm,
this );
2850 if ( pTimeline->hasColumnTag( nColumn ) ) {
2851 QToolTip::showText( globalPos,
2852 pTimeline->getTagAtColumn( nColumn ),
this );
2865 bool bTempoMarkerPresent =
2883 auto pCoreActionController = pHydrogen->getCoreActionController();
2885 int nColumn = std::max(
xToColumn( pEv->position().x() ), 0 );
2887 if (ev->button() == Qt::LeftButton ) {
2888 if ( pEv->position().y() > 22 ) {
2892 if ( nColumn > (
int)
m_pHydrogen->getSong()->getPatternGroupVector()->size() ) {
2897 pCoreActionController->activateSongMode(
true );
2901 m_pHydrogen->getCoreActionController()->locateToColumn( nColumn );
2904 else if ( pEv->position().y() > 22 - 1 -
m_nTagHeight ) {
2911 else if ( ev->button() == Qt::MiddleButton ) {
2914 else if (ev->button() == Qt::RightButton && pEv->position().y() >= 26) {
2916 if ( nColumn >= (
int)
m_pHydrogen->getSong()->getPatternGroupVector()->size() ) {
2945 auto pSongEditor = pHydrogenApp->getSongEditorPanel()->getSongEditor();
2948 auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
2958 QColor textColor( pPref->getColorTheme()->m_songEditor_textColor );
2959 QColor textColorAlpha( textColor );
2960 textColorAlpha.setAlpha( 45 );
2961 QColor highlightColor = pPref->getColorTheme()->m_highlightColor;
2962 QColor colorHovered( highlightColor );
2963 colorHovered.setAlpha( 200 );
2964 QColor backgroundColor = pPref->getColorTheme()->m_songEditor_alternateRowColor.darker( 115 );
2965 QColor backgroundColorTempoMarkers = backgroundColor.darker( 120 );
2970 QPainter painter(
this);
2971 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
2972 qreal pixelRatio = devicePixelRatio();
2977 pixelRatio * ev->rect().x(),
2978 pixelRatio * ev->rect().y(),
2979 pixelRatio * ev->rect().width(),
2980 pixelRatio * ev->rect().height()
2985 int nCurrentTempoMarkerColumn = -1;
2986 for (
const auto& tempoMarker : tempoMarkerVector ) {
2987 if ( tempoMarker->nColumn >
m_nColumn ) {
2990 nCurrentTempoMarkerColumn = tempoMarker->nColumn;
2992 if ( nCurrentTempoMarkerColumn == -1 &&
2993 tempoMarkerVector.size() != 0 ) {
2994 auto pTempoMarker = tempoMarkerVector[ tempoMarkerVector.size() - 1 ];
2995 if ( pTempoMarker !=
nullptr ) {
2996 nCurrentTempoMarkerColumn = pTempoMarker->nColumn;
2999 if ( nCurrentTempoMarkerColumn != -1 ) {
3000 auto pTempoMarker = pTimeline->getTempoMarkerAtColumn( nCurrentTempoMarkerColumn );
3001 if ( pTempoMarker !=
nullptr ) {
3005 painter.fillRect( rect, backgroundColorTempoMarkers );
3015 painter.drawLine( nX + nShaftOffset, 0, nX + nShaftOffset, height() );
3033 painter.fillRect( x, 1, 1, 4,
3034 backgroundColorTempoMarkers );
3035 painter.fillRect( x, height() / 2 - 5, 1, 4,
3036 backgroundColorTempoMarkers );
3042 backgroundColorTempoMarkers );
3045 painter.setPen( QPen( highlightColor, 1 ) );
3047 painter.drawLine( x, 1, x, 4 );
3048 painter.drawLine( x, height() / 2 - 5, x, height() / 2 - 1 );
3052 bool bTagPresent =
false;
3060 QFont font2( pPref->getApplicationFontFamily(), 5 );
3061 painter.setFont( font2 );
3063 painter.fillRect( rect, pPref->getColorTheme()->m_highlightColor );
3064 painter.setPen( pPref->getColorTheme()->m_highlightedTextColor );
3065 painter.drawText( rect, Qt::AlignCenter,
"T");
3067 painter.setFont( font );
3075 bool bTempoMarkerPresent =
false;
3076 if ( ! bTagPresent &&
3088 if ( pTimeline->hasColumnTempoMarker( nColumn ) ||
3089 ( pTimeline->isFirstTempoMarkerSpecial() &&
3092 auto pTempoMarker = pTimeline->getTempoMarkerAtColumn( nColumn );
3093 if ( pTempoMarker !=
nullptr ) {
3095 const bool bEmphasize = pTempoMarker->nColumn == nCurrentTempoMarkerColumn;
3098 painter.fillRect( rect, backgroundColorTempoMarkers );
3102 painter.setPen( QPen( colorHovered, 1 ) );
3104 painter.setPen( QPen( highlightColor, 1 ) );
3106 painter.drawRect( rect );
3108 painter.drawLine( rect.x(), 2, rect.x() +
m_nGridWidth / 2, 2 );
3110 bTempoMarkerPresent =
true;
3120 ! bTempoMarkerPresent ) ) ) ||
3122 ! bTempoMarkerPresent ) ) {
3126 color = colorHovered;
3128 color = highlightColor;
3132 painter.setPen( p );
3146 QRect hoveringRect( nCursorX, 6,
m_nGridWidth - 5, 12 );
3147 painter.fillRect( hoveringRect, backgroundColorTempoMarkers );
3148 painter.drawRect( hoveringRect );
3150 painter.drawRect( nCursorX, height() / 2 - 1 -
m_nTagHeight,
3156 if ( ! pHydrogenApp->hideKeyboardCursor() && pSongEditor->hasFocus() ) {
3157 int nCursorX =
columnToX( pSongEditor->getCursorColumn() ) + 2;
3159 QColor cursorColor = pPref->getColorTheme()->m_cursorColor;
3161 QPen p( cursorColor );
3163 painter.setPen( p );
3164 painter.setRenderHint( QPainter::Antialiasing );
3168 painter.drawLine( nCursorX, height() / 2 + 3,
3170 painter.drawLine( nCursorX, height() / 2 + 4,
3171 nCursorX, height() / 2 + 5 );
3172 painter.drawLine( nCursorX +
m_nGridWidth - 3, height() / 2 + 4,
3174 painter.drawLine( nCursorX, height() - 4,
3176 painter.drawLine( nCursorX, height() - 6,
3177 nCursorX, height() - 5 );
3178 painter.drawLine( nCursorX +
m_nGridWidth - 3, height() - 6,
3190 painter.drawLine( x + nShaftOffset, 0, x + nShaftOffset, height() / 2 + 1 );
3192 x + nShaftOffset, height() );
3195 if ( nPunchInPos <= nPunchOutPos ) {
3196 const int xIn =
columnToX( nPunchInPos );
3197 const int xOut =
columnToX( nPunchOutPos + 1 );
3198 painter.fillRect( xIn, 30, xOut-xIn+1, 12, QColor(200, 100, 100, 100) );
3199 QPen pen(QColor(200, 100, 100));
3200 painter.setPen(pen);
3201 painter.drawRect( xIn, 30, xOut-xIn+1, 12 );
3206 assert( pTempoMarker );
3210 auto weight = QFont::Normal;
3212 weight = QFont::Bold;
3215 const QFont font( pPref->getApplicationFontFamily(),
3218 const int x =
columnToX( pTempoMarker->nColumn );
3219 int nWidth = QFontMetrics( font ).size(
3220 Qt::TextSingleLine, pTempoMarker->getPrettyString( 2 ) ).width();
3224 const int nCoveredNeighborColumns =
3225 static_cast<int>(std::floor(
static_cast<float>(nWidth) /
3227 for (
int ii = 1; ii <= nCoveredNeighborColumns; ++ii ) {
3228 if ( pTimeline->hasColumnTempoMarker( pTempoMarker->nColumn + ii ) ) {
3234 QRect rect( x, 6, nWidth, 12 );
3236 return std::move( rect );
3240 assert( pTempoMarker );
3244 auto pSong = pHydrogen->getSong();
3245 auto pTimeline = pHydrogen->getTimeline();
3249 if ( pTempoMarker->nColumn == 0 && pTimeline->isFirstTempoMarkerSpecial() &&
3250 ! pHydrogen->isTimelineEnabled() ) {
3254 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
3261 QColor textColor( pPref->getColorTheme()->m_songEditor_textColor );
3263 if ( pTempoMarker->nColumn == 0 && pTimeline->isFirstTempoMarkerSpecial() ) {
3264 textColor = textColor.darker( 150 );
3267 if ( ! pHydrogen->isTimelineEnabled() ) {
3268 QColor textColorAlpha( textColor );
3269 textColorAlpha.setAlpha( 45 );
3270 painter.setPen( textColorAlpha );
3272 QColor tempoMarkerGridColor( textColor );
3273 tempoMarkerGridColor.setAlpha( 170 );
3274 painter.setPen( tempoMarkerGridColor );
3277 painter.drawLine( rect.x(), 2, rect.x() +
m_nGridWidth / 2, 2 );
3279 QColor tempoMarkerColor( textColor );
3280 if ( ! pHydrogen->isTimelineEnabled() ) {
3281 tempoMarkerColor.setAlpha( 45 );
3283 painter.setPen( tempoMarkerColor );
3286 font.setBold(
true );
3288 painter.setFont( font );
3289 painter.drawText( rect, Qt::AlignLeft | Qt::AlignVCenter,
3290 pTempoMarker->getPrettyString( 2 ) );
3293 font.setBold(
false );
3295 painter.setFont( font );
3300 const auto pTimeline =
m_pHydrogen->getTimeline();
3302 const auto tempoMarkerVector = pTimeline->getAllTempoMarkers();
3306 const auto pTransportPos =
m_pAudioEngine->getTransportPosition();
3307 const auto pPatternGroupVector =
3309 m_nColumn = std::max( pTransportPos->getColumn(), 0 );
3311 float fTick =
static_cast<float>(
m_nColumn);
3318 if ( pTransportPos->getColumn() == -1 ) {
3324 fTick = pPatternGroupVector->size();
3330 else if ( pPatternGroupVector->size() >
m_nColumn &&
3331 pPatternGroupVector->at(
m_nColumn )->size() > 0 ) {
3332 int nLength = pPatternGroupVector->at(
m_nColumn )->longest_pattern_length();
3333 fTick += (float)pTransportPos->getPatternTickPosition() /
3338 fTick += (float)pTransportPos->getPatternTickPosition() /
3359 update( updateRect );
3360 if ( fDiff > 1.0 || fDiff < -1.0 ) {
3363 updateRect.translate( -fDiff, 0 );
3364 update( updateRect );
3368 if ( pSongEditorPanel !=
nullptr ) {
3369 pSongEditorPanel->getSongEditor()->updatePosition( fTick );
3370 pSongEditorPanel->getPlaybackTrackWaveDisplay()->updatePosition( fTick );
3371 pSongEditorPanel->getAutomationPathView()->updatePosition( fTick );
3380 return static_cast<int>(
3386 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
Compatibility class to support QDropEvent more esily in Qt5 and Qt6.
Custom file dialog checking whether the user has write access to the selected folder before allowing ...
@ Playing
Transport is rolling.
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
std::shared_ptr< Song > getSong() const
Get the current song.
std::shared_ptr< Timeline > getTimeline() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
void updateSelectedPattern(bool bNeedsLock=true)
Updates the selected pattern to the one recorded note will be inserted to.
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.
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.
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
void set_info(const QString &info)
get the info of the pattern
static Pattern * load_file(const QString &sPpatternPath, std::shared_ptr< InstrumentList > pInstruments, bool bSilent=false)
load a pattern from a file
int get_length() const
set the denominator of the pattern
void set_category(const QString &category)
set the info of the pattern
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.
@ Stacked
An arbitrary number of pattern can be played.
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)
Compatibility class to support QMouseEvent more esily in Qt5 and Qt6.
Pattern Properties Dialog.
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...
SongEditorPatternList * getSongEditorPatternList() const
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
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()
static constexpr uint m_nInitialHeight
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.
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.
static constexpr int m_nMinimumHeight
int m_nActiveBpmWidgetColumn
virtual void paintEvent(QPaintEvent *ev) override
void invalidateBackground()
virtual void jackTimebaseStateChangedEvent(int nState) override
QRect calcTempoMarkerRect(std::shared_ptr< const H2Core::Timeline::TempoMarker > pTempoMarker, bool bEmphasize=false) const
~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)
static constexpr int m_nMinimumHeight
Default value of Preferences::m_nSongEditorGridHeight * 5 (patterns)
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.