playlist_model.cpp 29.4 KB
Newer Older
zorglub's avatar
zorglub committed
1
/*****************************************************************************
zorglub's avatar
zorglub committed
2
 * playlist_model.cpp : Manage playlist model
zorglub's avatar
zorglub committed
3
 ****************************************************************************
4
 * Copyright (C) 2006-2007 the VideoLAN team
5
 * $Id$
zorglub's avatar
zorglub committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
 * 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.
 *****************************************************************************/
zorglub's avatar
zorglub committed
23
#define PLI_NAME( p ) p ? p->p_input->psz_name : "null"
zorglub's avatar
zorglub committed
24

zorglub's avatar
zorglub committed
25
#include <assert.h>
zorglub's avatar
zorglub committed
26
#include <QIcon>
zorglub's avatar
Qt4:  
zorglub committed
27
#include <QFont>
zorglub's avatar
zorglub committed
28
#include <QMenu>
zorglub's avatar
zorglub committed
29
#include <QApplication>
zorglub's avatar
zorglub committed
30 31

#include "qt4.hpp"
zorglub's avatar
zorglub committed
32
#include "playlist_model.hpp"
zorglub's avatar
zorglub committed
33
#include "dialogs/mediainfo.hpp"
34
#include <vlc_intf_strings.h>
zorglub's avatar
zorglub committed
35

zorglub's avatar
zorglub committed
36 37 38 39 40 41 42 43 44 45 46 47 48
#include "pixmaps/type_unknown.xpm"
#include "pixmaps/type_afile.xpm"
#include "pixmaps/type_vfile.xpm"
#include "pixmaps/type_net.xpm"
#include "pixmaps/type_card.xpm"
#include "pixmaps/type_disc.xpm"
#include "pixmaps/type_cdda.xpm"
#include "pixmaps/type_directory.xpm"
#include "pixmaps/type_playlist.xpm"
#include "pixmaps/type_node.xpm"

QIcon PLModel::icons[ITEM_TYPE_NUMBER];

zorglub's avatar
zorglub committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
static int PlaylistChanged( vlc_object_t *, const char *,
                            vlc_value_t, vlc_value_t, void * );
static int PlaylistNext( vlc_object_t *, const char *,
                         vlc_value_t, vlc_value_t, void * );
static int ItemChanged( vlc_object_t *, const char *,
                        vlc_value_t, vlc_value_t, void * );
static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
                         vlc_value_t oval, vlc_value_t nval, void *param );
static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
                        vlc_value_t oval, vlc_value_t nval, void *param );

/*************************************************************************
 * Playlist item implementation
 *************************************************************************/

zorglub's avatar
zorglub committed
64 65 66 67 68 69 70 71
/**
 * Column strings
 *      Title
 *      Artist
 *      Duration
 */

