]> git.sesse.net Git - vlc/blob - modules/gui/qt4/playlist_model.cpp
Playlist model
[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) 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 PLItem::PLItem( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
46 {
47     parentItem = parent;
48     i_id = _i_id; i_input_id = _i_input_id;
49     model = m;
50 }
51
52 PLItem::PLItem( playlist_item_t * p_item, PLItem *parent, PLModel *m )
53 {
54     i_id = p_item->i_id;
55     i_input_id = p_item->p_input->i_id;
56     parentItem = parent;
57     model = m;
58 }
59
60 PLItem::~PLItem()
61 {
62     qDeleteAll(children);
63 }
64
65 void PLItem::insertChild( PLItem *item, int i_pos )
66 {
67     assert( model );
68     model->beginInsertRows( model->index( this , 0 ), i_pos, i_pos );
69     children.append( item );
70     model->endInsertRows();
71 }
72
73 int PLItem::row() const
74 {
75     if (parentItem)
76         return parentItem->children.indexOf(const_cast<PLItem*>(this));
77     return 0;
78 }
79
80
81 /*************************************************************************
82  * Playlist model implementation
83  *************************************************************************/
84
85 PLModel::PLModel( playlist_item_t * p_root, int i_depth, QObject *parent)
86                                     : QAbstractItemModel(parent)
87 {
88     rootItem = new PLItem( p_root, NULL, this );
89
90     i_items_to_append = 0;
91     b_need_update     = false;
92     i_cached_id       = -1;
93     i_cached_input_id = -1;
94
95     /* Some global changes happened -> Rebuild all */
96     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
97     /* We went to the next item */
98     var_AddCallback( p_playlist, "playlist-current", PlaylistNext, this );
99     /* One item has been updated */
100     var_AddCallback( p_playlist, "item-change", ItemChanged, this );
101     var_AddCallback( p_playlist, "item-append", ItemAppended, this );
102     var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this );
103 }
104
105 PLModel::~PLModel()
106 {
107     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
108     var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this );
109     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
110     var_DelCallback( p_playlist, "item-append", ItemAppended, this );
111     var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this );
112     delete rootItem;
113 }
114
115 /****************** Base model mandatory implementations *****************/
116 QVariant PLModel::data(const QModelIndex &index, int role) const
117 {
118     if (!index.isValid())
119         return QVariant();
120     if (role != Qt::DisplayRole)
121         return QVariant();
122
123     PLItem *item = static_cast<PLItem*>(index.internalPointer());
124     return QVariant( item->columnString( index.column() ) );
125 }
126
127 Qt::ItemFlags PLModel::flags(const QModelIndex &index) const
128 {
129     if (!index.isValid())
130         return Qt::ItemIsEnabled;
131
132     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
133 }
134
135 QVariant PLModel::headerData( int section, Qt::Orientation orientation,
136                               int role) const
137 {
138     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
139             return QVariant( rootItem->columnString( section ) );
140     return QVariant();
141 }
142
143 QModelIndex PLModel::index(int row, int column, const QModelIndex &parent)
144                   const
145 {
146     PLItem *parentItem;
147     if (!parent.isValid())
148         parentItem = rootItem;
149     else
150         parentItem = static_cast<PLItem*>(parent.internalPointer());
151
152     PLItem *childItem = parentItem->child(row);
153     if (childItem)
154         return createIndex(row, column, childItem);
155     else
156         return QModelIndex();
157 }
158
159 /* Return the index of a given item */
160 QModelIndex PLModel::index( PLItem *item, int column ) const 
161 {
162     if( !item ) return QModelIndex();
163     const PLItem *parent = item->parent();
164     if( parent )
165         return createIndex( parent->children.lastIndexOf( item ), column, item );
166     return QModelIndex();
167 }
168
169 QModelIndex PLModel::parent(const QModelIndex &index) const
170 {
171     if (!index.isValid())
172         return QModelIndex();
173
174     PLItem *childItem = static_cast<PLItem*>(index.internalPointer());
175     PLItem *parentItem = childItem->parent();
176
177     if (parentItem == rootItem)
178         return QModelIndex();
179
180     return createIndex(parentItem->row(), 0, parentItem);
181 }
182
183 int PLModel::childrenCount(const QModelIndex &parent) const
184 {
185     PLItem *parentItem;
186
187     if (!parent.isValid())
188         parentItem = rootItem;
189     else
190         parentItem = static_cast<PLItem*>(parent.internalPointer());
191
192     return parentItem->childCount();
193 }
194
195 /************************* Lookups *****************************/
196
197 PLItem *PLModel::FindById( PLItem *root, int i_id )
198 {
199     return FindInner( root, i_id, false );
200 }
201
202 PLItem *PLModel::FindByInput( PLItem *root, int i_id )
203 {
204     return FindInner( root, i_id, true );
205 }
206
207 #define CACHE( i, p ) i_cached_id = i; p_cached_item = p;
208 #define ICACHE( i, p ) i_cached_input_id = i; p_cached_item_bi = p;
209
210 PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input )
211 {
212     if( ( !b_input && i_cached_id == i_id) || 
213         ( b_input && i_cached_input_id ==i_id ) )
214         return b_input ? p_cached_item_bi : p_cached_item;
215
216     if( !b_input && root->i_id == i_id )
217     {
218         CACHE( i_id, root );
219         return root;
220     }
221     else if( b_input && root->i_input_id == i_id )
222     {
223         ICACHE( i_id, root );
224         return root;
225     }
226
227     QList<PLItem *>::iterator it = root->children.begin();
228     while ( it != root->children.end() )
229     {
230         if( !b_input && (*it)->i_id == i_id )
231         {
232             CACHE( i_id, (*it) );
233             return p_cached_item;
234         }
235         else if( b_input && (*it)->i_input_id == i_id )
236         {
237             ICACHE( i_id, (*it) );
238         }
239         if( (*it)->children.size() )
240         {
241             PLItem *childFound = FindInner( (*it), i_id, b_input );
242             if( childFound ) 
243             {
244                 if( b_input )
245                 {
246                     ICACHE( i_id, childFound );
247                 }
248                 else
249                 {
250                     CACHE( i_id, childFound );
251                 }
252                 return childFound;
253             } 
254         }
255     }
256     return NULL;
257 }
258 #undef CACHE
259 #undef ICACHE
260
261
262 /************************* Updates handling *****************************/
263 void PLModel::customEvent( QEvent *event )
264 {
265     PLEvent *ple = dynamic_cast<PLEvent *>(event);
266
267     if( event->type() == ItemUpdate_Type )
268         ProcessInputItemUpdate( ple->i_id );
269     else if( event->type() == ItemAppend_Type )
270         ProcessItemAppend( ple->p_add );
271     else
272         ProcessItemRemoval( ple->i_id );
273 }
274
275 void PLModel::ProcessInputItemUpdate( int i_input_id )
276 {
277     assert( i_input_id >= 0 );
278     /// \todo
279 }
280
281 void PLModel::ProcessItemRemoval( int i_id )
282 {
283     assert( i_id >= 0 );
284     if( i_id == i_cached_id ) i_cached_id = -1;
285     i_cached_input_id = -1;
286
287     /// \todo
288 }
289
290 void PLModel::ProcessItemAppend( playlist_add_t *p_add )
291 {
292     playlist_item_t *p_item = NULL;
293     i_items_to_append--;
294     if( b_need_update ) return;
295
296     PLItem *nodeItem = FindById( rootItem, p_add->i_node );
297     if( !nodeItem ) goto end;
298
299     p_item = playlist_ItemGetById( p_playlist, p_add->i_item );
300     if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end;
301
302     nodeItem->appendChild( new PLItem( p_item, nodeItem, this ) );
303
304
305 end:
306     return;
307 }
308         
309 /**********************************************************************
310  * Playlist callbacks 
311  **********************************************************************/
312 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
313                             vlc_value_t oval, vlc_value_t nval, void *param )
314 {
315     PLModel *p_model = (PLModel *) param;
316     p_model->b_need_update = VLC_TRUE;
317     return VLC_SUCCESS;
318 }
319
320 static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
321                          vlc_value_t oval, vlc_value_t nval, void *param )
322 {
323     PLModel *p_model = (PLModel *) param;
324     PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int );
325     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
326     event = new PLEvent( ItemUpdate_Type, nval.i_int );
327     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
328     return VLC_SUCCESS;
329 }
330
331 static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
332                         vlc_value_t oval, vlc_value_t nval, void *param )
333 {
334     PLModel *p_model = (PLModel *) param;
335     PLEvent *event = new PLEvent( ItemUpdate_Type, nval.i_int ); 
336     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
337     return VLC_SUCCESS;
338 }
339
340 static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
341                         vlc_value_t oval, vlc_value_t nval, void *param )
342 {
343     PLModel *p_model = (PLModel *) param;
344     PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int );
345     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
346     return VLC_SUCCESS;
347 }
348
349 static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
350                          vlc_value_t oval, vlc_value_t nval, void *param )
351 {
352     PLModel *p_model = (PLModel *) param;
353     playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t));
354     memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) );
355
356     if( ++p_model->i_items_to_append >= 50 )
357     {
358         p_model->b_need_update = VLC_TRUE;
359         return VLC_SUCCESS;
360     }
361     PLEvent *event = new PLEvent(  p_add );
362     QApplication::postEvent( p_model, static_cast<QEvent*>(event) );
363     return VLC_SUCCESS;
364 }