]> git.sesse.net Git - vlc/commitdiff
Qt/ML: ML Model and Item
authorSrikanth Raju <srikiraju@gmail.com>
Wed, 29 Dec 2010 14:17:46 +0000 (19:47 +0530)
committerSrikanth Raju <srikiraju@gmail.com>
Sun, 9 Jan 2011 18:34:55 +0000 (00:04 +0530)
ML Model/Item which queries the module and provides a QAbstractItemModel Interface

modules/gui/qt4/Modules.am
modules/gui/qt4/components/playlist/ml_item.cpp [new file with mode: 0644]
modules/gui/qt4/components/playlist/ml_item.hpp [new file with mode: 0644]
modules/gui/qt4/components/playlist/ml_model.cpp [new file with mode: 0644]
modules/gui/qt4/components/playlist/ml_model.hpp [new file with mode: 0644]
modules/gui/qt4/components/playlist/sorting.h

index 5ed7332de2454599029f0bc85b1642b5de94c827..cb7d4e4e20fda4f2960ad698f4d51742c96edac4 100644 (file)
@@ -60,6 +60,8 @@ nodist_SOURCES_qt4 = \
                components/epg/EPGWidget.moc.cpp \
                components/playlist/views.moc.cpp \
                components/playlist/vlc_model.moc.cpp \
+               components/playlist/ml_item.moc.cpp \
+               components/playlist/ml_model.moc.cpp \
                components/playlist/playlist_model.moc.cpp \
                components/playlist/playlist.moc.cpp \
                components/playlist/standardpanel.moc.cpp \
@@ -276,6 +278,8 @@ SOURCES_qt4 =       qt4.cpp \
                components/epg/EPGView.cpp \
                components/epg/EPGWidget.cpp \
                components/playlist/views.cpp \
+               components/playlist/ml_item.cpp \
+               components/playlist/ml_model.cpp \
                components/playlist/vlc_model.cpp \
                components/playlist/playlist_model.cpp \
                components/playlist/playlist_item.cpp \
@@ -343,6 +347,8 @@ noinst_HEADERS = \
        components/epg/EPGView.hpp \
        components/epg/EPGWidget.hpp \
        components/playlist/views.hpp \
+    components/playlist/ml_item.hpp \
+    components/playlist/ml_model.hpp \
        components/playlist/vlc_model.hpp \
        components/playlist/playlist_model.hpp \
        components/playlist/playlist_item.hpp \