void PLItem::init( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
zorglub's avatar
zorglub committed
72 73 74 75
{
    parentItem = parent;
    i_id = _i_id; i_input_id = _i_input_id;
    model = m;
zorglub's avatar
zorglub committed
76 77 78
    strings.append( "" );
    strings.append( "" );
    strings.append( "" );
zorglub's avatar
zorglub committed
79
    strings.append( "" );
zorglub's avatar
zorglub committed
80 81 82 83 84
}

PLItem::PLItem( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
{
    init( _i_id, _i_input_id, parent, m );
zorglub's avatar
zorglub committed
85 86 87 88
}

PLItem::PLItem( playlist_item_t * p_item, PLItem *parent, PLModel *m )
{
zorglub's avatar
zorglub committed
89
    init( p_item->i_id, p_item->p_input->i_id, parent, m );
zorglub's avatar
zorglub committed
90 91 92 93 94
}

PLItem::~PLItem()
{
    qDeleteAll(children);
zorglub's avatar
zorglub committed
95
    children.clear();
zorglub's avatar
zorglub committed
96 97
}

zorglub's avatar
zorglub committed
98
void PLItem::insertChild( PLItem *item, int i_pos, bool signal )
zorglub's avatar
zorglub committed
99 100
{
    assert( model );
zorglub's avatar
zorglub committed
101 102
    if( signal )
        model->beginInsertRows( model->index( this , 0 ), i_pos, i_pos );
zorglub's avatar
zorglub committed
103
    children.insert( i_pos, item );
zorglub's avatar
zorglub committed
104 105
    if( signal )
        model->endInsertRows();
zorglub's avatar
zorglub committed
106 107
}

zorglub's avatar
Qt4:  
zorglub committed
108 109 110 111 112 113 114 115 116
void PLItem::remove( PLItem *removed )
{
    assert( model && parentItem );
    int i_index = parentItem->children.indexOf( removed );
    model->beginRemoveRows( model->index( parentItem, 0 ), i_index, i_index );
    parentItem->children.removeAt( i_index );
    model->endRemoveRows();
}

zorglub's avatar
zorglub committed
117 118
int PLItem::row() const
{
119
    if( parentItem )
zorglub's avatar
zorglub committed
120 121 122 123
        return parentItem->children.indexOf(const_cast<PLItem*>(this));
    return 0;
}

zorglub's avatar
Qt4:  
zorglub committed
124
void PLItem::update( playlist_item_t *p_item, bool iscurrent )
zorglub's avatar
zorglub committed
125
{
zorglub's avatar
zorglub committed
126
    char psz_duration[MSTRTIME_MAX_SIZE];
zorglub's avatar
zorglub committed
127 128
    assert( p_item->p_input->i_id == i_input_id );
    strings[0] = QString::fromUtf8( p_item->p_input->psz_name );
zorglub's avatar
zorglub committed
129 130 131 132
    if( p_item->p_input->p_meta )
    {
        strings[1] = QString::fromUtf8( p_item->p_input->p_meta->psz_artist );
    }
zorglub's avatar
zorglub committed
133 134
    secstotimestr( psz_duration, p_item->p_input->i_duration / 1000000 );
    strings[2] = QString( psz_duration );
zorglub's avatar
zorglub committed
135
    type = p_item->p_input->i_type;
zorglub's avatar
Qt4:  
zorglub committed
136
    current = iscurrent;
137

138 139 140 141
    if( current && p_item->p_input->p_meta &&
        p_item->p_input->p_meta->psz_arturl &&
        !strncmp( p_item->p_input->p_meta->psz_arturl, "file://", 7 ) )
        model->sendArt( qfu( p_item->p_input->p_meta->psz_arturl ) );
142 143
    else if( current )
        model->removeArt();
zorglub's avatar
zorglub committed
144
}
zorglub's avatar
zorglub committed
145 146 147 148 149

/*************************************************************************
 * Playlist model implementation
 *************************************************************************/

zorglub's avatar
zorglub committed
150
PLModel::PLModel( playlist_t *_p_playlist, intf_thread_t *_p_intf,
zorglub's avatar
zorglub committed
151
                  playlist_item_t * p_root, int _i_depth, QObject *parent)
zorglub's avatar
zorglub committed
152 153
                                    : QAbstractItemModel(parent)
{
zorglub's avatar
zorglub committed
154 155
    i_depth = _i_depth;
    assert( i_depth == 1 || i_depth == -1 );
zorglub's avatar
zorglub committed
156
    p_intf = _p_intf;
zorglub's avatar
zorglub committed
157
    p_playlist= _p_playlist;
zorglub's avatar
zorglub committed
158 159 160 161
    i_items_to_append = 0;
    b_need_update     = false;
    i_cached_id       = -1;
    i_cached_input_id = -1;
zorglub's avatar
zorglub committed
162
    i_popup_item = i_popup_parent = -1;
zorglub's avatar
zorglub committed
163

zorglub's avatar
zorglub committed
164 165 166 167 168 169 170 171 172 173 174 175
#define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( QPixmap( type_##x##_xpm ) );
    ADD_ICON( UNKNOWN , unknown );
    ADD_ICON( AFILE,afile );
    ADD_ICON( VFILE, vfile );
    ADD_ICON( DIRECTORY, directory );
    ADD_ICON( DISC, disc );
    ADD_ICON( CDDA, cdda );
    ADD_ICON( CARD, card );
    ADD_ICON( NET, net );
    ADD_ICON( PLAYLIST, playlist );
    ADD_ICON( NODE, node );

zorglub's avatar
zorglub committed
176
    rootItem = NULL;
zorglub's avatar
zorglub committed
177
    addCallbacks();
zorglub's avatar
zorglub committed
178
    rebuild( p_root );
zorglub's avatar
zorglub committed
179 180
}

zorglub's avatar
zorglub committed
181

zorglub's avatar
zorglub committed
182 183 184 185 186 187
PLModel::~PLModel()
{
    delCallbacks();
    delete rootItem;
}

zorglub's avatar
zorglub committed
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
Qt::DropActions PLModel::supportedDropActions() const
{
    return Qt::CopyAction;
}

Qt::ItemFlags PLModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
    if( index.isValid() )
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;
}

QStringList PLModel::mimeTypes() const
{
    QStringList types;
    types << "vlc/playlist-item-id";
    return types;
}

QMimeData *PLModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;
    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    foreach (QModelIndex index, indexes) {
        if (index.isValid() && index.column() == 0 )
            stream << itemId(index);
    }
    mimeData->setData("vlc/playlist-item-id", encodedData);
    return mimeData;
}

