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