File indexing completed on 2024-05-05 04:51:44

0001 /*
0002     SPDX-FileCopyrightText: 2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2009 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
0004     SPDX-FileCopyrightText: 2009-2011 Michal Malek <michalm@jabster.pl>
0005     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "k3bdataprojectmodel.h"
0011 
0012 #include "k3bdatadoc.h"
0013 #include "k3bdiritem.h"
0014 #include "k3bfileitem.h"
0015 #include "k3bisooptions.h"
0016 #include "k3bspecialdataitem.h"
0017 
0018 #include <KUrlMimeData>
0019 #include <KLocalizedString>
0020 #include <KIconEngine>
0021 
0022 #include <QDataStream>
0023 #include <QMimeData>
0024 #include <QFont>
0025 
0026 
0027 class K3b::DataProjectModel::Private
0028 {
0029 public:
0030     Private( DataProjectModel* parent )
0031         : project( 0 ),
0032           q( parent ) {
0033     }
0034 
0035     K3b::DataDoc* project;
0036 
0037     K3b::DataItem* getChild( K3b::DirItem* dir, int offset );
0038     int findChildIndex( K3b::DataItem* item );
0039     void _k_itemsAboutToBeInserted( K3b::DirItem* parent, int start, int end );
0040     void _k_itemsAboutToBeRemoved( K3b::DirItem* parent, int start, int end );
0041     void _k_itemsInserted( K3b::DirItem* parent, int start, int end );
0042     void _k_itemsRemoved( K3b::DirItem* parent, int start, int end );
0043     void _k_volumeIdChanged();
0044 
0045 private:
0046     DataProjectModel* q;
0047 
0048     bool m_removingItem;
0049 };
0050 
0051 
0052 
0053 K3b::DataItem* K3b::DataProjectModel::Private::getChild( K3b::DirItem* dir, int offset )
0054 {
0055     QList<K3b::DataItem*> const& children = dir->children();
0056     if ( offset >= 0 && offset < children.count() ) {
0057         return children[offset];
0058     }
0059 
0060     return 0;
0061 }
0062 
0063 
0064 int K3b::DataProjectModel::Private::findChildIndex( K3b::DataItem* item )
0065 {
0066     if ( !item )
0067         return 0;
0068     else if ( DirItem* dir = item->parent() )
0069         return dir->children().indexOf( item );
0070     else
0071         return 0;
0072 }
0073 
0074 
0075 void K3b::DataProjectModel::Private::_k_itemsAboutToBeInserted( K3b::DirItem* parent, int start, int end )
0076 {
0077         qDebug() << q->indexForItem( parent ) << start << end;
0078     q->beginInsertRows( q->indexForItem( parent ), start, end );
0079 }
0080 
0081 
0082 void K3b::DataProjectModel::Private::_k_itemsAboutToBeRemoved( K3b::DirItem* parent, int start, int end )
0083 {
0084     m_removingItem = true;
0085     qDebug() << q->indexForItem( parent ) << start << end;
0086     q->beginRemoveRows( q->indexForItem( parent ), start, end );
0087 }
0088 
0089 
0090 void K3b::DataProjectModel::Private::_k_itemsInserted( K3b::DirItem* /*parent*/, int /*start*/, int /*end*/ )
0091 {
0092     q->endInsertRows();
0093 }
0094 
0095 
0096 void K3b::DataProjectModel::Private::_k_itemsRemoved( K3b::DirItem* /*parent*/, int /*start*/, int /*end*/ )
0097 {
0098     if ( m_removingItem ) {
0099         q->endRemoveRows();
0100     }
0101 }
0102 
0103 
0104 void K3b::DataProjectModel::Private::_k_volumeIdChanged()
0105 {
0106     QModelIndex index = q->index( 0, 0 );
0107     emit q->dataChanged( index, index );
0108 }
0109 
0110 
0111 K3b::DataProjectModel::DataProjectModel( K3b::DataDoc* doc, QObject* parent )
0112     : QAbstractItemModel( parent ),
0113       d( new Private(this) )
0114 {
0115     d->project = doc;
0116 
0117     connect( doc, SIGNAL(itemsAboutToBeInserted(K3b::DirItem*,int,int)),
0118              this, SLOT(_k_itemsAboutToBeInserted(K3b::DirItem*,int,int)), Qt::DirectConnection );
0119     connect( doc, SIGNAL(itemsAboutToBeRemoved(K3b::DirItem*,int,int)),
0120              this, SLOT(_k_itemsAboutToBeRemoved(K3b::DirItem*,int,int)), Qt::DirectConnection );
0121     connect( doc, SIGNAL(itemsInserted(K3b::DirItem*,int,int)),
0122              this, SLOT(_k_itemsInserted(K3b::DirItem*,int,int)), Qt::DirectConnection );
0123     connect( doc, SIGNAL(itemsRemoved(K3b::DirItem*,int,int)),
0124              this, SLOT(_k_itemsRemoved(K3b::DirItem*,int,int)), Qt::DirectConnection );
0125     connect( doc, SIGNAL(volumeIdChanged()),
0126              this, SLOT(_k_volumeIdChanged()), Qt::DirectConnection );
0127 }
0128 
0129 
0130 K3b::DataProjectModel::~DataProjectModel()
0131 {
0132     delete d;
0133 }
0134 
0135 
0136 K3b::DataDoc* K3b::DataProjectModel::project() const
0137 {
0138     return d->project;
0139 }
0140 
0141 K3b::DataItem* K3b::DataProjectModel::itemForIndex( const QModelIndex& index ) const
0142 {
0143     if ( index.isValid() ) {
0144         Q_ASSERT( index.internalPointer() );
0145         return static_cast<K3b::DataItem*>( index.internalPointer() );
0146     }
0147     else {
0148         return 0;
0149     }
0150 }
0151 
0152 QModelIndex K3b::DataProjectModel::indexForItem( K3b::DataItem* item ) const
0153 {
0154     return createIndex( d->findChildIndex( item ), 0, item );
0155 }
0156 
0157 
0158 int K3b::DataProjectModel::columnCount( const QModelIndex& /*index*/ ) const
0159 {
0160     return NumColumns;
0161 }
0162 
0163 
0164 QVariant K3b::DataProjectModel::data( const QModelIndex& index, int role ) const
0165 {
0166     if ( DataItem* item = itemForIndex( index ) ) {
0167 
0168         if ( role == ItemTypeRole ) {
0169             if (item->isDir())
0170                 return DirItemType;
0171             else
0172                 return FileItemType;
0173         }
0174         else if ( role == CustomFlagsRole ) {
0175             if (item->isRemoveable())
0176                 return ItemIsRemovable;
0177             else
0178                 return 0;
0179         }
0180         else if ( role == Qt::StatusTipRole ) {
0181             if (item->isSymLink())
0182                 return i18nc( "Symlink target shown in status bar", "Link to %1", static_cast<FileItem*>( item )->linkDest() );
0183             else
0184                 return QVariant();
0185         }
0186 
0187         switch( index.column() ) {
0188         case FilenameColumn:
0189             if( role == Qt::DisplayRole ||
0190                 role == Qt::EditRole ||
0191                 role == SortRole ) {
0192                 return item->k3bName();
0193             }
0194             else if ( role == Qt::DecorationRole ) {
0195                 QString iconName;
0196                 if ( item->isDir() && item->parent() ) {
0197                     iconName = ( static_cast<K3b::DirItem*>( item )->depth() > 7 ? "folder-root" : "folder" );
0198                 }
0199                 else if ( item->isDir() ) {
0200                     iconName = "media-optical-data";
0201                 }
0202                 else {
0203                     iconName = item->mimeType().iconName();
0204                 }
0205 
0206                 if( item->isSymLink() )
0207                     return QIcon( new KIconEngine( iconName, nullptr, QStringList() << "emblem-symbolic-link" ) );
0208                 else
0209                     return QIcon::fromTheme( iconName );
0210             }
0211             else if( role == Qt::FontRole && item->isSymLink() ) {
0212                 QFont font;
0213                 font.setItalic( true );
0214                 return font;
0215             }
0216             break;
0217 
0218         case TypeColumn:
0219             if( role == Qt::DisplayRole ||
0220                 role == SortRole ) {
0221                 if ( item->isSpecialFile() ) {
0222                     return static_cast<K3b::SpecialDataItem*>( item )->specialType();
0223                 }
0224                 else {
0225                     return item->mimeType().comment();
0226                 }
0227             }
0228             break;
0229 
0230         case SizeColumn:
0231             if( role == Qt::DisplayRole ) {
0232                 return KIO::convertSize( item->size() );
0233             }
0234             else if ( role == SortRole ) {
0235                 return item->size();
0236             }
0237             break;
0238         }
0239     }
0240 
0241     return QVariant();
0242 }
0243 
0244 
0245 QVariant K3b::DataProjectModel::headerData( int section, Qt::Orientation orientation, int role ) const
0246 {
0247     Q_UNUSED( orientation );
0248 
0249     if ( role == Qt::DisplayRole ) {
0250         switch( section ) {
0251         case FilenameColumn:
0252             return i18nc( "file name", "Name" );
0253         case TypeColumn:
0254             return i18nc( "file type", "Type" );
0255         case SizeColumn:
0256             return i18nc( "file size", "Size" );
0257         }
0258     }
0259 
0260     return QVariant();
0261 }
0262 
0263 
0264 Qt::ItemFlags K3b::DataProjectModel::flags( const QModelIndex& index ) const
0265 {
0266     if ( index.isValid() ) {
0267         Qt::ItemFlags f = Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsDropEnabled;
0268         if ( index.column() == FilenameColumn ) {
0269             f |= Qt::ItemIsEditable;
0270         }
0271         if ( itemForIndex( index ) != d->project->root() ) {
0272             f |= Qt::ItemIsDragEnabled;
0273         }
0274 
0275         return f;
0276     }
0277     else {
0278         return QAbstractItemModel::flags( index )|Qt::ItemIsDropEnabled;
0279     }
0280 }
0281 
0282 
0283 QModelIndex K3b::DataProjectModel::index( int row, int column, const QModelIndex& parent ) const
0284 {
0285     if ( !hasIndex( row, column, parent ) ) {
0286         return QModelIndex();
0287     }
0288     else if ( parent.isValid() ) {
0289         K3b::DirItem* dir = itemForIndex( parent )->getDirItem();
0290         K3b::DataItem* child = d->getChild( dir, row );
0291         if ( child && parent.column() == 0 ) {
0292             return createIndex( row, column, child );
0293         }
0294         else {
0295             return QModelIndex();
0296         }
0297     }
0298     else {
0299         // doc root item
0300         return createIndex( row, column, d->project->root() );
0301     }
0302 }
0303 
0304 
0305 QModelIndex K3b::DataProjectModel::parent( const QModelIndex& index ) const
0306 {
0307     //qDebug() << index;
0308     if( K3b::DataItem* item = itemForIndex( index ) ) {
0309         if( K3b::DirItem* dir = item->parent() ) {
0310             return createIndex( d->findChildIndex( dir ), 0, dir );
0311         }
0312     }
0313     return QModelIndex();
0314 }
0315 
0316 
0317 int K3b::DataProjectModel::rowCount( const QModelIndex& parent ) const
0318 {
0319     if ( parent.isValid() ) {
0320         K3b::DataItem* item = itemForIndex( parent );
0321         K3b::DirItem* dir = dynamic_cast<K3b::DirItem*>( item );
0322         if ( dir != 0 && parent.column() == 0 ) {
0323             return( dir->children().count() );
0324         }
0325         else {
0326             return 0;
0327         }
0328     }
0329     else {
0330         return 1;
0331     }
0332 }
0333 
0334 
0335 bool K3b::DataProjectModel::setData( const QModelIndex& index, const QVariant& value, int role )
0336 {
0337     if ( index.isValid() ) {
0338         K3b::DataItem* item = itemForIndex( index );
0339         if ( role == Qt::EditRole ) {
0340             if ( index.column() == 0 ) {
0341                 item->setK3bName( value.toString() );
0342                 emit dataChanged( index, index );
0343                 return true;
0344             }
0345         }
0346     }
0347 
0348     return false;
0349 }
0350 
0351 
0352 QMimeData* K3b::DataProjectModel::mimeData( const QModelIndexList& indexes ) const
0353 {
0354     QMimeData* mime = new QMimeData();
0355 
0356     QSet<K3b::DataItem*> items;
0357     QList<QUrl> urls;
0358     foreach( const QModelIndex& index, indexes ) {
0359         K3b::DataItem* item = itemForIndex( index );
0360         items << item;
0361 
0362         QUrl url = QUrl::fromLocalFile( item->localPath() );
0363         if ( item->isFile() && !urls.contains(url) ) {
0364             urls << url;
0365         }
0366     }
0367     mime->setUrls(urls);
0368 
0369     // the easy road: encode the pointers
0370     QByteArray itemData;
0371     QDataStream itemDataStream( &itemData, QIODevice::WriteOnly );
0372     foreach( K3b::DataItem* item, items ) {
0373         itemDataStream << ( qint64 )item;
0374     }
0375     mime->setData( "application/x-k3bdataitem", itemData );
0376 
0377     return mime;
0378 }
0379 
0380 
0381 Qt::DropActions K3b::DataProjectModel::supportedDropActions() const
0382 {
0383     return Qt::CopyAction | Qt::MoveAction;
0384 }
0385 
0386 
0387 QStringList K3b::DataProjectModel::mimeTypes() const
0388 {
0389     QStringList s = KUrlMimeData::mimeDataTypes();
0390     s += QString::fromLatin1( "application/x-k3bdataitem" );
0391     return s;
0392 }
0393 
0394 
0395 bool K3b::DataProjectModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
0396 {
0397     qDebug();
0398 
0399     // no need to handle the row as item order is not important (the model just forces us to use them above)
0400     Q_UNUSED( row );
0401     Q_UNUSED( column );
0402 
0403     if (action == Qt::IgnoreAction)
0404         return true;
0405 
0406     // determine the target dir:
0407     // - drop ontp an item -> get the item's dir (i.e. itself or its parent)
0408     // - drop onto the viewport -> the project's root
0409     // --------------------------------------------------------------
0410     K3b::DirItem* dir = d->project->root();
0411     if ( parent.isValid() ) {
0412         dir = itemForIndex( parent )->getDirItem();
0413     }
0414 
0415     if ( data->hasFormat( "application/x-k3bdataitem" ) ) {
0416         if( action == Qt::MoveAction )
0417             return false;
0418 
0419         qDebug() << "data item drop";
0420 
0421         QByteArray itemData = data->data( "application/x-k3bdataitem" );
0422         QDataStream itemDataStream( itemData );
0423         QList<K3b::DataItem*> items;
0424         while ( !itemDataStream.atEnd() ) {
0425             qint64 p;
0426             itemDataStream >> p;
0427             items << ( K3b::DataItem* )p;
0428         }
0429         // always move the items, no copy from within the views
0430         emit moveItemsRequested( items, dir );
0431         return true;
0432     }
0433     else if ( data->hasUrls() ) {
0434         qDebug() << "url list drop";
0435         QList<QUrl> urls = KUrlMimeData::urlsFromMimeData( data );
0436         emit addUrlsRequested( urls, dir );
0437         return true;
0438     }
0439     else {
0440         return false;
0441     }
0442 }
0443 
0444 
0445 bool K3b::DataProjectModel::removeRows( int row, int count, const QModelIndex& parent)
0446 {
0447     DirItem* dirItem = dynamic_cast<DirItem*>( itemForIndex( parent ) );
0448     if( dirItem && row >= 0 && count > 0 ) {
0449         // remove the indexes from the project
0450         d->project->removeItems( dirItem, row, count );
0451         return true;
0452     }
0453     else {
0454         return false;
0455     }
0456 }
0457 
0458 
0459 QModelIndex K3b::DataProjectModel::buddy( const QModelIndex& index ) const
0460 {
0461     if( index.isValid() && index.column() != FilenameColumn)
0462         return DataProjectModel::index( index.row(), FilenameColumn, index.parent() );
0463     else
0464         return index;
0465 }
0466 
0467 #include "moc_k3bdataprojectmodel.cpp"