bool PLModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
                           int row, int column, const QModelIndex &target)
{
    if ( data->hasFormat("vlc/playlist-item-id") )
    {
        if (action == Qt::IgnoreAction)
            return true;

        PLItem *targetItem;
        if( target.isValid() )
            targetItem = static_cast<PLItem*>( target.internalPointer() );
        else
            targetItem = rootItem;

        QByteArray encodedData = data->data("vlc/playlist-item-id");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);

        PLItem *newParentItem;
        while (!stream.atEnd())
        {
            int i;
            int srcId;
            stream >> srcId;

            PL_LOCK;
            playlist_item_t *p_target =
249 250 251 252
                        playlist_ItemGetById( p_playlist, targetItem->i_id,
                                              VLC_TRUE );
            playlist_item_t *p_src = playlist_ItemGetById( p_playlist, srcId,
                                                           VLC_TRUE );
zorglub's avatar
zorglub committed
253 254 255 256 257 258 259 260 261 262 263

            if( !p_target || !p_src )
            {
                PL_UNLOCK;
                return false;
            }
            if( p_target->i_children == -1 ) /* A leaf */
            {
                PLItem *parentItem = targetItem->parent();
                assert( parentItem );
                playlist_item_t *p_parent =
264 265
                         playlist_ItemGetById( p_playlist, parentItem->i_id,
                                               VLC_TRUE );
zorglub's avatar
zorglub committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
                if( !p_parent )
                {
                    PL_UNLOCK;
                    return false;
                }
                for( i = 0 ; i< p_parent->i_children ; i++ )
                    if( p_parent->pp_children[i] == p_target ) break;
                playlist_TreeMove( p_playlist, p_src, p_parent, i );
                newParentItem = parentItem;
            }
            else
            {
                /* \todo: if we drop on a top-level node, use copy instead ? */
                playlist_TreeMove( p_playlist, p_src, p_target, 0 );
                i = 0;
                newParentItem = targetItem;
            }
            /* Remove from source */
zorglub's avatar
zorglub committed
284 285 286 287 288 289 290 291 292 293
            PLItem *srcItem = FindById( rootItem, p_src->i_id );
            // We dropped on the source selector. Ask the dialog to forward
            // to the main view
            if( !srcItem )
            {
                emit shouldRemove( p_src->i_id );
            }
            else
                srcItem->remove( srcItem );

zorglub's avatar
zorglub committed
294 295 296 297 298 299 300 301 302 303
            /* Display at new destination */
            PLItem *newItem = new PLItem( p_src, newParentItem, this );
            newParentItem->insertChild( newItem, i, true );
            UpdateTreeItem( p_src, newItem, true );
            if( p_src->i_children != -1 )
                UpdateNodeChildren( newItem );
            PL_UNLOCK;
        }
    }
    return true;
zorglub's avatar
zorglub committed
304
}
zorglub's avatar
zorglub committed
305

zorglub's avatar
zorglub committed
306 307 308 309 310
void PLModel::removeItem( int i_id )
{
    PLItem *item = FindById( rootItem,i_id );
    if( item ) item->remove( item );
}
zorglub's avatar
zorglub committed
311

zorglub's avatar
zorglub committed
312 313
void PLModel::addCallbacks()
{
zorglub's avatar
zorglub committed
314 315 316 317 318 319 320 321 322 323
    /* Some global changes happened -> Rebuild all */
    var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
    /* We went to the next item */
    var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
    /* One item has been updated */
    var_AddCallback( p_playlist, "item-change", ItemChanged, this );
    var_AddCallback( p_playlist, "item-append", ItemAppended, this );
    var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
}

zorglub's avatar
zorglub committed
324
void PLModel::delCallbacks()
zorglub's avatar
zorglub committed
325 326 327 328 329 330 331 332
{
    var_DelCallback( p_playlist, "item-change", ItemChanged, this );
    var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
    var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
    var_DelCallback( p_playlist, "item-append", ItemAppended, this );
    var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this );
}

zorglub's avatar
zorglub committed
333 334 335 336 337 338
void PLModel::activateItem( const QModelIndex &index )
{
    assert( index.isValid() );
    PLItem *item = static_cast<PLItem*>(index.internalPointer());
    assert( item );
    PL_LOCK;
339 340
    playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id,
                                                    VLC_TRUE);
zorglub's avatar
zorglub committed
341 342 343 344 345 346 347
    activateItem( p_item );
    PL_UNLOCK;
}
/* Must be entered with lock */
void PLModel::activateItem( playlist_item_t *p_item )
{
    if( !p_item ) return;
zorglub's avatar
zorglub committed
348 349 350 351 352 353 354
    playlist_item_t *p_parent = p_item;
    while( p_parent )
    {
        if( p_parent->i_id == rootItem->i_id ) break;
        p_parent = p_parent->p_parent;
    }
    if( p_parent )
355
        playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE, p_parent, p_item );
zorglub's avatar
zorglub committed
356
}
zorglub's avatar
zorglub committed
357

zorglub's avatar
zorglub committed
358
/****************** Base model mandatory implementations *****************/
zorglub's avatar
zorglub committed
359 360
QVariant PLModel::data(const QModelIndex &index, int role) const
{
zorglub's avatar
zorglub committed
361
    if(!index.isValid() ) return QVariant();
zorglub's avatar
zorglub committed
362
    PLItem *item = static_cast<PLItem*>(index.internalPointer());
zorglub's avatar
zorglub committed
363 364 365 366 367 368 369 370 371
    if( role == Qt::DisplayRole )
    {
        return QVariant( item->columnString( index.column() ) );
    }
    else if( role == Qt::DecorationRole && index.column() == 0  )
    {
        if( item->type >= 0 )
            return QVariant( PLModel::icons[item->type] );
    }
zorglub's avatar
Qt4:  
zorglub committed
372 373 374 375 376 377 378
    else if( role == Qt::FontRole )
    {
        if( item->current == true )
        {
            QFont f; f.setBold( true ); return QVariant( f );
        }
    }
zorglub's avatar
zorglub committed
379
    return QVariant();
zorglub's avatar
zorglub committed
380 381
}

