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