diff --git a/modules/gui/qt4/components/playlist/ml_item.cpp b/modules/gui/qt4/components/playlist/ml_item.cpp
new file mode 100644 (file)
index 0000000..aa0afaa
--- /dev/null
@@ -0,0 +1,314 @@
+/*****************************************************************************
+ * ml_item.cpp: the media library's result item
+ *****************************************************************************
+ * Copyright (C) 2008-2011 the VideoLAN Team and AUTHORS
+ * $Id$
+ *
+ * Authors: Antoine Lejeune <phytos@videolan.org>
+ *          Jean-Philippe André <jpeg@videolan.org>
+ *          Rémi Duraffort <ivoire@videolan.org>
+ *          Adrien Maglo <magsoft@videolan.org>
+ *          Srikanth Raju <srikiraju#gmail#com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef MEDIA_LIBRARY
+
+#include <QDateTime>
+#include <QUrl>
+#include <QFileInfo>
+#include "ml_item.hpp"
+#include <assert.h>
+
+
+/**
+ * @brief Compare the attribute 'meta' between medias a and b.
+ * @param a
+ * @param b
+ * @param meta
+ * @note If a->meta < b->meta, return -1
+ *       If a->meta == b->meta, return 0
+ *       If a->meta > b->meta, return +1
+ * @note If a->meta == NULL and b->meta != NULL (strings), then b>a
+ */
+static int compareMeta( const ml_media_t *a, const ml_media_t *b,
+                        ml_select_e meta )
+{
+#   define scomp(c) ((a->c&&b->c&&*a->c&&*b->c) ? strcasecmp(a->c,b->c) : \
+                     (a->c&&*a->c?-1:(b->c&&*b->c?1:0)))
+#   define icomp(c) (a->c<b->c?-1:(a->c==b->c?0:1))
+    switch( meta )
+    {
+        case ML_ALBUM: return scomp( psz_album );
+        case ML_ALBUM_ID: return icomp( i_album_id );
+        //case ML_ARTIST: return scomp( psz_artist );
+        //case ML_ARTIST_ID: return icomp( i_artist_id );
+        case ML_COVER: return scomp( psz_cover );
+        case ML_DURATION: return icomp( i_duration );
+        case ML_EXTRA: return scomp( psz_extra );
+        case ML_GENRE: return scomp( psz_genre );
+        case ML_ID: return icomp( i_id );
+        case ML_LAST_PLAYED: return icomp( i_last_played );
+        case ML_ORIGINAL_TITLE: return scomp( psz_orig_title );
+        case ML_PLAYED_COUNT: return icomp( i_played_count );
+        // case ML_ROLE: return 0;
+        case ML_SCORE: return icomp( i_score );
+        case ML_TITLE: return scomp( psz_title );
+        case ML_TRACK_NUMBER: return icomp( i_track_number );
+        case ML_TYPE: return icomp( i_type );
+        case ML_URI: return scomp( psz_uri );
+        case ML_VOTE: return icomp( i_vote );
+        case ML_YEAR: return icomp( i_year );
+        default: return 0;
+    }
+#   undef scomp
+#   undef icomp
+}
+
+
+MLItem::MLItem( const MLModel *p_model,
+                            intf_thread_t* _p_intf,
+                            ml_media_t *p_media,
+                            MLItem *p_parent )
+        : model( p_model ), children(), parentItem( p_parent ), p_intf( _p_intf )
+{
+    if( p_media )
+        ml_gc_incref( p_media );
+    this->media = p_media;
+    p_ml = ml_Hold( _p_intf );
+}
+
+MLItem::~MLItem()
+{
+    // Free private data
+    if( this->media )
+        ml_gc_decref( this->media );
+    ml_Release( p_intf );
+    if( !children.isEmpty() )
+        clearChildren();
+}
+
+/**
+ * @brief recursively delete all children of this node
+ * @note must be entered after the appropriate beginRemoveRows()
+ */
+void MLItem::clearChildren()
+{
+    // Recursively delete all children
+    qDeleteAll( children );
+    children.clear();
+}
+
+MLItem* MLItem::child( int row ) const
+{
+    if( row < 0 || row >= childCount() ) return NULL;
+    else return children.at( row );
+}
+
+void MLItem::addChild( MLItem *child, int row )
+{
+    assert( child );
+    children.insert( row==-1 ? children.size() : row, child );
+}
+
+void MLItem::delChild( int row )
+{
+    if( !childCount() ) return; // assert ?
+    MLItem *item =
+            children.takeAt( ( row!=-1 ) ? row : ( children.size()-1 ) );
+    assert( item );
+    delete item;
+}
+
+int MLItem::rowOfChild( MLItem *item ) const
+{
+    return children.indexOf( item );
+}
+
+int MLItem::childCount() const
+{
+    return children.size();
+}
+
+MLItem* MLItem::parent() const
+{
+    return parentItem;
+}
+
+/**
+ * @brief Get a QVariant representing the data on a column
+ * @param column
+ * @return The QVariant may be formed of a int, QString
+ *         Use toString() to print it on the screen, except for pixmaps
+ */
+QVariant MLItem::data( int column ) const
+{
+    ml_select_e type = model->columnType( column );
+    ml_person_t *p_people = NULL, *p_person = NULL;
+    QString qsz_return;
+#define sreturn(a) if(media->a) return qfu(media->a); break
+    switch( type )
+    {
+        case ML_ALBUM: sreturn( psz_album );
+        case ML_ALBUM_ID: return media->i_album_id;
+        case ML_ARTIST:
+            p_people = ml_GetPersonsFromMedia( p_ml, media, ML_PERSON_ARTIST );
+            p_person = p_people;
+            while( p_person )
+            {
+                if( !EMPTY_STR( p_person->psz_name ) )
+                {
+                    qsz_return.isEmpty() ? qsz_return = qfu( p_person->psz_name ) :
+                        qsz_return.append( "," ).append( qfu( p_person->psz_name ) );
+                }
+                p_person = p_person->p_next;
+            }
+            ml_FreePeople( p_people );
+            return qsz_return;
+            break;
+        case ML_COVER: sreturn( psz_cover );
+        case ML_DURATION:
+            return QTime().addSecs( media->i_duration/1000000 ).toString( "HH:mm:ss" );
+        case ML_EXTRA: sreturn( psz_extra );
+        case ML_GENRE: sreturn( psz_genre );
+        case ML_ID: return media->i_id;
+        case ML_LAST_PLAYED:
+        {
+            if( media->i_last_played > 0 )
+            {
+                QDateTime time( QDate(1970,1,1) );
+                return time.addSecs( qint64( media->i_last_played ) );
+            }
+            else
+                return QString();
+        }
+        case ML_ORIGINAL_TITLE: sreturn( psz_orig_title );
+        case ML_PLAYED_COUNT: return media->i_played_count;
+        // case ML_ROLE: return qtr( "Role" );
+        case ML_SCORE: return media->i_score ? media->i_score : QVariant();
+        case ML_TITLE:
+        {
+            /* If no title, return filename */
+            if( !EMPTY_STR( media->psz_title ) )
+                return qfu( media->psz_title );
+            else
+            {
+                QFileInfo p_file = QFileInfo( qfu( media->psz_uri ) );
+                return p_file.fileName().isEmpty() ? p_file.absoluteFilePath()
+                    : p_file.fileName();
+            }
+        }
+        case ML_TRACK_NUMBER: return media->i_track_number ? media->i_track_number : QVariant();
+        case ML_TYPE:
+        {
+            QString txt;
+            if( media->i_type & ML_AUDIO )
+                txt = "Audio";
+            if( media->i_type & ML_VIDEO )
+                txt = "Video";
+            if( media->i_type & ML_STREAM )
+            {
+                if( txt.isEmpty() ) txt = "Stream";
+                else txt += " stream";
+            }
+            if( media->i_type & ML_REMOVABLE )
+            {
+                if( txt.isEmpty() ) txt = "Removable media";
+                else txt += " (removable media)";
+            }
+            if( media->i_type & ML_NODE )
+            {
+                if( txt.isEmpty() ) txt = "Playlist";
+                else txt += " (Playlist)";
+            }
+            if( txt.isEmpty() )
+                txt = qtr( "Unknown" );
+            return txt;
+        }
+        case ML_URI: sreturn( psz_uri );
+        case ML_VOTE: return media->i_vote ? media->i_vote : QVariant();
+        case ML_YEAR: return media->i_year ? media->i_year : QVariant();
+        default: return QVariant();
+    }
+#   undef sreturn
+    return QVariant();
+}
+
+bool MLItem::setData( ml_select_e meta, const QVariant &data )
+{
+#   define setmeta(a) ml_LockMedia(media); free(media->a);                  \
+    media->a = strdup( qtu(data.toString()) ); ml_UnlockMedia( media );     \
+    goto update;
+    switch( meta )
+    {
+        /* String values */
+        case ML_ALBUM: setmeta( psz_album );
+        case ML_ARTIST: ml_DeletePersonTypeFromMedia( media, ML_PERSON_ARTIST );
+                        ml_CreateAppendPersonAdv( &media->p_people,
+                                ML_PERSON_ARTIST, (char*)qtu(data.toString()), 0 );
+                        return ml_UpdateSimple( p_ml, ML_MEDIA, NULL, id(),
+                                ML_PEOPLE, ML_PERSON_ARTIST, qtu( data.toString() ) ) == VLC_SUCCESS;
+        case ML_EXTRA: setmeta( psz_extra );
+        case ML_GENRE: setmeta( psz_genre );
+        case ML_ORIGINAL_TITLE: setmeta( psz_orig_title );
+        case ML_TITLE: setmeta( psz_title );
+update:
+            return ml_UpdateSimple( p_ml, ML_MEDIA, NULL, id(),
+                                    meta, qtu( data.toString() ) ) == VLC_SUCCESS;
+
+        /* Modifiable integers */
+        case ML_TRACK_NUMBER:
+        case ML_YEAR:
+            return ml_UpdateSimple( p_ml, ML_MEDIA, NULL, id(),
+                                    meta, data.toInt() ) == VLC_SUCCESS;
+
+        // TODO case ML_VOTE:
+
+        /* Non modifiable meta */
+        default:
+            return false;
+    }
+#   undef setmeta
+}
+
+int MLItem::id() const
+{
+    return media->i_id;
+}
+
+ml_media_t* MLItem::getMedia() const
+{
+    return media;
+}
+
+QUrl MLItem::getUri() const
+{
+    if( !media->psz_uri ) return QUrl(); // This should be rootItem
+    QString uri = qfu( media->psz_uri );
+    if( uri.contains( "://" ) )
+        return QUrl( uri );
+    else
+        return QUrl( "file://" + uri );
+}
+
+bool MLItem::operator<( MLItem* item )
+{
+     int ret = compareMeta( getMedia(), item->getMedia(), ML_ALBUM );
+     if( ret == -1 )
+         return true;
+     else return false;
+}
+#endif
diff --git a/modules/gui/qt4/components/playlist/ml_item.hpp b/modules/gui/qt4/components/playlist/ml_item.hpp
new file mode 100644 (file)
index 0000000..0a9257f
--- /dev/null
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * ml_item.hpp: the media library's result item
+ *****************************************************************************
+ * Copyright (C) 2008-2011 the VideoLAN Team and AUTHORS
+ * $Id$
+ *
+ * Authors: Antoine Lejeune <phytos@videolan.org>
+ *          Jean-Philippe André <jpeg@videolan.org>
+ *          Rémi Duraffort <ivoire@videolan.org>
+ *          Adrien Maglo <magsoft@videolan.org>
+ *          Srikanth Raju <srikiraju#gmail#com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef _MEDIA_LIBRARY_MLITEM_H
+#define _MEDIA_LIBRARY_MLITEM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef MEDIA_LIBRARY
+
+#include <vlc_common.h>
+#include <vlc_interface.h>
+#include <vlc_media_library.h>
+
+#include "ml_model.hpp"
+#include "qt4.hpp"
+
+class MLModel;
+
+class MLItem
+{
+public:
+    MLItem( const MLModel *p_model, intf_thread_t *_p_intf,
+            ml_media_t *p_media, MLItem *p_parent );
+    virtual ~MLItem();
+
+    void addChild( MLItem *child, int row = -1 );
+    void delChild( int row );
+    void clearChildren();
+
+    MLItem* child( int row ) const;
+    int childCount() const;
+
+    MLItem* parent() const;
+
+    QVariant data( int column ) const;
+    bool setData( ml_select_e meta, const QVariant &data );
+
+    int rowOfChild( MLItem *item ) const;
+
+    // Media structure connections
+    int id() const;
+    ml_media_t* getMedia() const;
+    QUrl getUri() const;
+
+    bool operator<( MLItem* item );
+
+private:
+    ml_media_t* media;
+    intf_thread_t* p_intf;
+    media_library_t* p_ml;
+    QList< MLItem* > children;
+    MLItem *parentItem;
+    const MLModel *model;
+};
+
+#endif
+#endif
diff --git a/modules/gui/qt4/components/playlist/ml_model.cpp b/modules/gui/qt4/components/playlist/ml_model.cpp
new file mode 100644 (file)
index 0000000..8aae299
--- /dev/null
@@ -0,0 +1,628 @@
+/*****************************************************************************
+ * ml_model.cpp: the media library's model
+ *****************************************************************************
+ * Copyright (C) 2008-2011 the VideoLAN Team and AUTHORS
+ * $Id$
+ *
+ * Authors: Antoine Lejeune <phytos@videolan.org>
+ *          Jean-Philippe André <jpeg@videolan.org>
+ *          Rémi Duraffort <ivoire@videolan.org>
+ *          Adrien Maglo <magsoft@videolan.org>
+ *          Srikanth Raju <srikiraju#gmail#com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef MEDIA_LIBRARY
+
+#include <QUrl>
+#include <QMenu>
+#include <QMimeData>
+#include "ml_item.hpp"
+#include "ml_model.hpp"
+#include "dialogs/mediainfo.hpp"
+#include "dialogs/playlist.hpp"
+#include "components/playlist/sorting.h"
+#include "dialogs_provider.hpp"
+#include "input_manager.hpp"                            /* THEMIM */
+
+#include <assert.h>
+#include <vlc_intf_strings.h>
+
+static int mediaAdded( vlc_object_t *p_this, char const *psz_var,
+                                  vlc_value_t oldval, vlc_value_t newval,
+                                  void *data );
+static int mediaDeleted( vlc_object_t *p_this, char const *psz_var,
+                                  vlc_value_t oldval, vlc_value_t newval,
+                                  void *data );
+static int mediaUpdated( vlc_object_t *p_this, char const *psz_var,
+                                  vlc_value_t oldval, vlc_value_t newval,
+                                  void *data );
+
+/**
+ * @brief Definition of the result item model for the result tree
+ * @param parent the parent Qt object
+ */
+MLModel::MLModel( intf_thread_t* _p_intf, QObject *parent )
+        :VLCModel( _p_intf, parent )
+{
+    p_ml = ml_Hold( p_intf );
+    vlc_array_t *p_result_array = vlc_array_new();
+    ml_Find( p_ml, p_result_array, ML_MEDIA );
+    insertResultArray( p_result_array );
+
+    var_AddCallback( p_ml, "media-added", mediaAdded, this );
+    var_AddCallback( p_ml, "media-deleted", mediaDeleted, this );
+    var_AddCallback( p_ml, "media-meta-change", mediaUpdated, this );
+}
+
+/**
+ * @brief Simple destructor for the model
+ */
+MLModel::~MLModel()
+{
+    var_DelCallback( p_ml, "media-meta-change", mediaUpdated, this );
+    var_DelCallback( p_ml, "media-deleted", mediaDeleted, this );
+    var_DelCallback( p_ml, "media-added", mediaAdded, this );
+    ml_Release( p_intf );
+}
+
+void MLModel::clear()
+{
+    int rows = rowCount();
+    if( rows > 0 )
+    {
+        beginRemoveRows( createIndex( 0, 0 ), 0, rows-1 );
+        items.clear();
+        endRemoveRows();
+        emit layoutChanged();
+    }
+}
+
+QModelIndex MLModel::index( int row, int column,
+                                  const QModelIndex &parent ) const
+{
+    if( parent.isValid() )
+        return QModelIndex();
+    else
+    {
+        QModelIndex idx = createIndex( row, column, items.value( row ) );
+        return idx;
+    }
+}
+
+QModelIndex MLModel::parent(const QModelIndex &index) const
+{
+    return QModelIndex();
+}
+
+/**
+ * @brief Return the index of currently playing item
+ */
+QModelIndex MLModel::currentIndex() const
+{
+    input_thread_t *p_input_thread = THEMIM->getInput();
+    if( !p_input_thread ) return QModelIndex();
+
+    /*TODO: O(n) not good */
+    input_item_t* p_iitem = input_GetItem( p_input_thread );
+    foreach( MLItem* item, items )
+    {
+        if( !QString::compare( item->getUri().toString(),
+                    QString::fromAscii( p_iitem->psz_uri ) ) )
+            return index( items.indexOf( item ), 0 );
+    }
+    return QModelIndex();
+}
+/**
+ * @brief This returns the type of data shown in the specified column
+ * @param column must be valid
+ * @return The type, or ML_END in case of error
+ */
+ml_select_e MLModel::columnType( int logicalindex ) const
+{
+    if( logicalindex < 0 || logicalindex >= columnCount() ) return ML_END;
+    return meta_to_mlmeta( columnToMeta( logicalindex ) );
+}
+
+QVariant MLModel::headerData( int section, Qt::Orientation orientation,
+                                    int role ) const
+{
+    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+        return QVariant( psz_column_title( columnToMeta( section ) ) );
+    else
+        return QVariant();
+}
+
+Qt::ItemFlags MLModel::flags(const QModelIndex &index) const
+{
+    if( !index.isValid() )
+        return 0;
+
+    if( isEditable( index ) )
+        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled
+                | Qt::ItemIsEditable;
+    else
+        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
+}
+
+bool MLModel::isEditable( const QModelIndex &index ) const
+{
+    if( !index.isValid() )
+        return false;
+
+    ml_select_e type = columnType( index.column() );
+    switch( type )
+    {
+    // Read-only members: not editable
+    case ML_ALBUM_ID:
+    case ML_ARTIST_ID:
+    case ML_DURATION:
+    case ML_ID:
+    case ML_LAST_PLAYED:
+    case ML_PLAYED_COUNT:
+    case ML_TYPE:
+        return false;
+    // Read-write members: editable
+    case ML_ALBUM:
+    case ML_ARTIST:
+    case ML_COVER:
+    case ML_EXTRA:
+    case ML_GENRE:
+    case ML_ORIGINAL_TITLE:
+    // case ML_ROLE:
+    case ML_SCORE:
+    case ML_TITLE:
+    case ML_TRACK_NUMBER:
+    case ML_URI:
+    case ML_VOTE:
+    case ML_YEAR:
+        return true;
+    }
+    return false;
+}
+
+QMimeData* MLModel::mimeData( const QModelIndexList &indexes ) const
+{
+    QList< QUrl > urls;
+    QList< int > rows;
+    foreach( QModelIndex idx, indexes )
+    {
+        if( rows.contains( idx.row() ) )
+            continue;
+        rows.append( idx.row() );
+        MLItem* item = static_cast<MLItem*>( idx.internalPointer() );
+        urls.append( item->getUri() );
+    }
+    QMimeData *data = new QMimeData;
+    data->setUrls( urls );
+    return data;
+}
+
+int MLModel::columnCount( const QModelIndex & parent ) const
+{
+    return columnFromMeta( COLUMN_END );
+}
+
+int MLModel::rowCount( const QModelIndex & parent ) const
+{
+    if( !parent.isValid() )
+        return items.count();
+    return 0;
+}
+
+void MLModel::remove( MLItem *item )
+{
+    int row = items.indexOf( item );
+    remove( createIndex( row, 0 ) );
+}
+
+void MLModel::remove( QModelIndexList list )
+{
+    for (int i = 0; i < list.size(); ++i) {
+        remove( list.at(i) );
+    }
+}
+
+void MLModel::remove( QModelIndex idx )
+{
+    if( !idx.isValid() )
+        return;
+    else
+    {
+        beginRemoveRows( createIndex( 0, 0 ), idx.row(), idx.row() );
+        items.removeAt( idx.row() );
+        endRemoveRows();
+    }
+}
+
+int MLModel::getId( QModelIndex index ) const
+{
+    return getItem( index )->id();
+}
+
+QVariant MLModel::data( const QModelIndex &index, int role ) const
+{
+    if( index.isValid() )
+        if( role == Qt::DisplayRole || role == Qt::EditRole )
+        {
+            MLItem *it = static_cast<MLItem*>( index.internalPointer() );
+            if( !it ) return QVariant();
+            QVariant tmp = it->data( index.column() );
+            return tmp;
+        }
+        else if( role == VLCModel::IsLeafNodeRole )
+            return QVariant( true );
+        else if( role == VLCModel::IsCurrentsParentNodeRole )
+            return QVariant( false );
+    return QVariant();
+}
+
+bool MLModel::setData( const QModelIndex &idx, const QVariant &value,
+                             int role )
+{
+    if( role != Qt::EditRole || !idx.isValid() ) return false;
+    MLItem *media = static_cast<MLItem*>( idx.internalPointer() );
+    media->setData( columnType( idx.column() ), value );
+    emit dataChanged( idx, idx );
+}
+
+/**
+ * @brief Insert a media to the model in a given row
+ * @param p_media the media to append
+ * @param row the future row for this media, -1 to append at the end
+ * @param bSignal signal Qt that the model has been modified,
+ *                should NOT be used by the user
+ * @return a VLC error code
+ */
+int MLModel::insertMedia( ml_media_t *p_media, int row,
+                                bool bSignal )
+{
+    // Some checks
+    if( !p_media || row < -1 || row > rowCount() )
+        return VLC_EGENERIC;
+
+    if( row == -1 )
+        row = rowCount();
+
+    if( bSignal )
+        beginInsertRows( createIndex( -1, -1 ), row, row );
+
+    // Create and insert the item
+    MLItem *item = new MLItem( this, p_intf, p_media, NULL );
+    items.append( item );
+
+    if( bSignal )
+        endInsertRows();
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * @brief Append a media to the model
+ * @param p_media the media to append
+ * @return see insertMedia
+ * @note Always signals. Do not use in a loop.
+ */
+int MLModel::appendMedia( ml_media_t *p_media )
+{
+    return insertMedia( p_media, -1, true );
+}
+
+/**
+ * @brief Insert all medias from an array to the model
+ * @param p_media_array the medias to append
+ * @return see insertMedia
+ * @note if bSignal==true, then it signals only once
+ */
+int MLModel::insertMediaArray( vlc_array_t *p_media_array,
+                                     int row, bool bSignal )
+{
+    int i_ok = VLC_SUCCESS;
+    int count = vlc_array_count( p_media_array );
+
+    if( !count )
+        return i_ok;
+
+    if( row == -1 )
+        row = rowCount();
+
+    // Signal Qt that we will insert rows
+    if( bSignal )
+        beginInsertRows( createIndex( -1, -1 ), row, row + count-1 );
+
+    // Loop
+    for( int i = 0; i < count; ++i )
+    {
+        i_ok = insertMedia( (ml_media_t*)
+            vlc_array_item_at_index( p_media_array, i ), row + i, false );
+        if( i_ok != VLC_SUCCESS )
+            break;
+    }
+
+    if( bSignal )
+        endInsertRows();
+
+    return i_ok;
+}
+
+/**
+ * @brief Insert the media contained in a result to the model
+ * @param p_result the media to append is p_result->value.p_media
+ * @param row the future row for this media
+ * @param bSignal signal Qt that the model has been modified,
+ *                should NOT be used by the user
+ * @return a VLC error code
+ */
+int MLModel::insertResult( const ml_result_t *p_result, int row,
+                                 bool bSignal )
+{
+    if( !p_result || p_result->type != ML_TYPE_MEDIA )
+        return VLC_EGENERIC;
+    else
+        return insertMedia( p_result->value.p_media, row, bSignal );
+}
+
+/**
+ * @brief Append the media contained in a result to the model
+ * @param p_result the media to append is p_result->value.p_media
+ * @param row the future row for this media
+ * @return a VLC error code
+ * @note Always signals. Do not use in a loop.
+ */
+inline int MLModel::appendResult( const ml_result_t *p_result )
+{
+    return insertResult( p_result, -1, true );
+}
+
+/**
+ * @brief Insert all medias from a result array to the model
+ * @param p_result_array the medias to append
+ * @return see insertMedia
+ * @note if bSignal==true, then it signals only once
+ *       not media or NULL items are skipped
+ */
+int MLModel::insertResultArray( vlc_array_t *p_result_array,
+                                      int row, bool bSignal )
+{
+    int i_ok = VLC_SUCCESS;
+    int count = vlc_array_count( p_result_array );
+
+    if( !count )
+        return i_ok;
+
+    if( row == -1 )
+        row = rowCount();
+
+    // Signal Qt that we will insert rows
+    if( bSignal )
+        beginInsertRows( createIndex( -1, -1 ), row, row + count-1 );
+
+    // Loop and insert
+    for( int i = 0; i < count; ++i )
+    {
+        ml_result_t *p_result = (ml_result_t*)
+                        vlc_array_item_at_index( p_result_array, i );
+        if( !p_result || p_result->type != ML_TYPE_MEDIA )
+            continue;
+        i_ok = insertMedia( p_result->value.p_media, row + i, false );
+        if( i_ok != VLC_SUCCESS )
+            break;
+    }
+    // Signal we're done
+    if( bSignal )
+        endInsertRows();
+
+    return i_ok;
+}
+
+/** **************************************************************************
+ * \brief Add a media to the playlist
+ *
+ * \param id the item id
+ * @todo this code must definitely be done by the ML core
+ *****************************************************************************/
+static void AddItemToPlaylist( int i_media_id, bool bPlay, media_library_t* p_ml,
+        bool bRenew )
+{
+
+    input_item_t *p_item = ml_CreateInputItem( p_ml, i_media_id );
+    if( !p_item )
+    {
+        msg_Dbg( p_ml, "unable to create input item for media %d",
+                 i_media_id );
+        return;
+    }
+    playlist_t *p_playlist = pl_Get( p_ml );
+    playlist_item_t *p_playlist_item = NULL;
+
+    playlist_Lock( p_playlist );
+    if( !bRenew )
+    {
+        p_playlist_item = playlist_ItemGetByInput( p_playlist, p_item );
+    }
+
+    if( !p_playlist_item || p_playlist_item->i_id == 1 )
+    {
+        playlist_AddInput( p_playlist, p_item,
+                           PLAYLIST_APPEND,
+                           PLAYLIST_END, true, true );
+
+        p_playlist_item = playlist_ItemGetByInput( p_playlist, p_item );
+    }
+    playlist_Unlock( p_playlist );
+
+    if( !p_playlist_item || p_playlist_item->i_id == 1 )
+    {
+        msg_Dbg( p_ml, "could not find playlist item %s (%s:%d)",
+                 p_item->psz_name, __FILE__, __LINE__ );
+        return;
+    }
+
+    /* Auto play item */
+    if( bPlay ) // || p_playlist->status.i_status == PLAYLIST_STOPPED )
+    {
+        playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, false,
+                          NULL, p_playlist_item );
+    }
+    vlc_gc_decref( p_item );
+}
+
+void MLModel::activateItem( const QModelIndex &index )
+{
+    play( index );
+}
+
+void MLModel::play( const QModelIndex &idx )
+{
+    if( !idx.isValid() )
+        return;
+    MLItem *item = static_cast< MLItem* >( idx.internalPointer() );
+    if( !item )
+        return;
+    AddItemToPlaylist( item->id(), true, p_ml, true );
+}
+
+bool MLModel::popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list )
+{
+    current_selection = list;
+    current_index = index;
+    QMenu menu;
+    if( index.isValid() )
+    {
+        menu.addAction( QIcon( ":/menu/play" ), qtr(I_POP_PLAY), this, SLOT( popupPlay() ) );
+        menu.addAction( QIcon( ":/menu/stream" ),
+                        qtr(I_POP_STREAM), this, SLOT( popupStream() ) );
+        menu.addAction( qtr(I_POP_SAVE), this, SLOT( popupSave() ) );
+        menu.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO), this, SLOT( popupInfo() ) );
+        menu.addSeparator();
+    }
+
+
+    QIcon addIcon( ":/buttons/playlist/playlist_add" );
+    menu.addSeparator();
+    menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simpleMLAppendDialog()) );
+    menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( MLAppendDir() ) );
+    menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( MLAppendDialog() ) );
+
+    if( index.isValid() )
+    {
+        menu.addAction( QIcon( ":/buttons/playlist/playlist_remove" ),
+                        qtr(I_POP_DEL), this, SLOT( popupDel() ) );
+        menu.addSeparator();
+    }
+    if( !menu.isEmpty() )
+    {
+        menu.exec( point ); return true;
+    }
+    else return false;
+}
+
+
+void MLModel::popupDel()
+{
+    remove( current_selection );
+}
+
+void MLModel::popupPlay()
+{
+    play( current_index );
+}
+
+void MLModel::popupInfo()
+{
+    MLItem *item = static_cast< MLItem* >( current_index.internalPointer() );
+    input_item_t* p_input = ml_CreateInputItem( p_ml,  item->id() );
+    MediaInfoDialog *mid = new MediaInfoDialog( p_intf, p_input );
+    mid->setParent( PlaylistDialog::getInstance( p_intf ),
+                    Qt::Dialog );
+    mid->show();
+}
+
+QStringList MLModel::selectedURIs()
+{
+    QStringList list;
+    for( int i = 0; i < current_selection.size(); i++ )
+    {
+        QModelIndex idx = current_selection.value(i);
+        MLItem *item = static_cast< MLItem* >( idx.internalPointer() );
+        list.append( QString( item->getUri().toString() ) );
+    }
+    return list;
+}
+
+void MLModel::popupStream()
+{
+    QStringList mrls = selectedURIs();
+    if( !mrls.isEmpty() )
+        THEDP->streamingDialog( NULL, mrls[0], false );
+
+}
+
+void MLModel::popupSave()
+{
+    QStringList mrls = selectedURIs();
+    if( !mrls.isEmpty() )
+        THEDP->streamingDialog( NULL, mrls[0] );
+}
+
+static int mediaAdded( vlc_object_t *p_this, char const *psz_var,
+                                  vlc_value_t oldval, vlc_value_t newval,
+                                  void *data )
+{
+    int ret = VLC_SUCCESS;
+    media_library_t *p_ml = ( media_library_t* )p_this;
+    MLModel* p_model = ( MLModel* )data;
+    vlc_array_t* p_result = vlc_array_new();
+    ret = ml_FindMedia( p_ml, p_result, ML_ID, newval.i_int );
+    if( ret != VLC_SUCCESS )
+    {
+        vlc_array_destroy( p_result );
+        return VLC_EGENERIC;
+    }
+    p_model->insertResultArray( p_result );
+    vlc_array_destroy( p_result );
+    return VLC_SUCCESS;
+}
+
+static int mediaDeleted( vlc_object_t *p_this, char const *psz_var,
+                                  vlc_value_t oldval, vlc_value_t newval,
+                                  void *data )
+{
+    MLModel* p_model = ( MLModel* )data;
+    QModelIndex remove_idx = QModelIndex();
+    for( int i = 0; i < p_model->rowCount( ); i++ )
+    {
+        QModelIndex idx = p_model->index( i, 0 );
+        MLItem *item = static_cast< MLItem* >( idx.internalPointer() );
+        if( item->id() == newval.i_int )
+        {
+            remove_idx = idx;
+            break;
+        }
+    }
+    if( remove_idx.isValid() )
+        p_model->remove( remove_idx );
+    return VLC_SUCCESS;
+}
+
+static int mediaUpdated( vlc_object_t *p_this, char const *psz_var,
+                                  vlc_value_t oldval, vlc_value_t newval,
+                                  void *data )
+{
+    return VLC_SUCCESS;
+}
+
+#endif
diff --git a/modules/gui/qt4/components/playlist/ml_model.hpp b/modules/gui/qt4/components/playlist/ml_model.hpp
new file mode 100644 (file)
index 0000000..b06968e
--- /dev/null
@@ -0,0 +1,125 @@
+/*****************************************************************************
+ * ml_model.hpp ML model
+ *****************************************************************************
+ * Copyright (C) 2008-2011 the VideoLAN Team and AUTHORS
+ * $Id$
+ *
+ * Authors: Antoine Lejeune <phytos@videolan.org>
+ *          Jean-Philippe André <jpeg@videolan.org>
+ *          Rémi Duraffort <ivoire@videolan.org>
+ *          Adrien Maglo <magsoft@videolan.org>
+ *          Srikanth Raju <srikiraju#gmail#com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef _MEDIA_LIBRARY_MLMODEL_H
+#define _MEDIA_LIBRARY_MLMODEL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef MEDIA_LIBRARY
+#include <vlc_common.h>
+#include <vlc_interface.h>
+#include <vlc_media_library.h>
+
+#include "components/playlist/vlc_model.hpp"
+#include "ml_item.hpp"
+#include "qt4.hpp"
+
+class MLItem;
+
+/** *************************************************************************
+ * \brief Tree model for the result list
+ ****************************************************************************/
+class MLModel : public VLCModel
+{
+    Q_OBJECT;
+
+public:
+    // Basic QAbstractItemModel implementation
+    MLModel( intf_thread_t *_p_intf, QObject *parent = NULL );
+    virtual ~MLModel();
+    inline MLItem *getItem( QModelIndex index ) const
+    {
+        if( index.isValid() )
+            return static_cast<MLItem*>( index.internalPointer() );
+        else return NULL;
+    }
+    virtual int getId( QModelIndex index ) const;
+
+    QVariant data( const QModelIndex &idx, int role = Qt::DisplayRole ) const;
+    bool setData( const QModelIndex &idx, const QVariant &value,
+                  int role = Qt::EditRole );
+    ml_select_e columnType( int column ) const;
+
+    QModelIndex index( int row, int column,
+                       const QModelIndex & parent = QModelIndex() ) const;
+    virtual QModelIndex currentIndex() const;
+    int rowCount( const QModelIndex & parent = QModelIndex() ) const;
+    int columnCount( const QModelIndex & parent = QModelIndex() ) const;
+
+    QModelIndex parent( const QModelIndex& ) const;
+    QVariant headerData( int, Qt::Orientation, int ) const;
+    Qt::ItemFlags flags( const QModelIndex& ) const;
+    bool isEditable( const QModelIndex& ) const;
+
+    // Drag and drop: MIME data
+    QMimeData* mimeData( const QModelIndexList & indexes ) const;
+
+    // Custom functions
+    int insertMedia( ml_media_t *p_media, int row = -1,
+                     bool bSignal = true );
+    int appendMedia( ml_media_t *p_media );
+    int insertMediaArray( vlc_array_t *p_media_array, int row = -1,
+                          bool bSignal = true );
+
+    int insertResult( const ml_result_t *p_result, int row = -1,
+                      bool bSignal = true );
+    inline int appendResult( const ml_result_t *p_result );
+    int insertResultArray( vlc_array_t *p_result_array, int row = -1,
+                           bool bSignal = true );
+
+    void remove( MLItem *item );
+    void remove( QModelIndexList list );
+    void remove( QModelIndex idx );
+
+    void clear();
+    virtual bool popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list );
+    void play( const QModelIndex &idx );
+    QStringList selectedURIs();
+
+public slots:
+    void activateItem( const QModelIndex &index );
+
+protected slots:
+    void popupDel();
+    void popupPlay();
+    void popupInfo();
+    void popupStream();
+    void popupSave();
+
+private:
+    QList< MLItem* > items;
+    media_library_t* p_ml;
+
+    QModelIndex current_index;
+    QModelIndexList current_selection;
+};
+
+#endif
+#endif
index c3240548a88a58e5206869b94450b8b01f3b1ad6..f0ea8722c75619a347fd1ff43bd536e549645cd3 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * sorting.h : commun sorting & column display code
  ****************************************************************************