zorglub's avatar
Qt4:  
zorglub committed
382 383 384 385 386 387
bool PLModel::isCurrent( const QModelIndex &index )
{
    assert( index.isValid() );
    return static_cast<PLItem*>(index.internalPointer())->current;
}

zorglub's avatar
zorglub committed
388
int PLModel::itemId( const QModelIndex &index ) const
zorglub's avatar
zorglub committed
389
{
zorglub's avatar
zorglub committed
390 391 392
    assert( index.isValid() );
    return static_cast<PLItem*>(index.internalPointer())->i_id;
}
zorglub's avatar
zorglub committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

QVariant PLModel::headerData( int section, Qt::Orientation orientation,
                              int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
            return QVariant( rootItem->columnString( section ) );
    return QVariant();
}

QModelIndex PLModel::index(int row, int column, const QModelIndex &parent)
                  const
{
    PLItem *parentItem;
    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<PLItem*>(parent.internalPointer());

    PLItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

/* Return the index of a given item */
zorglub's avatar
zorglub committed
419
QModelIndex PLModel::index( PLItem *item, int column ) const
zorglub's avatar
zorglub committed
420 421 422 423
{
    if( !item ) return QModelIndex();
    const PLItem *parent = item->parent();
    if( parent )
424 425
        return createIndex( parent->children.lastIndexOf( item ),
                            column, item );
zorglub's avatar
zorglub committed
426 427 428 429 430
    return QModelIndex();
}

QModelIndex PLModel::parent(const QModelIndex &index) const
{
zorglub's avatar
Qt4:  
zorglub committed
431
    if( !index.isValid() ) return QModelIndex();
zorglub's avatar
zorglub committed
432 433

    PLItem *childItem = static_cast<PLItem*>(index.internalPointer());
zorglub's avatar
zorglub committed
434
    if( !childItem ) { msg_Err( p_playlist, "NULL CHILD \n" ); return QModelIndex(); }
zorglub's avatar
zorglub committed
435
    PLItem *parentItem = childItem->parent();
zorglub's avatar
zorglub committed
436
    if( !parentItem || parentItem == rootItem ) return QModelIndex();
437 438
    if( ! parentItem->parentItem )
    {
zorglub's avatar
zorglub committed
439 440
        msg_Err( p_playlist, "No parent parent, trying row 0 " );
        msg_Err( p_playlist, "----- PLEASE REPORT THIS ------" );
441 442
        return createIndex( 0, 0, parentItem );
    }
zorglub's avatar
zorglub committed
443 444
    QModelIndex ind = createIndex(parentItem->row(), 0, parentItem);
    return ind;
zorglub's avatar
zorglub committed
445 446
}

zorglub's avatar
zorglub committed
447
int PLModel::columnCount( const QModelIndex &i) const
zorglub's avatar
zorglub committed
448
{
zorglub's avatar
zorglub committed
449
    if( i_depth == 1 ) return 1;
zorglub's avatar
zorglub committed
450
    return 3;
zorglub's avatar
zorglub committed
451 452
}

zorglub's avatar
zorglub committed
453
int PLModel::childrenCount(const QModelIndex &parent) const
zorglub's avatar
zorglub committed
454 455 456 457 458
{
    return rowCount( parent );
}

int PLModel::rowCount(const QModelIndex &parent) const
zorglub's avatar
zorglub committed
459 460 461 462 463 464 465 466 467 468 469
{
    PLItem *parentItem;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<PLItem*>(parent.internalPointer());

    return parentItem->childCount();
}

zorglub's avatar
zorglub committed
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
/************************* General playlist status ***********************/

bool PLModel::hasRandom()
{
    if( var_GetBool( p_playlist, "random" ) ) return true;
    return false;
}
bool PLModel::hasRepeat()
{
    if( var_GetBool( p_playlist, "repeat" ) ) return true;
    return false;
}
bool PLModel::hasLoop()
{
    if( var_GetBool( p_playlist, "loop" ) ) return true;
    return false;
}
void PLModel::setLoop( bool on )
{
    var_SetBool( p_playlist, "loop", on ? VLC_TRUE:VLC_FALSE );
zorglub's avatar
zorglub committed
490
    config_PutInt( p_playlist, "loop", on ? 1: 0 );
zorglub's avatar
zorglub committed
491 492 493 494
}
void PLModel::setRepeat( bool on )
{
    var_SetBool( p_playlist, "repeat", on ? VLC_TRUE:VLC_FALSE );
zorglub's avatar
zorglub committed
495
    config_PutInt( p_playlist, "repeat", on ? 1: 0 );
zorglub's avatar
zorglub committed
496 497 498 499
}
void PLModel::setRandom( bool on )
{
    var_SetBool( p_playlist, "random", on ? VLC_TRUE:VLC_FALSE );
zorglub's avatar
zorglub committed
500
    config_PutInt( p_playlist, "random", on ? 1: 0 );
zorglub's avatar
zorglub committed
501 502
}

zorglub's avatar
zorglub committed
503 504 505 506 507 508 509 510 511 512 513 514
/************************* Lookups *****************************/

PLItem *PLModel::FindById( PLItem *root, int i_id )
{
    return FindInner( root, i_id, false );
}

PLItem *PLModel::FindByInput( PLItem *root, int i_id )
{
    return FindInner( root, i_id, true );
}

zorglub's avatar
zorglub committed
515 516
#define CACHE( i, p ) { i_cached_id = i; p_cached_item = p; }
#define ICACHE( i, p ) { i_cached_input_id = i; p_cached_item_bi = p; }
zorglub's avatar
zorglub committed
517 518 519

PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input )
{
zorglub's avatar
zorglub committed
520
    if( ( !b_input && i_cached_id == i_id) ||
zorglub's avatar
zorglub committed
521
        ( b_input && i_cached_input_id ==i_id ) )
522
    {
zorglub's avatar
zorglub committed
523
        return b_input ? p_cached_item_bi : p_cached_item;
524
    }
zorglub's avatar
zorglub committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547

    if( !b_input && root->i_id == i_id )
    {
        CACHE( i_id, root );
        return root;
    }
    else if( b_input && root->i_input_id == i_id )
    {
        ICACHE( i_id, root );
        return root;
    }

    QList<PLItem *>::iterator it = root->children.begin();
    while ( it != root->children.end() )
    {
        if( !b_input && (*it)->i_id == i_id )
        {
            CACHE( i_id, (*it) );
            return p_cached_item;
        }
        else if( b_input && (*it)->i_input_id == i_id )
        {
            ICACHE( i_id, (*it) );
548
            return p_cached_item_bi;
zorglub's avatar
zorglub committed
549 550 551 552
        }
        if( (*it)->children.size() )
        {
            PLItem *childFound = FindInner( (*it), i_id, b_input );
zorglub's avatar
zorglub committed
553
            if( childFound )
zorglub's avatar
zorglub committed
554 555
            {
                if( b_input )
zorglub's avatar
zorglub committed
556
                    ICACHE( i_id, childFound )
zorglub's avatar
zorglub committed
557
                else
zorglub's avatar
zorglub committed
558
                    CACHE( i_id, childFound )
zorglub's avatar
zorglub committed
559
                return childFound;
zorglub's avatar
zorglub committed
560
            }
zorglub's avatar
zorglub committed
561
        }
zorglub's avatar
zorglub committed
562
        it++;
zorglub's avatar
zorglub committed
563 564 565 566 567 568 569 570 571 572
    }
    return NULL;
}
#undef CACHE
#undef ICACHE


