1
/****************************************************************************************
2
 * Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org>                            *
3
 * Copyright (c) 2009 Ian Monroe <ian@monroe.nu>                                        *
4
 * Copyright (c) 2009 Max Howell <max@last.fm>                                          *
5
 *                                                                                      *
6
 * This program is free software; you can redistribute it and/or modify it under        *
7
 * the terms of the GNU General Public License as published by the Free Software        *
8
 * Foundation; either version 2 of the License, or (at your option) any later           *
9
 * version.                                                                             *
10
 *                                                                                      *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
12
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
13
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
14
 *                                                                                      *
15
 * You should have received a copy of the GNU General Public License along with         *
16
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
17
 ****************************************************************************************/
18
19
#ifndef AMAROK_SMART_POINTER_LIST_H
20
#define AMAROK_SMART_POINTER_LIST_H
21
22
#include <QList>   //baseclass
23
#include <QObject> //baseclass
24
25
26
class SmartPointerListDaddy : public QObject
27
{
28
    Q_OBJECT
29
    QList<QObject*>& m_list;
30
31
public:
32
    SmartPointerListDaddy( QList<QObject*>* list ) : m_list( *list )
33
    {}
34
35
private slots:
36
    void onDestroyed()
37
    {
38
        m_list.removeAll( sender() );
39
    }
40
};
41
42
/** QList has no virtual functions, so we inherit privately and define the
43
  * interface exactly to ensure users can't write code that breaks the 
44
  * class's internal behaviour.
45
  *
46
  * I deliberately didn't define clear. I worried people would assume it 
47
  * deleted the pointers. Or assume it didn't. I didn't expose a few other 
48
  * functions for that reason.
49
  *
50
  * non-const iterator functions are not exposed as they access the QList
51
  * baseclass, and then the Daddy wouldn't be watching newly inserted items.
52
  *
53
  * --mxcl
54
  * Exposed clear. This class doesn't have a QPtrList autodelete functionality
55
  * ever, so if people think that, they're really confused! -- Ian Monroe
56
  *
57
  */
58
template <class T> class SmartPointerList : private QList<T*>
59
{
60
    class SmartPointerListDaddy* m_daddy;
61
62
public:
63
    SmartPointerList() : m_daddy( new SmartPointerListDaddy( (QList<QObject*>*)this ) )
64
    {}
65
    
66
    ~SmartPointerList()
67
    {
68
        delete m_daddy;
69
    }
70
    
71
    SmartPointerList( const SmartPointerList<T>& that )
72
            : QList<T*>()
73
            , m_daddy( new SmartPointerListDaddy( (QList<QObject*>*)this ) )
74
    {
75
        QListIterator<T*> i( that );
76
        while (i.hasNext())
77
            append( i.next() );
78
    }
79
    
80
    SmartPointerList& operator=( const SmartPointerList<T>& that )
81
    {    
82
        QListIterator<T*> i( *this);
83
        while (i.hasNext())
84
            QObject::disconnect( m_daddy, 0, i.next(), 0 );
85
86
        QList<T*>::operator=( that );
87
        
88
        if (this != &that) {
89
            QListIterator<T*> i( that );
90
            while (i.hasNext())
91
                m_daddy->connect( i.next(), SIGNAL(destroyed()), SLOT(onDestroyed()) );
92
        }
93
            
94
        return *this;
95
    }
96
97
    // keep same function names as Qt
98
    void append( T* o )
99
    {
100
        m_daddy->connect( o, SIGNAL(destroyed()), SLOT(onDestroyed()) );
101
        QList<T*>::append( o );
102
    }
103
104
    void prepend( T* o )
105
    {
106
        m_daddy->connect( o, SIGNAL(destroyed()), SLOT(onDestroyed()) );
107
        QList<T*>::prepend( o );
108
    }
109
    
110
    SmartPointerList& operator+=( T* o )
111
    {
112
        append( o );
113
        return *this;
114
    }
115
116
    SmartPointerList& operator<<( T* o )
117
    {
118
        return operator+=( o );
119
    }
120
    
121
    SmartPointerList operator+( const SmartPointerList that )
122
    {
123
        SmartPointerList<T> copy = *this;
124
        QListIterator<T*> i( that );
125
        while (i.hasNext())
126
            copy.append( i.next() );
127
        return copy;
128
    }
129
130
    SmartPointerList& operator+=( const SmartPointerList that )
131
    {
132
        QListIterator<T*> i( that );
133
        while (i.hasNext())
134
            append( i.next() );
135
        return *this;
136
    }
137
138
    void push_back( T* o )
139
    {
140
        append( o );
141
    }
142
    
143
    void push_front( T* o )
144
    {
145
        prepend( o );
146
    }
147
    
148
    void replace( int i, T* o )
149
    {
150
        QList<T*>::replace( i, o );
151
        m_daddy->connect( o, SIGNAL(destroyed()), SLOT(onDestroyed()) );
152
    }
153
154
    /** this is a "safe" class. We always bounds check */
155
    inline T* operator[]( int index ) const { return QList<T*>::value( index ); }
156
    inline T* at( int index ) const { return QList<T*>::value( index ); }
157
 
158
    // make public safe functions again
159
    using QList<T*>::back;
160
    using QList<T*>::constBegin;
161
    using QList<T*>::constEnd;
162
    using QList<T*>::const_iterator;
163
    using QList<T*>::contains;
164
    using QList<T*>::count;
165
    using QList<T*>::empty;
166
    using QList<T*>::erase;
167
    using QList<T*>::first;
168
    using QList<T*>::front;
169
    using QList<T*>::indexOf;
170
    using QList<T*>::insert;
171
    using QList<T*>::isEmpty;    
172
    using QList<T*>::last;
173
    using QList<T*>::lastIndexOf;
174
    using QList<T*>::mid;        
175
    using QList<T*>::move;
176
    using QList<T*>::pop_back;
177
    using QList<T*>::pop_front;
178
    using QList<T*>::size;
179
    using QList<T*>::swap;
180
    using QList<T*>::value;
181
    using QList<T*>::operator!=;
182
    using QList<T*>::operator==;
183
    
184
    // can't use using directive here since we only want the const versions
185
    typename QList<T*>::const_iterator begin() const { return QList<T*>::constBegin(); }
186
    typename QList<T*>::const_iterator end() const { return QList<T*>::constEnd(); }
187
        
188
    // it can lead to poor performance situations if we don't disconnect
189
    // but I think it's not worth making this class more complicated for such 
190
    // an edge case
191
    using QList<T*>::clear;
192
    using QList<T*>::removeAll;
193
    using QList<T*>::removeAt;
194
    using QList<T*>::removeFirst;
195
    using QList<T*>::removeLast;
196
    using QList<T*>::removeOne;
197
    using QList<T*>::takeAt;
198
    using QList<T*>::takeFirst;
199
    using QList<T*>::takeLast;
200
};
201
202
#endif //HEADER_GUARD