- * Copyright © 2008 the VideoLAN team
+ * Copyright © 2008 the VideoLAN team and AUTHORS
  * $Id$
  *
  * Authors: Rafaël Carré <funman@videolanorg>
@@ -21,6 +21,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
+#ifndef _SORTING_H_
+#define _SORTING_H_
+#include <vlc_media_library.h>
 /* You can use these numbers with | and & to determine what you want to show */
 enum
 {
@@ -33,10 +36,12 @@ enum
     COLUMN_DESCRIPTION    = 0x0040,
     COLUMN_URI            = 0x0080,
     COLUMN_NUMBER         = 0x0100,
+    COLUMN_RATING         = 0x0200,
+    COLUMN_COVER          = 0x0400,
 
     /* Add new entries here and update the COLUMN_END value*/
 
-    COLUMN_END          = 0x0200
+    COLUMN_END          = 0x0800
 };
 
 #define COLUMN_DEFAULT (COLUMN_TITLE|COLUMN_DURATION|COLUMN_ALBUM)
@@ -55,6 +60,8 @@ static inline const char * psz_column_title( uint32_t i_column )
     case COLUMN_TRACK_NUMBER:    return VLC_META_TRACK_NUMBER;
     case COLUMN_DESCRIPTION:     return VLC_META_DESCRIPTION;
     case COLUMN_URI:             return _("URI");