/************************* Updates handling *****************************/
void PLModel::customEvent( QEvent *event )
{
zorglub's avatar
zorglub committed
573 574
    int type = event->type();
    if( type != ItemUpdate_Type && type != ItemAppend_Type &&
zorglub's avatar
zorglub committed
575
        type != ItemDelete_Type && type != PLUpdate_Type )
zorglub's avatar
zorglub committed
576 577
        return;

zorglub's avatar
zorglub committed
578
    PLEvent *ple = static_cast<PLEvent *>(event);
zorglub's avatar
zorglub committed
579

zorglub's avatar
zorglub committed
580
    if( type == ItemUpdate_Type )
zorglub's avatar
zorglub committed
581
        ProcessInputItemUpdate( ple->i_id );
zorglub's avatar
zorglub committed
582
    else if( type == ItemAppend_Type )
zorglub's avatar
zorglub committed
583
        ProcessItemAppend( ple->p_add );
zorglub's avatar
zorglub committed
584
    else if( type == ItemDelete_Type )
zorglub's avatar
zorglub committed
585
        ProcessItemRemoval( ple->i_id );
zorglub's avatar
zorglub committed
586 587
    else
        rebuild();
zorglub's avatar
zorglub committed
588 589
}

zorglub's avatar
zorglub committed
590
/**** Events processing ****/
zorglub's avatar
zorglub committed
591 592
void PLModel::ProcessInputItemUpdate( int i_input_id )
{
zorglub's avatar
zorglub committed
593 594
    if( i_input_id <= 0 ) return;
    PLItem *item = FindByInput( rootItem, i_input_id );
zorglub's avatar
zorglub committed
595 596
    if( item )
        UpdateTreeItem( item, true );
zorglub's avatar
zorglub committed
597 598 599 600
}

void PLModel::ProcessItemRemoval( int i_id )
{
zorglub's avatar
zorglub committed
601
    if( i_id <= 0 ) return;
zorglub's avatar
zorglub committed
602 603
    if( i_id == i_cached_id ) i_cached_id = -1;
    i_cached_input_id = -1;
zorglub's avatar
Qt4:  
zorglub committed
604 605 606
    PLItem *item = FindById( rootItem, i_id );
    if( item )
        item->remove( item );
zorglub's avatar
zorglub committed
607 608 609 610 611
}

