]> git.sesse.net Git - vlc/blob - modules/gui/qt4/playlist_model.cpp
WTF isn't QEvent a QObject ?
[vlc] / modules / gui / qt4 / playlist_model.cpp
1 /*****************************************************************************
2  * input_manager.cpp : Manage an input and interact with its GUI elements
3  ****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
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     {
259         return b_input ? p_cached_item_bi : p_cached_item;
260     }
261
262     if( !b_input && root->i_id == i_id )
263     {
264         CACHE( i_id, root );
265         return root;
266     }
267     else if( b_input && root->i_input_id == i_id )
268     {
269         ICACHE( i_id, root );
270         return root;
271     }
272
273     QList<PLItem *>::iterator it = root->children.begin();
274     while ( it != root->children.end() )
275     {
276         if( !b_input && (*it)->i_id == i_id )
277         {
278             CACHE( i_id, (*it) );
279             return p_cached_item;
280         }
281         else if( b_input && (*it)->i_input_id == i_id )
282         {
283             ICACHE( i_id, (*it) );
284             return p_cached_item_bi;
285         }
286         if( (*it)->children.size() )
287         {
288             PLItem *childFound = FindInner( (*it), i_id, b_input );
289             if( childFound ) 
290             {
291                 if( b_input )
292                 {
293                     ICACHE( i_id, childFound );
294                 }
295                 else
296                 {
297                     CACHE( i_id, childFound );
298                 }
299                 return childFound;
300             } 
301         }
302         it++;
303     }
304     fprintf( stderr, "Never found" );
305     return NULL;
306 }
307 #undef CACHE
308 #undef ICACHE
309
310
311 /************************* Updates handling *****************************/
312 void PLModel::customEvent( QEvent *event )
313 {
314     PLEvent *ple = static_cast<PLEvent *>(event);
315
316     if( event->type() == ItemUpdate_Type )
317         ProcessInputItemUpdate( ple->i_id );
318     else if( event->type() == ItemAppend_Type )
319         ProcessItemAppend( ple->p_add );
320     else
321         ProcessItemRemoval( ple->i_id );
322 }
323
324 /**** Events processing ****/
325 void PLModel::ProcessInputItemUpdate( int i_input_id )
326 {
327     if( i_input_id <= 0 ) return;
328     PLItem *item = FindByInput( rootItem, i_input_id );
329     fprintf( stderr, "Updating %i -> %p \n", i_input_id, item );
330     UpdateTreeItem( item, true );
331 }
332
333 void PLModel::ProcessItemRemoval( int i_id )
334 {
335     if( i_id <= 0 ) return;
336     if( i_id == i_cached_id ) i_cached_id = -1;
337     i_cached_input_id = -1;
338
339     /// \todo
340 }
341
342 void PLModel::ProcessItemAppend( playlist_add_t *p_add )
343 {
344     playlist_item_t *p_item = NULL;
345     PLItem *newItem = NULL;
346     i_items_to_append--;
347     if( b_need_update ) return;
348
349     PLItem *nodeItem = FindById( rootItem, p_add->i_node );
350     if( !nodeItem ) goto end;
351
352     p_item = playlist_ItemGetById( p_playlist, p_add->i_item );
353     if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end;
354
355     newItem = new PLItem( p_item, nodeItem, this );
356     nodeItem->appendChild( newItem );
357     UpdateTreeItem( p_item, newItem, true );
358
359 end:
360     return;
361 }
362
363 void PLModel::Rebuild()
364 {
365     /* Remove callbacks before locking to avoid deadlocks */
366     delCallbacks();
367     PL_LOCK;
368
369     /* Invalidate cache */
370     i_cached_id = i_cached_input_id = -1;
371
372     /* Clear the tree */
373     qDeleteAll( rootItem->children );
374
375     /* Recreate from root */
376     UpdateNodeChildren( rootItem );
377
378     /* And signal the view */
379     emit layoutChanged();
380
381     addCallbacks();
382     PL_UNLOCK;
383 }
384
385 void PLModel::UpdateNodeChildren( PLItem *root )
386 {
387     playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id );
388     UpdateNodeChildren( p_node, root );
389 }
390
391 void PLModel::UpdateNodeChildren( playlist_item_t *p_node, PLItem *root )
392 {
393     for( int i = 0; i < p_node->i_children ; i++ )
394     {
395         PLItem *newItem =  new PLItem( p_node->pp_children[i], root, this );
396         fprintf( stderr, "New %p\n", newItem );
397         root->appendChild( newItem, false );
398         UpdateTreeItem( newItem, false );
399         if( p_node->pp_children[i]->i_children != -1 )
400             UpdateNodeChildren( p_node->pp_children[i], newItem );
401     }
402 }
403
404 void PLModel::UpdateTreeItem( PLItem *item, bool signal )
405 {
406     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
407     UpdateTreeItem( p_item, item, signal );
408 }
409
410 void PLModel::UpdateTreeItem( playlist_item_t *p_item, PLItem *item, bool signal ) 
411 {
412     /// \todo
413     fprintf( stderr, "Updating item %s\n", p_item->p_input->psz_name );
414     item->update( p_item );
415     if( signal )
416     {    // emit 
417     }
418 }
419         
420 /**********************************************************************
421  * Playlist callbacks 
422  **********************************************************************/
423 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
424                             vlc_value_t oval, vlc_value_t nval, void *param )
425 {
426     PLModel *p_model = (PLModel *) param;
427     p_model->b_need_update = VLC_TRUE;
428     return VLC_SUCCESS;
429 }
430
431 static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
432                          vlc_value_t oval, vlc_value_t nval, void *param )
433 {
434     PLModel *p_model = (PLModel *) param;
435     PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int );
436     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
437     event = new PLEvent( ItemUpdate_Type, nval.i_int );
438     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
439     return VLC_SUCCESS;
440 }
441
442 static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
443                         vlc_value_t oval, vlc_value_t nval, void *param )
444 {
445     PLModel *p_model = (PLModel *) param;
446     PLEvent *event = new PLEvent( ItemUpdate_Type, nval.i_int ); 
447     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
448     return VLC_SUCCESS;
449 }
450
451 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
452                         vlc_value_t oval, vlc_value_t nval, void *param )
453 {
454     PLModel *p_model = (PLModel *) param;
455     PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int );
456     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
457     return VLC_SUCCESS;
458 }
459
460 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
461                          vlc_value_t oval, vlc_value_t nval, void *param )
462 {
463     PLModel *p_model = (PLModel *) param;
464     playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t));
465     memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );
466
467     if( ++p_model->i_items_to_append >= 50 )
468     {
469         p_model->b_need_update = VLC_TRUE;
470         return VLC_SUCCESS;
471     }
472     PLEvent *event = new PLEvent(  p_add );
473     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
474     return VLC_SUCCESS;
475 }