File indexing completed on 2024-05-05 04:47:21
0001 /**************************************************************************************** 0002 * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@kde.org> * 0003 * Copyright (c) 2008 Ian Monroe <imonroe@kde.org> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify it under * 0006 * the terms of the GNU General Public License as published by the Free Software * 0007 * Foundation; either version 2 of the License, or (at your option) any later * 0008 * version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License along with * 0015 * this program. If not, see <http://www.gnu.org/licenses/>. * 0016 ****************************************************************************************/ 0017 0018 #include "BookmarkModel.h" 0019 0020 #include "AmarokMimeData.h" 0021 #include "AmarokUrl.h" 0022 #include "BookmarkGroup.h" 0023 #include "AmarokUrlHandler.h" 0024 #include "core/support/Debug.h" 0025 #include <core-impl/storage/StorageManager.h> 0026 #include <core/storage/SqlStorage.h> 0027 0028 #include <QIcon> 0029 0030 #include <QAbstractListModel> 0031 #include <QListIterator> 0032 0033 #include <typeinfo> 0034 0035 static const int BOOKMARK_DB_VERSION = 4; 0036 static const QString key(QStringLiteral("AMAROK_BOOKMARKS")); 0037 0038 BookmarkModel * BookmarkModel::s_instance = nullptr; 0039 0040 BookmarkModel * BookmarkModel::instance() 0041 { 0042 if ( s_instance == nullptr ) 0043 s_instance = new BookmarkModel(); 0044 0045 return s_instance; 0046 } 0047 0048 0049 BookmarkModel::BookmarkModel() 0050 : QAbstractItemModel() 0051 { 0052 checkTables(); 0053 0054 m_root = BookmarkGroupPtr( new BookmarkGroup( QStringLiteral("root"), BookmarkGroupPtr() ) ); 0055 } 0056 0057 0058 BookmarkModel::~BookmarkModel() 0059 { 0060 } 0061 0062 QVariant 0063 BookmarkModel::data( const QModelIndex & index, int role ) const 0064 { 0065 0066 if ( !index.isValid() ) 0067 return QVariant(); 0068 0069 BookmarkViewItemPtr item = m_viewItems.value( index.internalId() ); 0070 0071 if( role == 0xf00d ) 0072 return QVariant::fromValue( item ); 0073 else if( role == Qt::DisplayRole || role == Qt::EditRole ) 0074 { 0075 switch( index.column() ) 0076 { 0077 case Name: 0078 return item->name(); 0079 break; 0080 case Command: 0081 { 0082 AmarokUrl * url = dynamic_cast<AmarokUrl *>( item.data() ); 0083 if ( url ) 0084 return url->prettyCommand(); 0085 else 0086 return i18n( "Group" ); 0087 break; 0088 } 0089 case Url: 0090 { 0091 AmarokUrl * url = dynamic_cast<AmarokUrl *>( item.data() ); 0092 if ( url ) 0093 return url->url(); 0094 else 0095 return QString(); 0096 break; 0097 } 0098 case Description: 0099 { 0100 return item->description(); 0101 break; 0102 } 0103 default: 0104 break; 0105 } 0106 } 0107 else if( role == Qt::DecorationRole ) 0108 { 0109 if( index.column() == Name ) 0110 { 0111 if ( BookmarkGroupPtr::dynamicCast( item ) ) 0112 return QVariant( QIcon::fromTheme( QStringLiteral("folder-bookmark") ) ); 0113 else if ( auto url = AmarokUrlPtr::dynamicCast( item ) ) 0114 return The::amarokUrlHandler()->iconForCommand( url->command() ); 0115 } 0116 } 0117 0118 return QVariant(); 0119 } 0120 0121 0122 QModelIndex 0123 BookmarkModel::createIndex( int row, int column, const BookmarkViewItemPtr &item ) const 0124 { 0125 quint32 index = qHash( item.data() ); 0126 if( !m_viewItems.contains( index ) ) 0127 m_viewItems[ index ] = item; 0128 QModelIndex ret = QAbstractItemModel::createIndex( row, column, index ); 0129 // debug() << "created " << ret << " with " << ret.parent().internalId(); 0130 return ret; 0131 } 0132 0133 QModelIndex 0134 BookmarkModel::index( int row, int column, const QModelIndex & parent ) const 0135 { 0136 //DEBUG_BLOCK 0137 0138 //debug() << "row: " << row << ", column: " <<column; 0139 if (!hasIndex(row, column, parent)) 0140 return QModelIndex(); 0141 0142 if ( !parent.isValid() ) 0143 { 0144 0145 int noOfGroups = m_root->childGroups().count(); 0146 if ( row < noOfGroups ) 0147 { 0148 return createIndex( row, column, BookmarkViewItemPtr::staticCast( m_root->childGroups().at( row ) ) ); 0149 } 0150 else 0151 { 0152 //debug() << "Root playlist"; 0153 return createIndex( row, column, BookmarkViewItemPtr::staticCast( m_root->childBookmarks().at( row - noOfGroups ) ) ); 0154 } 0155 } 0156 else 0157 { 0158 BookmarkGroupPtr playlistGroup = BookmarkGroupPtr::staticCast( m_viewItems.value( parent.internalId() ) ); 0159 int noOfGroups = playlistGroup->childGroups().count(); 0160 0161 if ( row < noOfGroups ) 0162 { 0163 return createIndex( row, column, BookmarkViewItemPtr::staticCast( playlistGroup->childGroups().at(row) ) ); 0164 } 0165 else 0166 { 0167 return createIndex( row, column, BookmarkViewItemPtr::staticCast( playlistGroup->childBookmarks().at(row - noOfGroups) ) ); 0168 } 0169 } 0170 } 0171 0172 QModelIndex 0173 BookmarkModel::parent( const QModelIndex & index ) const 0174 { 0175 //DEBUG_BLOCK 0176 0177 if (!index.isValid()) 0178 return QModelIndex(); 0179 BookmarkViewItemPtr item = m_viewItems.value( index.internalId() ); 0180 0181 BookmarkGroupPtr parent = item->parent(); 0182 0183 //debug() << "parent: " << parent; 0184 0185 if ( parent && parent->parent() ) 0186 { 0187 int row = parent->parent()->childGroups().indexOf( parent ); 0188 return createIndex( row , 0, BookmarkViewItemPtr::staticCast( parent ) ); 0189 } 0190 else { 0191 return QModelIndex(); 0192 } 0193 } 0194 0195 int 0196 BookmarkModel::rowCount( const QModelIndex & parent ) const 0197 { 0198 //DEBUG_BLOCK 0199 0200 if ( parent.column() > 0 ) { 0201 // debug() << "bad column"; 0202 return 0; 0203 } 0204 0205 if (!parent.isValid()) { 0206 0207 //debug() << "top level item has" << m_root->childCount(); 0208 0209 return m_root->childCount(); 0210 0211 } 0212 BookmarkViewItemPtr item = m_viewItems.value( parent.internalId() ); 0213 //debug() << "row: " << parent.row(); 0214 //debug() << "address: " << item; 0215 //debug() << "count: " << item->childCount(); 0216 0217 return item->childCount(); 0218 } 0219 0220 int 0221 BookmarkModel::columnCount(const QModelIndex & /*parent*/) const 0222 { 0223 //name, command, url, description 0224 return 4; 0225 } 0226 0227 0228 Qt::ItemFlags 0229 BookmarkModel::flags( const QModelIndex & index ) const 0230 { 0231 0232 if (!index.isValid()) 0233 return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; 0234 BookmarkViewItemPtr item = BookmarkViewItemPtr::staticCast( m_viewItems.value( index.internalId() ) ); 0235 0236 if ( BookmarkGroupPtr::dynamicCast( item ) ) 0237 { 0238 if ( index.column() != Command ) 0239 return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; 0240 else 0241 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; 0242 } 0243 else 0244 { 0245 if ( index.column() != Command ) 0246 return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; 0247 else 0248 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; 0249 } 0250 } 0251 0252 QVariant 0253 BookmarkModel::headerData(int section, Qt::Orientation orientation, int role) const 0254 { 0255 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0256 switch( section ) 0257 { 0258 case Name: return i18n("Name"); 0259 case Command: return i18n("Type"); 0260 case Url: return i18n("URL"); 0261 case Description: return i18n("Description"); 0262 default: return QVariant(); 0263 } 0264 } 0265 0266 return QVariant(); 0267 } 0268 0269 bool BookmarkModel::setData( const QModelIndex & index, const QVariant & value, int role ) 0270 { 0271 if (role != Qt::EditRole) 0272 return false; 0273 if ( index.column() == Command ) 0274 return false; 0275 0276 BookmarkViewItemPtr item = m_viewItems.value( index.internalId() ); 0277 0278 switch( index.column() ) 0279 { 0280 case Name: 0281 item->rename( value.toString() ); 0282 Q_EMIT dataChanged( index, index ); 0283 break; 0284 case Url: 0285 { 0286 AmarokUrl * url = dynamic_cast<AmarokUrl *>( item.data() ); 0287 if ( url ) 0288 { 0289 debug() << "writing " << value.toString() << " as new url!"; 0290 url->initFromString( value.toString() ); 0291 url->saveToDb(); 0292 0293 Q_EMIT dataChanged( index, index ); 0294 } 0295 break; 0296 } 0297 case Description: 0298 { 0299 item->setDescription( value.toString() ); 0300 0301 AmarokUrl * url = dynamic_cast<AmarokUrl *>( item.data() ); 0302 if ( url ) 0303 { 0304 url->saveToDb(); 0305 Q_EMIT dataChanged( index, index ); 0306 } 0307 0308 break; 0309 } 0310 } 0311 return true; 0312 0313 } 0314 0315 QStringList 0316 BookmarkModel::mimeTypes() const 0317 { 0318 DEBUG_BLOCK 0319 QStringList ret; 0320 ret << AmarokMimeData::BOOKMARKGROUP_MIME; 0321 ret << AmarokMimeData::AMAROKURL_MIME; 0322 return ret; 0323 } 0324 0325 QMimeData* 0326 BookmarkModel::mimeData( const QModelIndexList &indexes ) const 0327 { 0328 DEBUG_BLOCK 0329 AmarokMimeData* mime = new AmarokMimeData(); 0330 0331 BookmarkGroupList groups; 0332 BookmarkList bookmarks; 0333 0334 foreach( const QModelIndex &index, indexes ) { 0335 0336 BookmarkViewItemPtr item = m_viewItems.value( index.internalId() ); 0337 0338 if ( auto group = BookmarkGroupPtr::dynamicCast( item ) ) 0339 groups << group; 0340 else if( auto bookmark = AmarokUrlPtr::dynamicCast( item ) ) 0341 bookmarks << bookmark; 0342 } 0343 0344 debug() << "adding " << groups.count() << " groups and " << bookmarks.count() << " bookmarks"; 0345 0346 mime->setBookmarkGroups( groups ); 0347 mime->setBookmarks( bookmarks ); 0348 0349 return mime; 0350 } 0351 0352 0353 bool 0354 BookmarkModel::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) //reimplemented 0355 { 0356 DEBUG_BLOCK 0357 0358 Q_UNUSED( column ); 0359 Q_UNUSED( row ); 0360 if( action == Qt::IgnoreAction ) 0361 return true; 0362 0363 BookmarkGroupPtr parentGroup; 0364 if ( !parent.isValid() ) 0365 { 0366 parentGroup = m_root; 0367 } 0368 else 0369 { 0370 parentGroup = BookmarkGroupPtr::staticCast( m_viewItems.value( parent.internalId() ) ); 0371 } 0372 0373 if( data->hasFormat( AmarokMimeData::BOOKMARKGROUP_MIME ) ) 0374 { 0375 debug() << "Found playlist group mime type"; 0376 0377 const AmarokMimeData* bookmarkGroupDrag = dynamic_cast<const AmarokMimeData*>( data ); 0378 if( bookmarkGroupDrag ) 0379 { 0380 0381 BookmarkGroupList groups = bookmarkGroupDrag->bookmarkGroups(); 0382 0383 foreach( BookmarkGroupPtr group, groups ) { 0384 group->reparent( parentGroup ); 0385 } 0386 0387 reloadFromDb(); 0388 0389 return true; 0390 } 0391 } 0392 else if( data->hasFormat( AmarokMimeData::AMAROKURL_MIME ) ) 0393 { 0394 debug() << "Found amarokurl mime type"; 0395 0396 const AmarokMimeData* dragList = dynamic_cast<const AmarokMimeData*>( data ); 0397 if( dragList ) 0398 { 0399 BookmarkList bookmarks = dragList->bookmarks(); 0400 0401 foreach( AmarokUrlPtr bookmarkPtr, bookmarks ) { 0402 bookmarkPtr->reparent( parentGroup ); 0403 } 0404 0405 reloadFromDb(); 0406 0407 return true; 0408 } 0409 } 0410 0411 return false; 0412 } 0413 0414 void BookmarkModel::createTables() 0415 { 0416 DEBUG_BLOCK 0417 0418 auto sqlStorage = StorageManager::instance()->sqlStorage(); 0419 if( !sqlStorage ) 0420 return; 0421 0422 sqlStorage->query( QString( "CREATE TABLE bookmark_groups (" 0423 " id " + sqlStorage->idType() + 0424 ", parent_id INTEGER" 0425 ", name " + sqlStorage->textColumnType() + 0426 ", description " + sqlStorage->textColumnType() + 0427 ", custom " + sqlStorage->textColumnType() + " ) ENGINE = MyISAM;" ) ); 0428 0429 sqlStorage->query( QString( "CREATE TABLE bookmarks (" 0430 " id " + sqlStorage->idType() + 0431 ", parent_id INTEGER" 0432 ", name " + sqlStorage->textColumnType() + 0433 ", url " + sqlStorage->exactTextColumnType() + 0434 ", description " + sqlStorage->exactTextColumnType() + 0435 ", custom " + sqlStorage->textColumnType() + " ) ENGINE = MyISAM;" ) ); 0436 0437 } 0438 0439 void BookmarkModel::deleteTables() 0440 { 0441 0442 DEBUG_BLOCK 0443 0444 auto sqlStorage = StorageManager::instance()->sqlStorage(); 0445 if( !sqlStorage ) 0446 return; 0447 0448 sqlStorage->query( QStringLiteral("DROP TABLE IF EXISTS bookmark_groups;") ); 0449 sqlStorage->query( QStringLiteral("DROP TABLE IF EXISTS bookmarks;") ); 0450 0451 } 0452 0453 void BookmarkModel::checkTables() 0454 { 0455 0456 DEBUG_BLOCK 0457 0458 auto sqlStorage = StorageManager::instance()->sqlStorage(); 0459 if( !sqlStorage ) 0460 return; 0461 0462 QStringList values = sqlStorage->query( QStringLiteral("SELECT version FROM admin WHERE component = '%1';").arg(sqlStorage->escape( key ) ) ); 0463 0464 //also check if the db version is correct but the table is simply missing... can happen due to a bug in 2.2.0 beta1 and beta2 0465 QStringList values2 = sqlStorage->query( QStringLiteral("show tables like 'bookmarks';")); 0466 0467 if( values.isEmpty() || values2.isEmpty() ) 0468 { 0469 debug() << "creating Playlist Tables"; 0470 createTables(); 0471 sqlStorage->query( "INSERT INTO admin(component,version) " 0472 "VALUES('" + key + "'," + QString::number( BOOKMARK_DB_VERSION ) + ");" ); 0473 } 0474 else if ( values.at( 0 ).toInt() < 4 ) 0475 { 0476 upgradeTables( values.at( 0 ).toInt() ); 0477 sqlStorage->query( "UPDATE admin SET version=" + QString::number( BOOKMARK_DB_VERSION ) + " WHERE component=" + key + ';' ); 0478 } 0479 } 0480 0481 void 0482 BookmarkModel::reloadFromDb() 0483 { 0484 DEBUG_BLOCK; 0485 0486 beginResetModel(); 0487 m_root->clear(); 0488 endResetModel(); 0489 0490 } 0491 0492 void 0493 BookmarkModel::editBookmark( int id ) 0494 { 0495 0496 //for now, assume that the newly added playlist is in the top level: 0497 int row = m_root->childGroups().count() - 1; 0498 foreach ( AmarokUrlPtr bookmark, m_root->childBookmarks() ) { 0499 row++; 0500 if ( bookmark->id() == id ) { 0501 Q_EMIT editIndex( createIndex( row , 0, BookmarkViewItemPtr::staticCast( bookmark ) ) ); 0502 } 0503 } 0504 } 0505 0506 void 0507 BookmarkModel::createNewGroup() 0508 { 0509 DEBUG_BLOCK 0510 0511 BookmarkGroup * group = new BookmarkGroup( i18n("New Group"), m_root ); 0512 group->save(); 0513 int id = group->id(); 0514 delete group; 0515 0516 reloadFromDb(); 0517 0518 int row = 0; 0519 foreach ( BookmarkGroupPtr childGroup, m_root->childGroups() ) { 0520 if ( childGroup->id() == id ) 0521 { 0522 debug() << "emitting edit for " << childGroup->name() << " id " << childGroup->id() << " in row " << row; 0523 Q_EMIT editIndex( createIndex( row , 0, BookmarkViewItemPtr::staticCast( childGroup ) ) ); 0524 } 0525 row++; 0526 } 0527 0528 } 0529 0530 void 0531 BookmarkModel::createNewBookmark() 0532 { 0533 DEBUG_BLOCK 0534 AmarokUrl * url = new AmarokUrl(); 0535 url->reparent( m_root ); 0536 url->setName( i18n( "New Bookmark" ) ); 0537 url->setCommand( i18n( "none" ) ); 0538 url->saveToDb(); 0539 int id = url->id(); 0540 delete url; 0541 0542 reloadFromDb(); 0543 debug() << "id of new bookmark: " << id; 0544 int row = m_root->childGroups().count(); 0545 foreach ( AmarokUrlPtr childBookmark, m_root->childBookmarks() ) { 0546 debug() << id << " == " << childBookmark->id() << " ? "; 0547 if ( childBookmark->id() == id ) 0548 { 0549 debug() << "emitting edit for " << childBookmark->name() << " id " << childBookmark->id() << " in row " << row; 0550 Q_EMIT editIndex( createIndex( row , 0, BookmarkViewItemPtr::staticCast( childBookmark ) ) ); 0551 } 0552 row++; 0553 } 0554 0555 } 0556 0557 void 0558 BookmarkModel::setBookmarkArg( const QString &name, const QString &key, const QString &value ) 0559 { 0560 if( setBookmarkArgRecursively( m_root, name, key, value ) ) 0561 { 0562 reloadFromDb(); 0563 The::amarokUrlHandler()->updateTimecodes(); 0564 } 0565 else 0566 { 0567 warning() << "Cannot set argument" << key << "of the bookmark" << name 0568 << "to value" << value << "- bookmark not found."; 0569 } 0570 } 0571 0572 void 0573 BookmarkModel::deleteBookmark( const QString& name ) 0574 { 0575 DEBUG_BLOCK 0576 0577 debug() << "Name: " << name; 0578 0579 if( deleteBookmarkRecursively( m_root, name ) ) 0580 { 0581 debug() << "Deleted!"; 0582 reloadFromDb(); 0583 The::amarokUrlHandler()->updateTimecodes(); 0584 } 0585 else 0586 debug() << "No such bookmark found!"; 0587 } 0588 0589 void 0590 BookmarkModel::renameBookmark( const QString& oldName, const QString& newName ) 0591 { 0592 DEBUG_BLOCK 0593 0594 debug() << "OldName: " << oldName << " NewName: " << newName; 0595 0596 if( renameBookmarkRecursively( m_root, oldName, newName ) ) 0597 { 0598 debug() << "Renamed!!"; 0599 reloadFromDb(); 0600 const QString* name = &newName; 0601 The::amarokUrlHandler()->updateTimecodes( name ); 0602 } 0603 else 0604 debug() << "No such bookmark found!"; 0605 } 0606 0607 bool 0608 BookmarkModel::setBookmarkArgRecursively( BookmarkGroupPtr group, const QString& name, const QString& key, const QString &value ) 0609 { 0610 foreach( AmarokUrlPtr item, group->childBookmarks() ) 0611 { 0612 if( item->name() == name ) 0613 { 0614 item->setArg( key, value ); 0615 item->saveToDb(); 0616 return true; 0617 } 0618 } 0619 0620 //if not found, recurse through child groups 0621 foreach( BookmarkGroupPtr childGroup, group->childGroups() ) 0622 { 0623 if( setBookmarkArgRecursively( childGroup, name, key, value ) ) 0624 return true; 0625 } 0626 0627 return false; 0628 0629 } 0630 0631 bool 0632 BookmarkModel::deleteBookmarkRecursively( BookmarkGroupPtr group, const QString& name ) 0633 { 0634 foreach( AmarokUrlPtr item, group->childBookmarks() ) 0635 { 0636 debug() << "item->name(): " << item->name(); 0637 if( item->name() == name ) 0638 { 0639 debug() << "Deleting Bookmark: " << name; 0640 item->removeFromDb(); 0641 return true; 0642 } 0643 } 0644 0645 //if not found, recurse through child groups 0646 foreach( BookmarkGroupPtr childGroup, group->childGroups() ) 0647 { 0648 if( deleteBookmarkRecursively( childGroup, name ) ) 0649 return true; 0650 } 0651 0652 return false; 0653 0654 } 0655 0656 bool 0657 BookmarkModel::renameBookmarkRecursively( BookmarkGroupPtr group, const QString& oldName, const QString& newName ) 0658 { 0659 foreach( AmarokUrlPtr item, group->childBookmarks() ) 0660 { 0661 debug() << "item->name(): " << item->name(); 0662 if( item->name() == oldName) 0663 { 0664 debug() << "Renaming Bookmark: " << oldName; 0665 item->rename(newName); 0666 return true; 0667 } 0668 } 0669 0670 //if not found, recurse through child groups 0671 foreach( BookmarkGroupPtr childGroup, group->childGroups() ) 0672 { 0673 if( renameBookmarkRecursively( childGroup, oldName, newName ) ) 0674 return true; 0675 } 0676 0677 return false; 0678 0679 } 0680 0681 void BookmarkModel::upgradeTables( int from ) 0682 { 0683 auto sqlStorage = StorageManager::instance()->sqlStorage(); 0684 if( !sqlStorage ) 0685 return; 0686 0687 if ( from == 2 ) { 0688 sqlStorage->query( "ALTER TABLE bookmarks ADD custom " + sqlStorage->textColumnType() + ';' ); 0689 } 0690 0691 sqlStorage->query( "ALTER TABLE bookmark_groups ADD custom " + sqlStorage->textColumnType() + ';' ); 0692 } 0693 0694 0695 0696 0697