2018-11-19 15:57:54 -05:00
# include "mainwindow.h"
# include "ui_mainwindow.h"
2018-11-28 05:19:10 -05:00
using Xybrid : : MainWindow ;
2018-11-19 15:57:54 -05:00
2018-11-28 05:19:10 -05:00
# include <QDebug>
# include <QKeyEvent>
2018-12-04 17:04:45 -05:00
# include <QShortcut>
2018-11-28 05:19:10 -05:00
# include <QTabWidget>
2018-12-01 10:41:14 -05:00
# include <QFileDialog>
2018-12-04 17:04:45 -05:00
# include <QInputDialog>
# include <QMessageBox>
2018-12-17 14:09:44 -05:00
# include <QWindow>
2018-12-06 14:27:22 -05:00
# include <QUndoStack>
2018-12-22 21:03:51 -05:00
# include <QTimer>
# include <QOpenGLWidget>
2018-12-25 01:54:23 -05:00
# include <QGLWidget>
2018-12-22 21:03:51 -05:00
# include <QScroller>
# include <QGraphicsTextItem>
2018-12-25 01:54:23 -05:00
# include <QJsonObject>
2018-12-22 21:03:51 -05:00
# include "data/graph.h"
2018-11-22 06:36:27 -05:00
2018-12-04 17:04:45 -05:00
# include "util/strings.h"
2018-12-22 21:03:51 -05:00
# include "util/lambdaeventfilter.h"
2018-12-06 07:25:57 -05:00
# include "fileops.h"
2018-12-04 17:04:45 -05:00
# include "ui/patternlistmodel.h"
# include "ui/patternsequencermodel.h"
2018-11-22 06:36:27 -05:00
# include "ui/patterneditoritemdelegate.h"
2018-12-22 21:03:51 -05:00
# include "ui/patchboard/patchboardscene.h"
2018-12-07 13:45:32 -05:00
# include "editing/projectcommands.h"
2018-12-22 21:03:51 -05:00
# include "config/pluginregistry.h"
2018-12-17 14:09:44 -05:00
# include "audio/audioengine.h"
2018-12-01 10:41:14 -05:00
using Xybrid : : Data : : Project ;
using Xybrid : : Data : : Pattern ;
2018-12-22 21:03:51 -05:00
using Xybrid : : Data : : Graph ;
using Xybrid : : Data : : Node ;
using Xybrid : : Data : : Port ;
2018-12-01 10:41:14 -05:00
2018-12-04 17:04:45 -05:00
using Xybrid : : UI : : PatternListModel ;
using Xybrid : : UI : : PatternSequencerModel ;
2018-11-22 06:36:27 -05:00
using Xybrid : : UI : : PatternEditorModel ;
using Xybrid : : UI : : PatternEditorItemDelegate ;
2018-12-22 21:03:51 -05:00
using Xybrid : : UI : : PatchboardScene ;
2018-12-07 13:45:32 -05:00
using namespace Xybrid : : Editing ;
2018-12-22 21:03:51 -05:00
using namespace Xybrid : : Config ;
2018-12-18 19:33:41 -05:00
using namespace Xybrid : : Audio ;
2018-12-07 13:45:32 -05:00
2018-11-23 09:03:35 -05:00
namespace {
2018-12-06 14:27:22 -05:00
constexpr const auto projectFilter = u8 " Xybrid project (*.xyp) \n All files (*) " ;
2018-11-23 09:03:35 -05:00
}
2018-11-19 15:57:54 -05:00
MainWindow : : MainWindow ( QWidget * parent ) :
QMainWindow ( parent ) ,
2018-11-22 06:36:27 -05:00
ui ( new Ui : : MainWindow ) {
2018-11-19 15:57:54 -05:00
ui - > setupUi ( this ) ;
2018-11-22 06:36:27 -05:00
2018-12-17 14:09:44 -05:00
setAttribute ( Qt : : WA_DeleteOnClose ) ;
// remove tab containing system widgets
ui - > tabWidget - > removeTab ( ui - > tabWidget - > indexOf ( ui - > extra_ ) ) ;
2018-12-06 14:27:22 -05:00
undoStack = new QUndoStack ( this ) ;
//undoStack->setUndoLimit(256);
2018-12-19 10:18:32 -05:00
connect ( undoStack , & QUndoStack : : cleanChanged , this , [ this ] ( bool ) {
2018-12-06 14:27:22 -05:00
updateTitle ( ) ;
} ) ;
2018-11-22 06:36:27 -05:00
2018-12-06 14:27:22 -05:00
auto * undoAction = undoStack - > createUndoAction ( this , tr ( " &Undo " ) ) ;
undoAction - > setShortcuts ( QKeySequence : : Undo ) ;
ui - > menuEdit - > addAction ( undoAction ) ;
auto * redoAction = undoStack - > createRedoAction ( this , tr ( " &Redo " ) ) ;
redoAction - > setShortcuts ( QKeySequence : : Redo ) ;
ui - > menuEdit - > addAction ( redoAction ) ;
2018-12-17 14:09:44 -05:00
auto * t = ui - > tabWidget ;
2018-12-04 17:04:45 -05:00
t - > setCornerWidget ( ui - > menuBar ) ;
2018-12-22 21:03:51 -05:00
t - > setCornerWidget ( ui - > logo , Qt : : TopLeftCorner ) ;
2018-12-17 14:09:44 -05:00
//ui->menuBar->setStyleSheet("QMenuBar { background: transparent; vertical-align: center; } QMenuBar::item { } QMenuBar::item:!pressed { background: transparent; }");
2018-11-22 06:36:27 -05:00
2018-12-01 10:41:14 -05:00
// prevent right pane of pattern view from being collapsed
ui - > patternViewSplitter - > setCollapsible ( 1 , false ) ;
2018-12-19 10:18:32 -05:00
connect ( ui - > patternViewSplitter , & QSplitter : : splitterMoved , this , [ this ] ( int , int ) {
2018-12-04 17:04:45 -05:00
// and when the list is collapsed, make sure header size is updated
ui - > patternEditor - > updateHeader ( ) ;
} ) ;
{ /* Set up pattern list */ } {
// model
ui - > patternList - > setModel ( new PatternListModel ( ui - > patternList , this ) ) ;
// events
// on selection change
2018-12-19 10:18:32 -05:00
connect ( ui - > patternList - > selectionModel ( ) , & QItemSelectionModel : : currentChanged , this , [ this ] ( const QModelIndex & index , const QModelIndex & old ) {
2018-12-04 17:04:45 -05:00
if ( index = = old ) return ; // no actual change
size_t idx = static_cast < size_t > ( index . row ( ) ) ;
if ( idx > = project - > patterns . size ( ) ) return ;
this - > selectPatternForEditing ( project - > patterns [ idx ] . get ( ) ) ;
} ) ;
// on click
2018-12-19 10:18:32 -05:00
connect ( ui - > patternList , & QListView : : clicked , this , [ this ] ( ) { // deselect on sequencer when list clicked
2018-12-04 17:04:45 -05:00
ui - > patternSequencer - > setCurrentIndex ( ui - > patternSequencer - > model ( ) - > index ( 0 , - 1 ) ) ;
} ) ;
// rightclick menu
2018-12-19 10:18:32 -05:00
connect ( ui - > patternList , & QListView : : customContextMenuRequested , this , [ this ] ( const QPoint & pt ) {
2018-12-04 17:04:45 -05:00
size_t idx = static_cast < size_t > ( ui - > patternList - > indexAt ( pt ) . row ( ) ) ;
std : : shared_ptr < Pattern > p = nullptr ;
if ( idx < project - > patterns . size ( ) ) p = project - > patterns [ idx ] ;
QMenu * menu = new QMenu ( this ) ;
2018-12-19 10:18:32 -05:00
menu - > addAction ( " New Pattern " , this , [ this , idx ] ( ) {
2018-12-07 13:45:32 -05:00
( new ProjectPatternAddCommand ( project , static_cast < int > ( idx ) ) ) - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
if ( p ) {
2018-12-19 10:18:32 -05:00
menu - > addAction ( " Duplicate Pattern " , this , [ this , p , idx ] ( ) {
2018-12-07 13:45:32 -05:00
( new ProjectPatternAddCommand ( project , static_cast < int > ( idx ) + 1 , - 1 , p ) ) - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
menu - > addSeparator ( ) ;
2018-12-19 10:18:32 -05:00
menu - > addAction ( " Delete Pattern " , this , [ this , p ] ( ) {
2018-12-04 17:04:45 -05:00
if ( QMessageBox : : warning ( this , " Are you sure? " , QString ( " Remove pattern %1? " ) . arg ( Util : : numAndName ( p - > index , p - > name ) ) , QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : No ) ! = QMessageBox : : Yes ) return ;
2018-12-07 13:45:32 -05:00
( new ProjectPatternDeleteCommand ( project , p ) ) - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
}
2018-12-25 01:54:23 -05:00
menu - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2018-12-04 17:04:45 -05:00
menu - > popup ( ui - > patternList - > mapToGlobal ( pt ) ) ;
2018-12-17 14:09:44 -05:00
} ) ; //*/
2018-12-04 17:04:45 -05:00
}
{ /* Set up sequencer */ } {
// model
ui - > patternSequencer - > setModel ( new PatternSequencerModel ( ui - > patternSequencer , this ) ) ;
// some metrics that the designer doesn't seem to like doing
ui - > patternSequencer - > horizontalHeader ( ) - > setSectionResizeMode ( QHeaderView : : Fixed ) ;
ui - > patternSequencer - > horizontalHeader ( ) - > setDefaultSectionSize ( 24 ) ;
ui - > patternSequencer - > verticalHeader ( ) - > setSectionResizeMode ( QHeaderView : : Fixed ) ;
ui - > patternSequencer - > verticalHeader ( ) - > setDefaultSectionSize ( 24 ) ;
// events
// on selection change
2018-12-19 10:18:32 -05:00
connect ( ui - > patternSequencer - > selectionModel ( ) , & QItemSelectionModel : : currentChanged , this , [ this ] ( const QModelIndex & index , const QModelIndex & ) {
2018-12-04 17:04:45 -05:00
size_t idx = static_cast < size_t > ( index . column ( ) ) ;
if ( idx > = project - > sequence . size ( ) ) return ;
this - > selectPatternForEditing ( project - > sequence [ idx ] ) ;
} ) ;
// rightclick menu
2018-12-19 10:18:32 -05:00
connect ( ui - > patternSequencer , & QTableView : : customContextMenuRequested , this , [ this ] ( const QPoint & pt ) {
2018-12-04 17:04:45 -05:00
size_t idx = static_cast < size_t > ( ui - > patternSequencer - > indexAt ( pt ) . column ( ) ) ;
2018-11-22 06:36:27 -05:00
2018-12-04 17:04:45 -05:00
QMenu * menu = new QMenu ( this ) ;
2018-12-19 10:18:32 -05:00
menu - > addAction ( " Insert Pattern " , this , [ this , idx ] ( ) {
2018-12-04 17:04:45 -05:00
if ( ! editingPattern - > validFor ( project ) ) return ; // nope
int si = static_cast < int > ( std : : min ( idx , project - > sequence . size ( ) ) ) ;
2018-12-07 13:45:32 -05:00
auto * c = new ProjectSequencerDeltaCommand ( project ) ;
c - > seq . insert ( c - > seq . begin ( ) + si , editingPattern . get ( ) ) ;
c - > seqSel = si + 1 ;
c - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
2018-12-19 10:18:32 -05:00
menu - > addAction ( " Insert Separator " , this , [ this , idx ] ( ) {
2018-12-04 17:04:45 -05:00
int si = static_cast < int > ( std : : min ( idx , project - > sequence . size ( ) ) ) ;
2018-12-07 13:45:32 -05:00
auto * c = new ProjectSequencerDeltaCommand ( project ) ;
c - > seq . insert ( c - > seq . begin ( ) + si , nullptr ) ;
c - > seqSel = si + 1 ;
c - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
2018-12-19 10:18:32 -05:00
if ( idx < project - > sequence . size ( ) ) menu - > addAction ( " Remove " , this , [ this , idx ] ( ) {
2018-12-07 13:45:32 -05:00
auto * c = new ProjectSequencerDeltaCommand ( project ) ;
c - > seq . erase ( c - > seq . begin ( ) + static_cast < ptrdiff_t > ( idx ) ) ;
c - > seqSel = static_cast < int > ( idx ) - 1 ;
c - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
menu - > addSeparator ( ) ;
2018-12-19 10:18:32 -05:00
menu - > addAction ( " Create New Pattern " , this , [ this , idx ] ( ) {
2018-12-04 17:04:45 -05:00
int si = static_cast < int > ( std : : min ( idx , project - > sequence . size ( ) ) ) ;
2018-12-07 13:45:32 -05:00
( new ProjectPatternAddCommand ( project , - 1 , si ) ) - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
if ( idx < project - > sequence . size ( ) & & project - > sequence [ idx ] ) {
2018-12-19 10:18:32 -05:00
menu - > addAction ( " Duplicate Pattern " , this , [ this , idx , p = project - > patterns [ project - > sequence [ idx ] - > index ] ] ( ) {
2018-12-04 17:04:45 -05:00
int si = static_cast < int > ( std : : min ( idx + 1 , project - > sequence . size ( ) ) ) ;
2018-12-07 13:45:32 -05:00
( new ProjectPatternAddCommand ( project , static_cast < int > ( p - > index ) + 1 , si , p ) ) - > commit ( ) ;
2018-12-04 17:04:45 -05:00
} ) ;
}
2018-12-25 01:54:23 -05:00
menu - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2018-12-04 17:04:45 -05:00
menu - > popup ( ui - > patternSequencer - > mapToGlobal ( pt ) ) ;
2018-12-17 14:09:44 -05:00
} ) ; //*/
2018-12-04 17:04:45 -05:00
}
{ /* Set up keyboard shortcuts for pattern view */ } {
// Ctrl+PgUp/Down - previous or next pattern in sequencer
2018-12-19 10:18:32 -05:00
connect ( new QShortcut ( QKeySequence ( " Ctrl+PgUp " ) , ui - > pattern ) , & QShortcut : : activated , this , [ this ] ( ) {
2018-12-04 17:04:45 -05:00
auto i = ui - > patternSequencer - > currentIndex ( ) ;
if ( ! i . isValid ( ) ) {
ui - > patternSequencer - > setCurrentIndex ( ui - > patternSequencer - > model ( ) - > index ( ui - > patternSequencer - > horizontalHeader ( ) - > count ( ) - 1 , 0 ) ) ;
return ;
}
auto count = ui - > patternSequencer - > horizontalHeader ( ) - > count ( ) ;
ui - > patternSequencer - > setCurrentIndex ( i . siblingAtColumn ( ( count + i . column ( ) - 1 ) % count ) ) ;
} ) ;
2018-12-19 10:18:32 -05:00
connect ( new QShortcut ( QKeySequence ( " Ctrl+PgDown " ) , ui - > pattern ) , & QShortcut : : activated , this , [ this ] ( ) {
2018-12-04 17:04:45 -05:00
auto i = ui - > patternSequencer - > currentIndex ( ) ;
if ( ! i . isValid ( ) ) {
ui - > patternSequencer - > setCurrentIndex ( ui - > patternSequencer - > model ( ) - > index ( 0 , 0 ) ) ;
return ;
}
auto count = ui - > patternSequencer - > horizontalHeader ( ) - > count ( ) ;
ui - > patternSequencer - > setCurrentIndex ( i . siblingAtColumn ( ( count + i . column ( ) + 1 ) % count ) ) ;
} ) ;
2018-12-17 14:09:44 -05:00
2018-12-18 19:33:41 -05:00
// TEMP - play/stop
2018-12-25 01:54:23 -05:00
connect ( new QShortcut ( QKeySequence ( " Ctrl+P " ) , this ) , & QShortcut : : activated , this , [ this ] ( ) {
2018-12-18 19:33:41 -05:00
if ( audioEngine - > playbackMode ( ) = = AudioEngine : : Playing ) audioEngine - > stop ( ) ;
else audioEngine - > play ( project ) ;
} ) ;
2018-12-17 14:09:44 -05:00
/* tmp test
2018-12-19 10:18:32 -05:00
connect ( new QShortcut ( QKeySequence ( " Ctrl+F1 " ) , ui - > patchboard ) , & QShortcut : : activated , this , [ this ] ( ) {
2018-12-17 14:09:44 -05:00
auto inp = QInputDialog : : getText ( this , " yes " , " yes " ) ;
WId id = inp . toULongLong ( ) ;
auto * w = QWindow : : fromWinId ( id ) ;
auto * w2 = new QWindow ( ) ;
auto * wc = QWidget : : createWindowContainer ( w2 ) ;
w - > setParent ( w2 ) ;
ui - > patchboard - > layout ( ) - > addWidget ( wc ) ;
} ) ;
*/
2018-12-04 17:04:45 -05:00
}
2018-12-22 21:03:51 -05:00
{ /* Set up patchboard view */ } {
//ui->patchboardView->setDragMode(QGraphicsView::DragMode::RubberBandDrag);
auto * view = ui - > patchboardView ;
2018-12-25 01:54:23 -05:00
bool enableHWAccel = false ; // disabled because QOpenGLWidget has some huge lag issues in this context
if ( enableHWAccel ) {
auto * vp = new QOpenGLWidget ( ) ;
view - > setViewport ( vp ) ; // enable hardware acceleration
}
2018-12-22 21:03:51 -05:00
view - > setRenderHints ( QPainter : : Antialiasing | QPainter : : SmoothPixmapTransform | QPainter : : HighQualityAntialiasing ) ;
2018-12-25 01:54:23 -05:00
glEnable ( GL_MULTISAMPLE ) ;
glEnable ( GL_LINE_SMOOTH ) ;
//QGL::FormatOption::Rgba
2018-12-22 21:03:51 -05:00
view - > setAlignment ( Qt : : AlignTop | Qt : : AlignLeft ) ;
view - > setHorizontalScrollBarPolicy ( Qt : : ScrollBarAlwaysOff ) ;
view - > setVerticalScrollBarPolicy ( Qt : : ScrollBarAlwaysOff ) ;
view - > setAttribute ( Qt : : WA_AcceptTouchEvents , true ) ;
QScroller : : grabGesture ( view , QScroller : : MiddleMouseButtonGesture ) ;
{
auto prop = QScroller : : scroller ( view ) - > scrollerProperties ( ) ;
prop . setScrollMetric ( QScrollerProperties : : HorizontalOvershootPolicy , QScrollerProperties : : OvershootAlwaysOff ) ;
prop . setScrollMetric ( QScrollerProperties : : VerticalOvershootPolicy , QScrollerProperties : : OvershootAlwaysOff ) ;
prop . setScrollMetric ( QScrollerProperties : : AxisLockThreshold , 1 ) ;
QScroller : : scroller ( view ) - > setScrollerProperties ( prop ) ;
QScroller : : scroller ( view ) - > setSnapPositionsX ( { } ) ;
QScroller : : scroller ( view ) - > setSnapPositionsY ( { } ) ;
}
// event filter to make drag-to-select only happen on left click
view - > viewport ( ) - > installEventFilter ( new LambdaEventFilter ( view , [ view ] ( QObject * w , QEvent * e ) {
if ( e - > type ( ) = = QEvent : : MouseButtonPress ) {
auto * me = static_cast < QMouseEvent * > ( e ) ;
// initiate drag
2018-12-25 01:54:23 -05:00
if ( me - > button ( ) = = Qt : : LeftButton ) {
view - > setDragMode ( QGraphicsView : : RubberBandDrag ) ;
}
2018-12-22 21:03:51 -05:00
} else if ( e - > type ( ) = = QEvent : : MouseButtonRelease ) { // disable drag after end
2018-12-25 01:54:23 -05:00
QTimer : : singleShot ( 1 , [ view ] {
view - > setDragMode ( QGraphicsView : : NoDrag ) ;
} ) ;
2018-12-22 21:03:51 -05:00
}
return w - > QObject : : eventFilter ( w , e ) ;
} ) ) ;
}
2018-12-04 17:04:45 -05:00
// Set up signaling from project to UI
2018-12-17 14:09:44 -05:00
socket = new UISocket ( ) ;
socket - > setParent ( this ) ;
2018-12-07 13:45:32 -05:00
socket - > window = this ;
2018-12-06 14:27:22 -05:00
socket - > undoStack = undoStack ;
2018-12-17 14:09:44 -05:00
connect ( socket , & UISocket : : updatePatternLists , this , & MainWindow : : updatePatternLists ) ;
2018-12-19 10:18:32 -05:00
connect ( socket , & UISocket : : patternUpdated , this , [ this ] ( Pattern * p ) {
2018-12-06 14:27:22 -05:00
if ( editingPattern . get ( ) ! = p ) return ;
ui - > patternEditor - > refresh ( ) ;
} ) ;
2018-12-19 10:18:32 -05:00
connect ( socket , & UISocket : : rowUpdated , this , [ this ] ( Pattern * p , int ch , int r ) {
2018-12-06 14:27:22 -05:00
if ( editingPattern . get ( ) ! = p ) return ;
const auto cpc = PatternEditorModel : : colsPerChannel ;
auto ind = ui - > patternEditor - > model ( ) - > index ( r , ch * cpc ) ;
emit ui - > patternEditor - > model ( ) - > dataChanged ( ind , ind . siblingAtColumn ( ( ch + 1 ) * cpc - 1 ) ) ;
static_cast < PatternEditorModel * > ( ui - > patternEditor - > model ( ) ) - > updateColumnDisplay ( ) ;
} ) ;
2018-12-25 01:54:23 -05:00
connect ( socket , & UISocket : : openGraph , this , [ this ] ( Graph * g ) {
if ( ! g ) return ;
auto gg = std : : static_pointer_cast < Graph > ( g - > shared_from_this ( ) ) ;
QString name = QString : : fromStdString ( gg - > name ) ;
if ( name . isEmpty ( ) ) name = QString : : fromStdString ( gg - > pluginName ( ) ) ;
ui - > patchboardBreadcrumbs - > push ( name , this , [ this , gg ] {
openGraph ( gg ) ;
} ) ;
} ) ;
2018-11-23 09:03:35 -05:00
2018-12-19 10:18:32 -05:00
// and from audio engine
2018-12-22 21:03:51 -05:00
connect ( audioEngine , & AudioEngine : : playbackModeChanged , this , [ this , undoAction , redoAction ] ( ) {
2018-12-19 10:18:32 -05:00
bool locked = project - > editingLocked ( ) ;
undoAction - > setEnabled ( ! locked ) ;
redoAction - > setEnabled ( ! locked ) ;
} ) ;
2018-12-04 17:04:45 -05:00
// and start with a new project
2018-12-01 10:41:14 -05:00
menuFileNew ( ) ;
2018-11-19 15:57:54 -05:00
}
2018-11-22 06:36:27 -05:00
MainWindow : : ~ MainWindow ( ) {
2018-11-19 15:57:54 -05:00
delete ui ;
}
2018-11-28 05:19:10 -05:00
bool MainWindow : : eventFilter ( QObject * obj [[maybe_unused]] , QEvent * event ) {
if ( event - > type ( ) = = QEvent : : KeyPress ) {
auto ke = static_cast < QKeyEvent * > ( event ) ;
qDebug ( ) < < QString ( " key pressed: %1 " ) . arg ( ( ke - > key ( ) ) ) ;
if ( ke - > key ( ) = = Qt : : Key_Tab ) {
qDebug ( ) < < QString ( " tab " ) ;
auto * t = this - > ui - > tabWidget ;
t - > setTabPosition ( QTabWidget : : TabPosition : : South ) ;
t - > setCurrentIndex ( t - > currentIndex ( ) + 1 ) ;
return true ;
}
}
return false ;
}
2018-12-01 10:41:14 -05:00
void MainWindow : : menuFileNew ( ) {
auto hold = project ; // keep alive until done
2018-12-19 10:18:32 -05:00
if ( audioEngine - > playingProject ( ) = = project ) audioEngine - > stop ( ) ;
2018-12-01 10:41:14 -05:00
project = std : : make_shared < Project > ( ) ;
2018-12-04 17:04:45 -05:00
project - > sequence . push_back ( project - > newPattern ( ) . get ( ) ) ;
2018-12-01 10:41:14 -05:00
onNewProjectLoaded ( ) ;
}
void MainWindow : : menuFileOpen ( ) {
2018-12-06 14:27:22 -05:00
auto fileName = QFileDialog : : getOpenFileName ( this , " Open project... " , QString ( ) , projectFilter ) ;
2018-12-01 10:41:14 -05:00
if ( fileName . isEmpty ( ) ) return ; // canceled
2018-12-06 07:25:57 -05:00
auto np = FileOps : : loadProject ( fileName ) ;
if ( ! np ) {
QMessageBox : : critical ( this , " Error " , " Error loading project " ) ;
return ;
}
2018-12-19 10:18:32 -05:00
if ( audioEngine - > playingProject ( ) = = project ) audioEngine - > stop ( ) ;
2018-12-06 07:25:57 -05:00
project = np ;
onNewProjectLoaded ( ) ;
2018-12-01 10:41:14 -05:00
}
void MainWindow : : menuFileSave ( ) {
2018-12-06 07:25:57 -05:00
if ( project - > fileName . isEmpty ( ) ) menuFileSaveAs ( ) ;
else {
FileOps : : saveProject ( project ) ;
2018-12-25 01:54:23 -05:00
undoStack - > setClean ( ) ;
2018-12-06 07:25:57 -05:00
}
2018-12-01 10:41:14 -05:00
}
void MainWindow : : menuFileSaveAs ( ) {
2018-12-06 14:27:22 -05:00
auto fileName = QFileDialog : : getSaveFileName ( this , " Save project as... " , QString ( ) , projectFilter ) ;
2018-12-01 10:41:14 -05:00
if ( fileName . isEmpty ( ) ) return ; // canceled
2018-12-06 07:25:57 -05:00
FileOps : : saveProject ( project , fileName ) ;
2018-12-25 01:54:23 -05:00
undoStack - > setClean ( ) ;
updateTitle ( ) ;
2018-12-01 10:41:14 -05:00
}
void MainWindow : : onNewProjectLoaded ( ) {
2018-12-06 14:27:22 -05:00
undoStack - > clear ( ) ;
2018-12-17 14:09:44 -05:00
project - > socket = socket ;
2018-12-08 07:01:45 -05:00
updatePatternLists ( ) ;
patternSelection ( 0 ) ;
sequenceSelection ( - 1 ) ;
for ( size_t i = 0 ; i < project - > sequence . size ( ) ; i + + ) { // find first non-spacer in sequence, else fall back to first pattern numerically
if ( project - > sequence [ i ] = = nullptr ) continue ;
sequenceSelection ( static_cast < int > ( i ) ) ;
2018-12-01 10:41:14 -05:00
break ;
}
2018-12-06 14:27:22 -05:00
2018-12-25 01:54:23 -05:00
//openGraph(project->rootGraph);
ui - > patchboardBreadcrumbs - > clear ( ) ;
ui - > patchboardBreadcrumbs - > push ( " / " , this , [ this , gg = project - > rootGraph ] {
openGraph ( gg ) ;
} ) ;
2018-12-22 21:03:51 -05:00
2018-12-06 14:27:22 -05:00
updateTitle ( ) ;
2018-12-01 10:41:14 -05:00
}
2018-12-07 13:45:32 -05:00
int MainWindow : : patternSelection ( int n ) {
auto i = ui - > patternList - > currentIndex ( ) ;
if ( n > = - 1 ) ui - > patternList - > setCurrentIndex ( ui - > patternList - > model ( ) - > index ( n , 0 ) ) ;
return i . isValid ( ) ? i . row ( ) : - 1 ;
}
int MainWindow : : sequenceSelection ( int n ) {
auto i = ui - > patternSequencer - > currentIndex ( ) ;
if ( n > = - 1 ) ui - > patternSequencer - > setCurrentIndex ( ui - > patternSequencer - > model ( ) - > index ( 0 , n ) ) ;
return i . isValid ( ) ? i . column ( ) : - 1 ;
}
2018-12-19 10:18:32 -05:00
void MainWindow : : playbackPosition ( int seq , int row ) {
sequenceSelection ( seq ) ;
auto mi = ui - > patternEditor - > currentIndex ( ) . siblingAtRow ( row ) ;
if ( ! mi . isValid ( ) ) mi = ui - > patternEditor - > model ( ) - > index ( row , 0 ) ;
ui - > patternEditor - > setCurrentIndex ( mi ) ;
ui - > patternEditor - > selectionModel ( ) - > select ( QItemSelection ( mi . siblingAtColumn ( 0 ) , mi . siblingAtColumn ( ui - > patternEditor - > horizontalHeader ( ) - > count ( ) - 1 ) ) , QItemSelectionModel : : SelectionFlag : : ClearAndSelect ) ;
ui - > patternEditor - > scrollTo ( mi , QAbstractItemView : : PositionAtCenter ) ;
}
2018-12-04 17:04:45 -05:00
void MainWindow : : updatePatternLists ( ) {
emit ui - > patternList - > model ( ) - > layoutChanged ( ) ;
emit ui - > patternSequencer - > model ( ) - > layoutChanged ( ) ;
2018-12-07 13:45:32 -05:00
if ( auto i = ui - > patternList - > currentIndex ( ) ; i . isValid ( ) & & ! ui - > patternSequencer - > currentIndex ( ) . isValid ( ) )
selectPatternForEditing ( project - > patterns [ static_cast < size_t > ( i . row ( ) ) ] . get ( ) ) ; // make sure pattern editor matches selection
2018-12-04 17:04:45 -05:00
if ( editingPattern & & ! editingPattern - > validFor ( project ) ) // if current pattern invalidated, select new one
selectPatternForEditing ( project - > patterns [ std : : min ( editingPattern - > index , project - > patterns . size ( ) - 1 ) ] . get ( ) ) ;
}
2018-12-06 14:27:22 -05:00
void MainWindow : : updateTitle ( ) {
QString title = u8 " Xybrid - %1%2 " ;
if ( project - > fileName . isEmpty ( ) ) title = title . arg ( " (new project) " ) ;
else title = title . arg ( QFileInfo ( project - > fileName ) . baseName ( ) ) ;
if ( undoStack - > isClean ( ) ) title = title . arg ( " " ) ;
else title = title . arg ( " * " ) ;
this - > setWindowTitle ( title ) ;
}
2018-12-01 10:41:14 -05:00
bool MainWindow : : selectPatternForEditing ( Pattern * pattern ) {
2018-12-04 17:04:45 -05:00
if ( ! pattern | | pattern = = editingPattern . get ( ) ) return false ; // no u
2018-12-01 10:41:14 -05:00
if ( pattern - > project ! = project . get ( ) ) return false ; // wrong project
if ( project - > patterns . size ( ) < = pattern - > index ) return false ; // invalid id
auto sp = project - > patterns [ pattern - > index ] ;
if ( sp . get ( ) ! = pattern ) return false ; // wrong index
auto hold = editingPattern ; // keep alive until done
editingPattern = sp ;
2018-12-04 17:04:45 -05:00
ui - > patternEditor - > setPattern ( editingPattern ) ;
ui - > patternList - > setCurrentIndex ( ui - > patternList - > model ( ) - > index ( static_cast < int > ( editingPattern - > index ) , 0 ) ) ;
2018-12-01 10:41:14 -05:00
return true ;
}
2018-12-22 21:03:51 -05:00
void MainWindow : : openGraph ( const std : : shared_ptr < Data : : Graph > & g ) {
if ( ! g ) return ; // invalid
2018-12-25 01:54:23 -05:00
QPointF scrollPt ( g - > viewX , g - > viewY ) ;
2018-12-22 21:03:51 -05:00
ui - > patchboardView - > setScene ( new PatchboardScene ( ui - > patchboardView , g ) ) ;
2018-12-25 01:54:23 -05:00
QScroller : : scroller ( ui - > patchboardView ) - > scrollTo ( scrollPt , 0 ) ;
QScroller : : scroller ( ui - > patchboardView ) - > scrollTo ( scrollPt , 1 ) ;
2018-12-22 21:03:51 -05:00
}