f5810dd33450c7d0d91a07ef4783cdfa0a430643
[vlc.git] / modules / gui / qt4 / playlist_model.cpp
1 /*****************************************************************************
2  * input_manager.cpp : Manage an input and interact with its GUI elements
3  ****************************************************************************
4  * Copyright (C) 2000-2005 the VideoLAN team
5  * $Id: wxwidgets.cpp 15731 2006-05-25 14:43:53Z zorglub $
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include <QApplication>
25 #include "playlist_model.hpp"
26 #include <assert.h>
27
28
29 static int PlaylistChanged( vlc_object_t *, const char *,
30                             vlc_value_t, vlc_value_t, void * );
31 static int PlaylistNext( vlc_object_t *, const char *,
32                          vlc_value_t, vlc_value_t, void * );
33 static int ItemChanged( vlc_object_t *, const char *,
34                         vlc_value_t, vlc_value_t, void * );
35 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
36                          vlc_value_t oval, vlc_value_t nval, void *param );
37 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
38                         vlc_value_t oval, vlc_value_t nval, void *param );
39     
40
41 /*************************************************************************
42  * Playlist item implementation
43  *************************************************************************/
44
45 /**
46  * Column strings
47  *      Title
48  *      Artist
49  *      Duration
50  */
51
52 void PLItem::init( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
53 {
54     parentItem = parent;
55     i_id = _i_id; i_input_id = _i_input_id;
56     model = m;
57     strings.append( "" );    
58     strings.append( "" );    
59     strings.append( "" );    
60 }
61
62 PLItem::PLItem( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
63 {
64     init( _i_id, _i_input_id, parent, m );
65 }
66
67 PLItem::PLItem( playlist_item_t * p_item, PLItem *parent, PLModel *m )
68 {
69     init( p_item->i_id, p_item->p_input->i_id, parent, m );
70 }
71
72 PLItem::~PLItem()
73 {
74     qDeleteAll(children);
75 }
76
77 void PLItem::insertChild( PLItem *item, int i_pos, bool signal )
78 {
79     assert( model );
80     fprintf( stderr, "Inserting child \n" );
81     if( signal )
82         model->beginInsertRows( model->index( this , 0 ), i_pos, i_pos );
83     children.append( item );
84     if( signal )
85         model->endInsertRows();
86 }
87
88 int PLItem::row() const
89 {
90     if (parentItem)
91         return parentItem->children.indexOf(const_cast<PLItem*>(this));
92     return 0;
93 }
94
95 void PLItem::update( playlist_item_t *p_item )
96 {
97     assert( p_item->p_input->i_id == i_input_id );
98     strings[0] = QString::fromUtf8( p_item->p_input->psz_name );
99 }
100
101 /*************************************************************************
102  * Playlist model implementation
103  *************************************************************************/
104
105 PLModel::PLModel( playlist_t *_p_playlist,
106                   playlist_item_t * p_root, int i_depth, QObject *parent)
107                                     : QAbstractItemModel(parent)
108 {
109      rootItem = NULL;
110         rootItem = new PLItem( p_root, NULL, this );
111     fprintf( stderr, "%i -> %i, %i -> %i", p_root->i_id, rootItem->i_id, p_root->p_input->i_id, rootItem->i_input_id );
112     p_playlist= _p_playlist;
113     i_items_to_append = 0;
114     b_need_update     = false;
115     i_cached_id       = -1;
116     i_cached_input_id = -1;
117
118     addCallbacks();
119 }
120
121 PLModel::~PLModel()
122 {
123     delCallbacks();
124     delete rootItem;
125 }
126
127 void PLModel::addCallbacks()
128 {
129     /* Some global changes happened -> Rebuild all */
130     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
131     /* We went to the next item */
132     var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
133     /* One item has been updated */
134     var_AddCallback( p_playlist, "item-change", ItemChanged, this );
135     var_AddCallback( p_playlist, "item-append", ItemAppended, this );
136     var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
137 }
138
139 void PLModel::delCallbacks()
140 {
141     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
142     var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
143     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
144     var_DelCallback( p_playlist, "item-append", ItemAppended, this );
145     var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this );
146 }
147
148 /****************** Base model mandatory implementations *****************/
149
150 QVariant PLModel::data(const QModelIndex &index, int role) const
151 {
152     if (!index.isValid())
153         return QVariant();
154     if (role != Qt::DisplayRole)
155         return QVariant();
156
157     PLItem *item = static_cast<PLItem*>(index.internalPointer());
158     return QVariant( item->columnString( index.column() ) );
159 }
160
161 Qt::ItemFlags PLModel::flags(const QModelIndex &index) const
162 {
163     if (!index.isValid())
164         return Qt::ItemIsEnabled;
165
166     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
167 }
168
169 QVariant PLModel::headerData( int section, Qt::Orientation orientation,
170                               int role) const
171 {
172     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
173             return QVariant( rootItem->columnString( section ) );
174     return QVariant();
175 }
176
177 QModelIndex PLModel::index(int row, int column, const QModelIndex &parent)
178                   const
179 {
180     PLItem *parentItem;
181     if (!parent.isValid())
182         parentItem = rootItem;
183     else
184         parentItem = static_cast<PLItem*>(parent.internalPointer());
185
186     PLItem *childItem = parentItem->child(row);
187     if (childItem)
188         return createIndex(row, column, childItem);
189     else
190         return QModelIndex();
191 }
192
193 /* Return the index of a given item */
194 QModelIndex PLModel::index( PLItem *item, int column ) const 
195 {
196     if( !item ) return QModelIndex();
197     const PLItem *parent = item->parent();
198     if( parent )
199         return createIndex( parent->children.lastIndexOf( item ), column, item );
200     return QModelIndex();
201 }
202
203 QModelIndex PLModel::parent(const QModelIndex &index) const
204 {
205     if (!index.isValid())
206         return QModelIndex();
207
208     PLItem *childItem = static_cast<PLItem*>(index.internalPointer());
209     PLItem *parentItem = childItem->parent();
210
211     if (parentItem == rootItem)
212         return QModelIndex();
213
214     return createIndex(parentItem->row(), 0, parentItem);
215 }
216
217 int PLModel::columnCount( const QModelIndex &i) const 
218 {
219     return 1;
220 }
221
222 int PLModel::childrenCount(const QModelIndex &parent) const
223 {
224     return rowCount( parent );
225 }
226
227 int PLModel::rowCount(const QModelIndex &parent) const
228 {
229     PLItem *parentItem;
230
231     if (!parent.isValid())
232         parentItem = rootItem;
233     else
234         parentItem = static_cast<PLItem*>(parent.internalPointer());
235
236     return parentItem->childCount();
237 }
238
239 /************************* Lookups *****************************/
240
241 PLItem *PLModel::FindById( PLItem *root, int i_id )
242 {
243     return FindInner( root, i_id, false );
244 }
245
246 PLItem *PLModel::FindByInput( PLItem *root, int i_id )
247 {
248     return FindInner( root, i_id, true );
249 }
250
251 #define CACHE( i, p ) i_cached_id = i; p_cached_item = p;
252 #define ICACHE( i, p ) i_cached_input_id = i; p_cached_item_bi = p;
253
254 PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input )
255 {
256     if( ( !b_input && i_cached_id == i_id) || 
257         ( b_input && i_cached_input_id ==i_id ) )
258         return b_input ? p_cached_item_bi : p_cached_item;
259
260     if( !b_input && root->i_id == i_id )
261     {
262         CACHE( i_id, root );
263         return root;
264     }
265     else if( b_input && root->i_input_id == i_id )
266     {
267         ICACHE( i_id, root );
268         return root;
269     }
270
271     QList<PLItem *>::iterator it = root->children.begin();
272     while ( it != root->children.end() )
273     {
274         if( !b_input && (*it)->i_id == i_id )
275         {
276             CACHE( i_id, (*it) );
277             return p_cached_item;
278         }
279         else if( b_input && (*it)->i_input_id == i_id )
280         {
281             ICACHE( i_id, (*it) );
282         }
283         if( (*it)->children.size() )
284         {
285             PLItem *childFound = FindInner( (*it), i_id, b_input );
286             if( childFound ) 
287             {
288                 if( b_input )
289                 {
290                     ICACHE( i_id, childFound );
291                 }
292                 else
293                 {
294                     CACHE( i_id, childFound );
295                 }
296                 return childFound;
297             } 
298         }
299         it++;
300     }
301     return NULL;
302 }
303 #undef CACHE
304 #undef ICACHE
305
306
307 /************************* Updates handling *****************************/
308 void PLModel::customEvent( QEvent *event )
309 {
310     PLEvent *ple = dynamic_cast<PLEvent *>(event);
311
312     if( event->type() == ItemUpdate_Type )
313         ProcessInputItemUpdate( ple->i_id );
314     else if( event->type() == ItemAppend_Type )
315         ProcessItemAppend( ple->p_add );
316     else
317         ProcessItemRemoval( ple->i_id );
318 }
319
320 /**** Events processing ****/
321 void PLModel::ProcessInputItemUpdate( int i_input_id )
322 {
323     if( i_input_id <= 0 ) return;
324     PLItem *item = FindByInput( rootItem, i_input_id );
325     fprintf( stderr, "Updating %i -> %p \n", i_input_id, item );
326     UpdateTreeItem( item, true );
327 }
328
329 void PLModel::ProcessItemRemoval( int i_id )
330 {
331     if( i_id <= 0 ) return;
332     if( i_id == i_cached_id ) i_cached_id = -1;
333     i_cached_input_id = -1;
334
335     /// \todo
336 }
337
338 void PLModel::ProcessItemAppend( playlist_add_t *p_add )
339 {
340     playlist_item_t *p_item = NULL;
341     PLItem *newItem = NULL;
342     i_items_to_append--;
343     if( b_need_update ) return;
344
345     PLItem *nodeItem = FindById( rootItem, p_add->i_node );
346     if( !nodeItem ) goto end;
347
348     p_item = playlist_ItemGetById( p_playlist, p_add->i_item );
349     if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end;
350
351     newItem = new PLItem( p_item, nodeItem, this );
352     nodeItem->appendChild( newItem );
353     UpdateTreeItem( p_item, newItem, true );
354
355 end:
356     return;
357 }
358
359 void PLModel::Rebuild()
360 {
361     /* Remove callbacks before locking to avoid deadlocks */
362     delCallbacks();
363     PL_LOCK;
364
365     /* Invalidate cache */
366     i_cached_id = i_cached_input_id = -1;
367
368     /* Clear the tree */
369     qDeleteAll( rootItem->children );
370
371     /* Recreate from root */
372     UpdateNodeChildren( rootItem );
373
374     /* And signal the view */
375     emit layoutChanged();
376
377     addCallbacks();
378     PL_UNLOCK;
379 }
380
381 void PLModel::UpdateNodeChildren( PLItem *root )
382 {
383     playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id );
384     UpdateNodeChildren( p_node, root );
385 }
386
387 void PLModel::UpdateNodeChildren( playlist_item_t *p_node, PLItem *root )
388 {
389     for( int i = 0; i < p_node->i_children ; i++ )
390     {
391         PLItem *newItem =  new PLItem( p_node->pp_children[i], root, this );
392         fprintf( stderr, "New %p\n", newItem );
393         root->appendChild( newItem, false );
394         UpdateTreeItem( newItem, false );
395         if( p_node->pp_children[i]->i_children != -1 )
396             UpdateNodeChildren( p_node->pp_children[i], newItem );
397     }
398 }
399
400 void PLModel::UpdateTreeItem( PLItem *item, bool signal )
401 {
402     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
403     UpdateTreeItem( p_item, item, signal );
404 }
405
406 void PLModel::UpdateTreeItem( playlist_item_t *p_item, PLItem *item, bool signal ) 
407 {
408     /// \todo
409     fprintf( stderr, "Updating item %s\n", p_item->p_input->psz_name );
410     item->update( p_item );
411     if( signal )
412     {    // emit 
413     }
414 }
415         
416 /**********************************************************************
417  * Playlist callbacks 
418  **********************************************************************/
419 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
420                             vlc_value_t oval, vlc_value_t nval, void *param )
421 {
422     PLModel *p_model = (PLModel *) param;
423     p_model->b_need_update = VLC_TRUE;
424     return VLC_SUCCESS;
425 }
426
427 static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
428                          vlc_value_t oval, vlc_value_t nval, void *param )
429 {
430     PLModel *p_model = (PLModel *) param;
431     PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int );
432     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
433     event = new PLEvent( ItemUpdate_Type, nval.i_int );
434     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
435     return VLC_SUCCESS;
436 }
437
438 static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
439                         vlc_value_t oval, vlc_value_t nval, void *param )
440 {
441     PLModel *p_model = (PLModel *) param;
442     PLEvent *event = new PLEvent( ItemUpdate_Type, nval.i_int ); 
443     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
444     return VLC_SUCCESS;
445 }
446
447 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
448                         vlc_value_t oval, vlc_value_t nval, void *param )
449 {
450     PLModel *p_model = (PLModel *) param;
451     PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int );
452     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
453     return VLC_SUCCESS;
454 }
455
456 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
457                          vlc_value_t oval, vlc_value_t nval, void *param )
458 {
459     PLModel *p_model = (PLModel *) param;
460     playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t));
461     memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );
462
463     if( ++p_model->i_items_to_append >= 50 )
464     {
465         p_model->b_need_update = VLC_TRUE;
466         return VLC_SUCCESS;
467     }
468     PLEvent *event = new PLEvent(  p_add );
469     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
470     return VLC_SUCCESS;
471 }