void PLModel::ProcessItemAppend( playlist_add_t *p_add )
{
    playlist_item_t *p_item = NULL;
zorglub's avatar
zorglub committed
612
    PLItem *newItem = NULL;
zorglub's avatar
zorglub committed
613 614 615 616
    i_items_to_append--;
    if( b_need_update ) return;

    PLItem *nodeItem = FindById( rootItem, p_add->i_node );
zorglub's avatar
zorglub committed
617
    PL_LOCK;
zorglub's avatar
zorglub committed
618 619
    if( !nodeItem ) goto end;

620
    p_item = playlist_ItemGetById( p_playlist, p_add->i_item, VLC_TRUE );
zorglub's avatar
zorglub committed
621
    if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end;
zorglub's avatar
zorglub committed
622 623 624 625
    if( i_depth == 1 && p_item->p_parent &&
                        p_item->p_parent->i_id != rootItem->i_id )
        goto end;

zorglub's avatar
zorglub committed
626 627 628
    newItem = new PLItem( p_item, nodeItem, this );
    nodeItem->appendChild( newItem );
    UpdateTreeItem( p_item, newItem, true );
zorglub's avatar
zorglub committed
629
end:
zorglub's avatar
Qt4:  
zorglub committed
630
    PL_UNLOCK;
zorglub's avatar
zorglub committed
631 632
    return;
}
zorglub's avatar
zorglub committed
633

zorglub's avatar
zorglub committed
634 635 636 637 638 639 640

void PLModel::rebuild()
{
    rebuild( NULL );
}

void PLModel::rebuild( playlist_item_t *p_root )
zorglub's avatar
zorglub committed
641 642 643 644 645 646
{
    /* Remove callbacks before locking to avoid deadlocks */
    delCallbacks();
    /* Invalidate cache */
    i_cached_id = i_cached_input_id = -1;

zorglub's avatar
Qt4:  
zorglub committed
647
    PL_LOCK;
zorglub's avatar
zorglub committed
648
    /* Clear the tree */
zorglub's avatar
zorglub committed
649 650 651 652 653
    if( rootItem )
    {
        beginRemoveRows( index( rootItem, 0 ), 0,
                         rootItem->children.size() -1 );
        qDeleteAll( rootItem->children );
zorglub's avatar
zorglub committed
654
        rootItem->children.clear();
zorglub's avatar
zorglub committed
655 656 657 658 659 660 661 662
        endRemoveRows();
    }
    if( p_root )
    {
        //if( rootItem ) delete rootItem;
        rootItem = new PLItem( p_root, NULL, this );
        rootItem->strings[0] = qtr("Name");
        rootItem->strings[1] = qtr("Artist");
zorglub's avatar
zorglub committed
663
        rootItem->strings[2] = qtr("Duration");
zorglub's avatar
zorglub committed
664 665
    }
    assert( rootItem );
zorglub's avatar
zorglub committed
666 667
    /* Recreate from root */
    UpdateNodeChildren( rootItem );
zorglub's avatar
zorglub committed
668 669 670 671 672 673 674 675 676 677
    if( p_playlist->status.p_item )
    {
        PLItem *currentItem = FindByInput( rootItem,
                                     p_playlist->status.p_item->p_input->i_id );
        if( currentItem )
        {
            UpdateTreeItem( p_playlist->status.p_item, currentItem,
                            true, false );
        }
    }
zorglub's avatar
Qt4:  
zorglub committed
678
    PL_UNLOCK;
zorglub's avatar
zorglub committed
679 680 681 682 683 684

    /* And signal the view */
    emit layoutChanged();
    addCallbacks();
}

zorglub's avatar
Qt4:  
zorglub committed
685
/* This function must be entered WITH the playlist lock */
zorglub's avatar
zorglub committed
686 687
void PLModel::UpdateNodeChildren( PLItem *root )
{
688 689
    playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id,
                                                    VLC_TRUE );
zorglub's avatar
zorglub committed
690 691 692
    UpdateNodeChildren( p_node, root );
}

zorglub's avatar
Qt4:  
zorglub committed
693
/* This function must be entered WITH the playlist lock */
zorglub's avatar
zorglub committed
694 695 696 697
void PLModel::UpdateNodeChildren( playlist_item_t *p_node, PLItem *root )
{
    for( int i = 0; i < p_node->i_children ; i++ )
    {
zorglub's avatar
zorglub committed
698
        if( p_node->pp_children[i]->i_flags & PLAYLIST_DBL_FLAG ) continue;
zorglub's avatar
zorglub committed
699 700
        PLItem *newItem =  new PLItem( p_node->pp_children[i], root, this );
        root->appendChild( newItem, false );
zorglub's avatar
zorglub committed
701 702
        UpdateTreeItem( newItem, false, true );
        if( i_depth != 1 && p_node->pp_children[i]->i_children != -1 )
zorglub's avatar
zorglub committed
703 704 705 706
            UpdateNodeChildren( p_node->pp_children[i], newItem );
    }
}

zorglub's avatar
Qt4:  
zorglub committed
707
/* This function must be entered WITH the playlist lock */
zorglub's avatar
zorglub committed
708
void PLModel::UpdateTreeItem( PLItem *item, bool signal, bool force )
zorglub's avatar
zorglub committed
709
{
710 711
    playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id,
                                                    VLC_TRUE );
zorglub's avatar
zorglub committed
712
    UpdateTreeItem( p_item, item, signal, force );
