2019-06-16 14:40:09 -04:00
# include "samplelistmodel.h"
using Xybrid : : UI : : SampleListModel ;
# include "data/sample.h"
using Xybrid : : Data : : Project ;
using Xybrid : : Data : : Pattern ;
using Xybrid : : Data : : Sample ;
2019-06-23 03:57:40 -04:00
# include "uisocket.h"
2019-06-16 14:40:09 -04:00
# include "editing/projectcommands.h"
# include "editing/patterncommands.h"
using namespace Xybrid : : Editing ;
2019-06-25 04:31:57 -04:00
# include <QTreeView>
2019-06-16 14:40:09 -04:00
# include <QDebug>
2019-06-25 04:31:57 -04:00
# include <QTimer>
2019-06-25 16:46:44 -04:00
# include <QStringBuilder>
2019-06-16 14:40:09 -04:00
# include <QMimeData>
# include <QUrl>
2019-06-17 02:23:45 -04:00
# include <QMenu>
# include <QMessageBox>
2019-06-16 14:40:09 -04:00
# include "mainwindow.h"
2019-07-22 09:19:46 -04:00
2019-06-25 02:15:56 -04:00
SampleListModel : : SampleListModel ( QObject * parent , MainWindow * window ) : QAbstractItemModel ( parent ) {
2019-06-16 14:40:09 -04:00
this - > window = window ;
2019-06-25 04:31:57 -04:00
root = std : : make_shared < DirectoryNode > ( ) ;
auto view = static_cast < QTreeView * > ( parent ) ;
2019-06-16 14:40:09 -04:00
2019-06-16 15:14:46 -04:00
connect ( window , & MainWindow : : projectLoaded , this , [ this , view ] {
view - > setCurrentIndex ( index ( - 1 , - 1 ) ) ; // select none
2019-06-16 14:40:09 -04:00
refresh ( ) ;
} ) ;
2019-06-25 16:09:34 -04:00
view - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( view , & QTreeView : : customContextMenuRequested , this , [ this , view ] ( const QPoint & pt ) {
auto dn = static_cast < DirectoryNode * > ( view - > indexAt ( pt ) . internalPointer ( ) ) ;
if ( ! dn ) return ; // no items applicable yet
2019-06-17 02:23:45 -04:00
auto menu = new QMenu ( view ) ;
2019-06-25 16:09:34 -04:00
if ( dn ) {
2019-06-25 16:58:53 -04:00
menu - > addAction ( " Rename... " , this , [ this , view , dn ] { view - > edit ( createIndex ( dn - > index , 0 , dn ) ) ; } ) ;
2019-06-25 16:09:34 -04:00
if ( dn - > isDirectory ( ) ) {
menu - > addAction ( " Unpack Folder " , this , [ this , dn ] {
dn - > name = " " ;
propagateSampleNames ( dn ) ;
refresh ( ) ;
} ) ;
menu - > addAction ( " Delete Folder " , this , [ this , dn , view ] {
if ( QMessageBox : : warning ( view , " Are you sure? " , QString ( " Remove folder \" %1 \" ? \n (This cannot be undone!) " ) . arg ( dn - > name ) , QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : No ) ! = QMessageBox : : Yes ) return ;
auto * project = this - > window - > getProject ( ) . get ( ) ;
dn - > treeExec ( [ project ] ( DirectoryNode * dn ) {
if ( dn - > isDirectory ( ) ) return ;
auto smp = project - > samples [ dn - > data . toUuid ( ) ] ;
smp - > project - > samples . remove ( smp - > uuid ) ;
smp - > project = nullptr ;
} ) ;
refresh ( ) ;
} ) ;
} else {
auto * project = this - > window - > getProject ( ) . get ( ) ;
auto smp = project - > samples [ dn - > data . toUuid ( ) ] ;
menu - > addAction ( " Delete Sample " , this , [ this , dn , smp , view ] {
if ( QMessageBox : : warning ( view , " Are you sure? " , QString ( " Remove sample \" %1 \" ? \n (This cannot be undone!) " ) . arg ( dn - > name ) , QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : No ) ! = QMessageBox : : Yes ) return ;
smp - > project - > samples . remove ( smp - > uuid ) ;
smp - > project = nullptr ;
refresh ( ) ;
} ) ;
}
}
2019-06-17 02:23:45 -04:00
menu - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
menu - > popup ( view - > mapToGlobal ( pt ) ) ;
2019-06-25 16:09:34 -04:00
} ) ;
2019-06-23 03:57:40 -04:00
connect ( window - > uiSocket ( ) , & UISocket : : updatePatternLists , this , [ this ] { refresh ( ) ; } ) ;
2019-06-16 14:40:09 -04:00
}
2019-06-25 04:31:57 -04:00
std : : shared_ptr < Sample > SampleListModel : : itemAt ( const QModelIndex & index ) {
if ( ! index . isValid ( ) | | index . model ( ) ! = this | | ! index . internalPointer ( ) ) return nullptr ;
auto dn = static_cast < DirectoryNode * > ( index . internalPointer ( ) ) ;
if ( dn - > isDirectory ( ) ) return nullptr ;
auto * project = window - > getProject ( ) . get ( ) ;
if ( ! project ) return nullptr ;
return project - > samples [ dn - > data . toUuid ( ) ] ;
2019-06-16 15:14:46 -04:00
}
2019-06-16 14:40:09 -04:00
void SampleListModel : : refresh ( ) {
2019-06-25 04:31:57 -04:00
DirectoryNode * dn = nullptr ;
auto view = static_cast < QTreeView * > ( QObject : : parent ( ) ) ;
if ( auto ind = view - > currentIndex ( ) ; ind . isValid ( ) ) dn = static_cast < DirectoryNode * > ( ind . internalPointer ( ) ) ;
2019-06-25 16:09:34 -04:00
hold = root ; // hold old tree alive until next refresh
2019-06-25 04:31:57 -04:00
root = std : : make_shared < DirectoryNode > ( ) ;
auto * project = window - > getProject ( ) . get ( ) ;
if ( ! project ) return ;
for ( auto s : project - > samples ) root - > placeData ( s - > name , s - > uuid ) ;
root - > sortTree ( ) ;
view - > setCurrentIndex ( QModelIndex ( ) ) ;
emit layoutChanged ( ) ;
2019-06-25 16:09:34 -04:00
// find previously selected path
if ( dn ) if ( auto f = root - > findPath ( dn - > path ( ) ) ; f ) view - > setCurrentIndex ( createIndex ( f - > index , 0 , f ) ) ;
2019-06-25 14:57:43 -04:00
auto ii = view - > currentIndex ( ) ;
while ( ii . isValid ( ) ) { // expand down to selected
view - > expand ( ii ) ;
ii = ii . parent ( ) ;
}
2019-06-16 14:40:09 -04:00
}
2019-06-25 04:31:57 -04:00
int SampleListModel : : rowCount ( const QModelIndex & parent [[maybe_unused]] ) const {
auto dn = const_cast < DirectoryNode * > ( root . get ( ) ) ;
if ( parent . isValid ( ) ) dn = static_cast < DirectoryNode * > ( parent . internalPointer ( ) ) ;
return dn - > children . size ( ) ;
2019-06-16 14:40:09 -04:00
}
int SampleListModel : : columnCount ( const QModelIndex & parent [[maybe_unused]] ) const {
return 1 ;
}
QVariant SampleListModel : : data ( const QModelIndex & index , int role ) const {
auto * project = window - > getProject ( ) . get ( ) ;
if ( ! project ) return QVariant ( ) ;
2019-06-25 04:31:57 -04:00
if ( ! index . internalPointer ( ) ) return QVariant ( ) ;
2019-06-16 14:40:09 -04:00
if ( role = = Qt : : DisplayRole ) {
2019-06-25 04:31:57 -04:00
auto dn = static_cast < DirectoryNode * > ( index . internalPointer ( ) ) ;
auto c = dn - > name ;
2019-06-16 14:40:09 -04:00
if ( c . isEmpty ( ) ) return " (unnamed) " ;
return c ;
}
if ( role = = Qt : : EditRole ) {
2019-06-25 04:31:57 -04:00
auto dn = static_cast < DirectoryNode * > ( index . internalPointer ( ) ) ;
return dn - > name ;
2019-06-16 14:40:09 -04:00
}
return QVariant ( ) ;
}
2019-06-25 14:57:43 -04:00
void SampleListModel : : propagateSampleNames ( DirectoryNode * dn ) {
if ( ! dn - > data . isNull ( ) ) {
auto * project = window - > getProject ( ) . get ( ) ;
project - > samples [ dn - > data . toUuid ( ) ] - > name = dn - > path ( ) ;
} else for ( auto c : dn - > children ) propagateSampleNames ( c ) ;
}
2019-06-25 04:31:57 -04:00
bool SampleListModel : : setData ( const QModelIndex & index , const QVariant & value , int role ) {
2019-06-16 14:40:09 -04:00
if ( role = = Qt : : EditRole ) {
2019-06-25 14:57:43 -04:00
auto dn = static_cast < DirectoryNode * > ( index . internalPointer ( ) ) ;
dn - > name = value . toString ( ) ;
propagateSampleNames ( dn ) ;
refresh ( ) ;
/*if (auto smp = const_cast<SampleListModel*>(this)->itemAt(index); smp) {
2019-06-25 04:31:57 -04:00
smp - > name = value . toString ( ) ;
refresh ( ) ;
} else { // directory
// TODO
2019-06-25 14:57:43 -04:00
} */
2019-06-25 04:31:57 -04:00
}
/*if (role == Qt::EditRole) {
2019-06-16 14:40:09 -04:00
auto * project = window - > getProject ( ) . get ( ) ;
if ( ! project ) return true ;
2019-06-25 04:31:57 -04:00
auto smp = itemAt ( index ) ;
2019-06-16 14:40:09 -04:00
smp - > name = value . toString ( ) ;
refresh ( ) ;
2019-06-25 04:31:57 -04:00
} */
2019-06-16 14:40:09 -04:00
return true ;
}
Qt : : ItemFlags SampleListModel : : flags ( const QModelIndex & index ) const {
2019-06-25 14:57:43 -04:00
return Qt : : ItemIsEditable | Qt : : ItemIsDragEnabled | Qt : : ItemIsDropEnabled | QAbstractItemModel : : flags ( index ) ;
2019-06-25 02:15:56 -04:00
}
2019-06-25 04:31:57 -04:00
QModelIndex SampleListModel : : index ( int row , int column , const QModelIndex & parent ) const {
DirectoryNode * dn = nullptr ;
if ( parent . isValid ( ) ) dn = static_cast < DirectoryNode * > ( parent . internalPointer ( ) ) ;
if ( ! dn ) dn = const_cast < DirectoryNode * > ( root . get ( ) ) ;
if ( row > = 0 & & row < dn - > children . size ( ) ) return createIndex ( row , column , dn - > children [ row ] ) ;
return QModelIndex ( ) ;
2019-06-25 02:15:56 -04:00
}
QModelIndex SampleListModel : : parent ( const QModelIndex & index ) const {
2019-06-25 04:31:57 -04:00
if ( ! index . isValid ( ) ) return QModelIndex ( ) ;
auto dn = static_cast < DirectoryNode * > ( index . internalPointer ( ) ) ;
if ( ! dn - > parent | | dn - > parent = = root . get ( ) ) return QModelIndex ( ) ;
return createIndex ( dn - > parent - > index , 0 , dn - > parent ) ;
2019-06-16 14:40:09 -04:00
}
Qt : : DropActions SampleListModel : : supportedDropActions ( ) const {
return Qt : : CopyAction ;
}
QStringList SampleListModel : : mimeTypes ( ) const {
QStringList types ;
//types << "xybrid-internal/x-pattern-index";
return types ;
}
2019-06-25 14:57:43 -04:00
QMimeData * SampleListModel : : mimeData ( const QModelIndexList & indexes ) const {
if ( indexes . empty ( ) ) return new QMimeData ( ) ;
auto dn = static_cast < DirectoryNode * > ( indexes . first ( ) . internalPointer ( ) ) ;
if ( ! dn ) return new QMimeData ( ) ;
2019-06-16 14:40:09 -04:00
auto d = new QMimeData ( ) ;
2019-06-25 14:57:43 -04:00
QByteArray b ;
b . resize ( sizeof ( void * ) * 2 ) ;
QDataStream s ( & b , QIODevice : : WriteOnly ) ;
s < < reinterpret_cast < qintptr > ( this ) ;
s < < reinterpret_cast < qintptr > ( dn ) ;
d - > setData ( " xybrid-internal/x-sample-entry-move " , b ) ;
2019-06-16 14:40:09 -04:00
return d ;
}
bool SampleListModel : : canDropMimeData ( const QMimeData * data , Qt : : DropAction action [[maybe_unused]], int row [[maybe_unused]], int column [[maybe_unused]], const QModelIndex &parent [[maybe_unused]] ) const {
2019-06-25 14:57:43 -04:00
return data - > hasUrls ( ) | | data - > hasFormat ( " xybrid-internal/x-sample-entry-move " ) ;
2019-06-16 14:40:09 -04:00
}
2019-07-22 09:19:46 -04:00
2019-06-16 14:40:09 -04:00
bool SampleListModel : : dropMimeData ( const QMimeData * data , Qt : : DropAction action , int row [[maybe_unused]], int column [[maybe_unused]], const QModelIndex &parent [[maybe_unused]] ) {
2019-06-25 14:57:43 -04:00
if ( data - > hasUrls ( ) ) {
if ( action = = Qt : : IgnoreAction ) return true ; // can accept type
2019-06-25 16:46:44 -04:00
auto tdn = reinterpret_cast < DirectoryNode * > ( parent . internalPointer ( ) ) ;
if ( ! tdn ) tdn = root . get ( ) ;
if ( ! tdn - > isDirectory ( ) ) tdn = tdn - > parent ;
QString p = tdn - > path ( ) ;
2019-07-22 09:19:46 -04:00
2019-06-25 14:57:43 -04:00
QList < QUrl > urls = data - > urls ( ) ;
bool success = false ;
for ( auto u : urls ) {
if ( ! u . isLocalFile ( ) ) continue ;
auto smp = Sample : : fromFile ( u . toLocalFile ( ) ) ;
if ( smp ) { // valid sample returned
auto prj = window - > getProject ( ) ;
smp - > project = prj . get ( ) ;
prj - > samples [ smp - > uuid ] = smp ;
2019-06-25 16:46:44 -04:00
if ( ! p . isEmpty ( ) ) smp - > name = p % ' / ' % smp - > name ; // place in folder
2019-06-25 14:57:43 -04:00
success = true ;
}
2019-06-16 14:40:09 -04:00
}
2019-06-25 16:46:44 -04:00
if ( success ) {
auto view = static_cast < QTreeView * > ( QObject : : parent ( ) ) ;
view - > setCurrentIndex ( parent ) ; // focus wherever you dragged
refresh ( ) ;
}
2019-06-25 14:57:43 -04:00
return success ;
} else if ( data - > hasFormat ( " xybrid-internal/x-sample-entry-move " ) ) {
if ( action = = Qt : : IgnoreAction ) return true ; // can accept type
QByteArray b = data - > data ( " xybrid-internal/x-sample-entry-move " ) ;
QDataStream s ( & b , QIODevice : : ReadOnly ) ;
qintptr p ;
s > > p ;
if ( p ! = reinterpret_cast < qintptr > ( this ) ) return false ;
s > > p ;
auto dn = reinterpret_cast < DirectoryNode * > ( p ) ;
if ( ! dn ) return false ;
//auto ti = index(row, column, parent);
auto tdn = reinterpret_cast < DirectoryNode * > ( parent . internalPointer ( ) ) ;
if ( ! tdn ) tdn = root . get ( ) ;
if ( ! tdn - > isDirectory ( ) ) tdn = tdn - > parent ;
2019-06-25 16:21:01 -04:00
if ( tdn - > isChildOf ( dn ) ) return false ; // can't drag a folder within itself
2019-06-25 14:57:43 -04:00
dn - > parent = tdn ;
propagateSampleNames ( dn ) ;
refresh ( ) ;
return true ;
2019-06-16 14:40:09 -04:00
}
2019-06-25 14:57:43 -04:00
return false ;
2019-06-16 14:40:09 -04:00
}
//