+    case COLUMN_RATING:          return VLC_META_RATING;
+    case COLUMN_COVER:           return VLC_META_ART_URL;
     default: abort();
     }
 }
@@ -88,6 +95,10 @@ static inline char * psz_column_meta( input_item_t *p_item, uint32_t i_column )
         return input_item_GetDescription( p_item );
     case COLUMN_URI:
         return input_item_GetURI( p_item );
+    case COLUMN_RATING:
+        return input_item_GetRating( p_item );
+    case COLUMN_COVER:
+        return input_item_GetArtworkURL( p_item );
     default:
         abort();
     }
@@ -107,6 +118,28 @@ static inline int i_column_sorting( uint32_t i_column )
     case COLUMN_TRACK_NUMBER:   return SORT_TRACK_NUMBER;
     case COLUMN_DESCRIPTION:    return SORT_DESCRIPTION;
     case COLUMN_URI:            return SORT_URI;
+    case COLUMN_RATING:         return SORT_RATING;
     default: abort();
     }
 }
+
+static inline ml_select_e meta_to_mlmeta( uint32_t i_column )
+{
+    switch( i_column )
+    {
+    case COLUMN_NUMBER:         return ML_ID;
+    case COLUMN_TITLE:          return ML_TITLE;
+    case COLUMN_DURATION:       return ML_DURATION;
+    case COLUMN_ARTIST:         return ML_ARTIST;
+    case COLUMN_GENRE:          return ML_GENRE;
+    case COLUMN_ALBUM:          return ML_ALBUM;
+    case COLUMN_TRACK_NUMBER:   return ML_TRACK_NUMBER;
+    case COLUMN_DESCRIPTION:    return ML_EXTRA;
+    case COLUMN_URI:            return ML_URI;
+    case COLUMN_RATING:         return ML_VOTE;
+    case COLUMN_COVER:          return ML_COVER;
+    default: abort();
+    }
+}
+
+#endif