zorglub's avatar
zorglub committed
713 714
}

zorglub's avatar
Qt4:  
zorglub committed
715
/* This function must be entered WITH the playlist lock */
zorglub's avatar
zorglub committed
716 717
void PLModel::UpdateTreeItem( playlist_item_t *p_item, PLItem *item,
                              bool signal, bool force )
zorglub's avatar
zorglub committed
718
{
zorglub's avatar
zorglub committed
719
    if( !force && i_depth == 1 && p_item->p_parent &&
zorglub's avatar
zorglub committed
720
                                 p_item->p_parent->i_id != rootItem->i_id )
zorglub's avatar
zorglub committed
721
        return;
zorglub's avatar
Qt4:  
zorglub committed
722
    item->update( p_item, p_item == p_playlist->status.p_item );
zorglub's avatar
zorglub committed
723
    if( signal )
zorglub's avatar
Qt4:  
zorglub committed
724
        emit dataChanged( index( item, 0 ) , index( item, 1 ) );
zorglub's avatar
zorglub committed
725
}
zorglub's avatar
zorglub committed
726

zorglub's avatar
zorglub committed
727 728
/************************* Actions ******************************/

729 730 731 732 733 734
void PLModel::sendArt( QString url )
{
    QString arturl = url.replace( "file://",QString("" ) );
    emit artSet( arturl );
}

735 736 737 738 739
void PLModel::removeArt()
{
    emit artSet( QString() );
}

zorglub's avatar
zorglub committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
/**
 * Deletion, here we have to do a ugly slow hack as we retrieve the full
 * list of indexes to delete at once: when we delete a node and all of
 * its children, we need to update the list.
 * Todo: investigate whethere we can use ranges to be sure to delete all items?
 */
void PLModel::doDelete( QModelIndexList selected )
{
    for( int i = selected.size() -1 ; i >= 0; i-- )
    {
        QModelIndex index = selected[i];
        if( index.column() != 0 ) continue;
        PLItem *item = static_cast<PLItem*>(index.internalPointer());
        if( item )
        {
            if( item->children.size() )
                recurseDelete( item->children, &selected );
            doDeleteItem( item, &selected );
        }
    }
}

void PLModel::recurseDelete( QList<PLItem*> children, QModelIndexList *fullList)
{
    for( int i = children.size() - 1; i >= 0 ; i-- )
    {
        PLItem *item = children[i];
        if( item->children.size() )
            recurseDelete( item->children, fullList );
        doDeleteItem( item, fullList );
    }
}

void PLModel::doDeleteItem( PLItem *item, QModelIndexList *fullList )
{
    QModelIndex deleteIndex = index( item, 0 );
    fullList->removeAll( deleteIndex );

    PL_LOCK;
779 780
    playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id,
                                                    VLC_TRUE );
zorglub's avatar
zorglub committed
781 782 783 784 785
    if( !p_item )
    {
        PL_UNLOCK; return;
    }
    if( p_item->i_children == -1 )
786
        playlist_DeleteFromInput( p_playlist, item->i_input_id, VLC_TRUE );
zorglub's avatar
zorglub committed
787 788 789 790 791 792 793
    else
        playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
    /* And finally, remove it from the tree */
    item->remove( item );
    PL_UNLOCK;
}

zorglub's avatar
zorglub committed
794 795 796 797 798
/******* Volume III: Sorting and searching ********/
void PLModel::sort( int column, Qt::SortOrder order )
{
    PL_LOCK;
    {
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
        playlist_item_t *p_root = playlist_ItemGetById( p_playlist,
                                                        rootItem->i_id,
                                                        VLC_TRUE );
        int i_mode;
        switch( column )
        {
        case 0: i_mode = SORT_TITLE_NODES_FIRST;break;
        case 1: i_mode = SORT_ARTIST;break;
        case 2: i_mode = SORT_DURATION; break;
        default: i_mode = SORT_TITLE_NODES_FIRST; break;
        }
        if( p_root )
            playlist_RecursiveNodeSort( p_playlist, p_root, i_mode,
                                        order == Qt::AscendingOrder ?
                                            ORDER_NORMAL : ORDER_REVERSE );
zorglub's avatar
zorglub committed
814 815 816 817 818
    }
    PL_UNLOCK
    rebuild();
}

zorglub's avatar
zorglub committed
819 820 821 822
void PLModel::search( QString search_text )
{
    /** \todo Fire the search with a small delay ? */
    PL_LOCK;
823 824 825 826 827 828 829 830
    {
        playlist_item_t *p_root = playlist_ItemGetById( p_playlist,
                                                        rootItem->i_id,
                                                        VLC_TRUE );
        assert( p_root );
        char *psz_name = search_text.toUtf8().data();
        playlist_LiveSearchUpdate( p_playlist , p_root, psz_name );
    }
zorglub's avatar
zorglub committed
831 832 833 834
    PL_UNLOCK;
    rebuild();
}

zorglub's avatar
zorglub committed
835 836 837 838 839 840
/*********** Popup *********/
void PLModel::popup( QModelIndex & index, QPoint &point, QModelIndexList list )
{
    assert( index.isValid() );
    PL_LOCK;
    playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
841
                                                    itemId( index ), VLC_TRUE );
zorglub's avatar
zorglub committed
842 843 844 845 846 847 848
    if( p_item )
    {
        i_popup_item = p_item->i_id;
        i_popup_parent = p_item->p_parent ? p_item->p_parent->i_id : -1;
        PL_UNLOCK;
        current_selection = list;
        QMenu *menu = new QMenu;
849 850
        menu->addAction( qfu(I_POP_PLAY), this, SLOT( popupPlay() ) );
        menu->addAction( qfu(I_POP_DEL), this, SLOT( popupDel() ) );
zorglub's avatar
zorglub committed
851 852 853 854
        menu->addSeparator();
        menu->addAction( qfu(I_POP_STREAM), this, SLOT( popupStream() ) );
        menu->addAction( qfu(I_POP_SAVE), this, SLOT( popupSave() ) );
        menu->addSeparator();
855
        menu->addAction( qfu(I_POP_INFO), this, SLOT( popupInfo() ) );
zorglub's avatar
zorglub committed
856 857 858
        if( p_item->i_children > -1 )
        {
            menu->addSeparator();
859 860
            menu->addAction( qfu(I_POP_SORT), this, SLOT( popupSort() ) );
            menu->addAction( qfu(I_POP_ADD), this, SLOT( popupAdd() ) );
zorglub's avatar
zorglub committed
861 862 863 864 865 866 867 868 869 870 871 872 873 874
        }
        menu->popup( point );
    }
    else
        PL_UNLOCK;
}

void PLModel::popupDel()
{
    doDelete( current_selection );
}
void PLModel::popupPlay()
{
    PL_LOCK;
875 876 877 878 879
    {
        playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
                                                        i_popup_item,VLC_TRUE );
        activateItem( p_item );
    }
zorglub's avatar
zorglub committed
880 881 882
    PL_UNLOCK;
}

zorglub's avatar
zorglub committed
883 884
void PLModel::popupInfo()
{
zorglub's avatar
zorglub committed
885 886 887 888 889 890 891 892
    playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
                                                    i_popup_item,VLC_TRUE );
    if( p_item )
    {
        MediaInfoDialog *mid = new MediaInfoDialog( p_intf );
        mid->setInput( p_item->p_input );
        mid->show();
    }
zorglub's avatar
zorglub committed
893 894 895 896 897 898 899 900 901 902 903
}

void PLModel::popupStream()
{
    fprintf( stderr, "Stream not implemented\n" );
}
void PLModel::popupSave()
{
    fprintf( stderr, "Save not implemented\n" );
}

zorglub's avatar
zorglub committed
904
/**********************************************************************
zorglub's avatar
zorglub committed
905
 * Playlist callbacks
zorglub's avatar
zorglub committed
906 907 908 909 910
 **********************************************************************/
static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
                            vlc_value_t oval, vlc_value_t nval, void *param )
{
    PLModel *p_model = (PLModel *) param;
zorglub's avatar
zorglub committed
911 912
    PLEvent *event = new PLEvent( PLUpdate_Type, 0 );
    QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
913
    printf( "blabla" );
zorglub's avatar
zorglub committed
914 915 916 917 918 919 920 921 922 923 924
    return VLC_SUCCESS;
}

static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
                         vlc_value_t oval, vlc_value_t nval, void *param )
{
    PLModel *p_model = (PLModel *) param;
    PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int );
    QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
    event = new PLEvent( ItemUpdate_Type, nval.i_int );
    QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
925
    printf( "blabla" );
zorglub's avatar
zorglub committed
926 927 928 929 930 931 932
    return VLC_SUCCESS;
}

static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
                        vlc_value_t oval, vlc_value_t nval, void *param )
{
    PLModel *p_model = (PLModel *) param;
zorglub's avatar
zorglub committed
933
    PLEvent *event = new PLEvent( ItemUpdate_Type, nval.i_int );
zorglub's avatar
zorglub committed
934
    QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
935
    printf( "blabla" );
zorglub's avatar
zorglub committed
936 937 938 939 940 941 942 943 944
    return VLC_SUCCESS;
}

static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
                        vlc_value_t oval, vlc_value_t nval, void *param )
{
    PLModel *p_model = (PLModel *) param;
    PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int );
    QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
945
    printf( "blabla" );
zorglub's avatar
zorglub committed
946 947 948 949 950 951 952 953 954 955 956 957
    return VLC_SUCCESS;
}

static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
                         vlc_value_t oval, vlc_value_t nval, void *param )
{
    PLModel *p_model = (PLModel *) param;
    playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t));
    memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );

    if( ++p_model->i_items_to_append >= 50 )
    {
zorglub's avatar
zorglub committed
958 959
//        p_model->b_need_update = VLC_TRUE;
//        return VLC_SUCCESS;
zorglub's avatar
zorglub committed
960 961 962
    }
    PLEvent *event = new PLEvent(  p_add );
    QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
963
    printf( "blabla" );
zorglub's avatar
zorglub committed
964 965
    return VLC_SUCCESS;
}