PageRenderTime 91ms CodeModel.GetById 20ms app.highlight 39ms RepoModel.GetById 11ms app.codeStats 0ms

/src/libtomahawk/widgets/checkdirtree.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 397 lines | 283 code | 66 blank | 48 comment | 39 complexity | 29dec88a4eb2417a957544a3ddea28b1 MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  4 *   Copyright 2011,      Leo Franchi <lfranchi@kde.org>
  5 *
  6 *   Tomahawk 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 3 of the License, or
  9 *   (at your option) any later version.
 10 *
 11 *   Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 18 */
 19
 20#include "CheckDirTree.h"
 21
 22#include "utils/Logger.h"
 23#include "TomahawkSettings.h"
 24#include "Source.h"
 25
 26#include <QCoreApplication>
 27#include <QProcess>
 28
 29static QString s_macVolumePath = "/Volumes";
 30
 31CheckDirModel::CheckDirModel( QWidget* parent )
 32    : QFileSystemModel( parent )
 33    , m_shownVolumes( false )
 34{
 35#ifdef Q_OS_MAC
 36    m_setFilePath = QString( "%1/SetFile" )        .arg( QCoreApplication::applicationDirPath() );
 37    m_getFileInfoPath = QString( "%1/GetFileInfo" ).arg( QCoreApplication::applicationDirPath() );
 38
 39    QProcess* checkVolumeVisible = new QProcess( this );
 40    connect( checkVolumeVisible, SIGNAL( readyReadStandardOutput() ), this, SLOT( getFileInfoResult() ) );
 41    qDebug() << "Running GetFileInfo:" << m_getFileInfoPath << "-aV" << s_macVolumePath;
 42    checkVolumeVisible->start( m_getFileInfoPath, QStringList() <<  "-aV" << s_macVolumePath );
 43#endif
 44}
 45
 46CheckDirModel::~CheckDirModel()
 47{
 48#ifdef Q_OS_MAC
 49    // reset to previous state
 50    if ( m_shownVolumes )
 51        QProcess::startDetached( QString( "%1 -a V %2" ).arg( m_setFilePath).arg( s_macVolumePath ) );
 52#endif
 53}
 54
 55void
 56CheckDirModel::getFileInfoResult()
 57{
 58#ifdef Q_OS_MAC
 59    QProcess* p = qobject_cast< QProcess* >( sender() );
 60    Q_ASSERT( p );
 61
 62    QByteArray res = p->readAll().trimmed();
 63    qDebug() << "Got output from GetFileInfo:" << res;
 64    // 1 means /Volumes is hidden, so we show it while the dialog is visible
 65    if ( res == "1" )
 66    {
 67        // Remove the hidden flag for the /Volumnes folder so all mount points are visible in the default (Q)FileSystemModel
 68        QProcess* showProcess = new QProcess( this );
 69        qDebug() << "Running SetFile:" << QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath );
 70        showProcess->start( QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath ) );
 71        connect( showProcess, SIGNAL( readyReadStandardError() ), this, SLOT( processErrorOutput() ) );
 72        m_shownVolumes = true;
 73
 74        QTimer::singleShot( 500, this, SLOT( volumeShowFinished() ) );
 75    }
 76
 77    p->terminate();
 78    p->deleteLater();
 79#endif
 80}
 81
 82void
 83CheckDirModel::volumeShowFinished()
 84{
 85    reset();
 86}
 87
 88
 89void
 90CheckDirModel::processErrorOutput()
 91{
 92    QProcess* p = qobject_cast< QProcess* >( sender() );
 93    Q_ASSERT( p );
 94    qDebug() << "Got ERROR OUTPUT from subprocess in CheckDirModel:" << p->readAll();
 95}
 96
 97
 98Qt::ItemFlags
 99CheckDirModel::flags( const QModelIndex& index ) const
100{
101    return QFileSystemModel::flags( index ) | Qt::ItemIsUserCheckable;
102}
103
104
105QVariant
106CheckDirModel::data( const QModelIndex& index, int role ) const
107{
108#ifdef Q_WS_MAC
109    // return the 'My Computer' icon for the /Volumes folder
110    if ( index.column() == 0 && filePath( index ) == s_macVolumePath )
111    {
112        switch ( role )
113        {
114            case Qt::DecorationRole:
115                return myComputer( role );
116            default:
117                break;
118        }
119    }
120#endif
121
122    if ( role == Qt::CheckStateRole )
123    {
124        return m_checkTable.contains( index ) ? m_checkTable.value( index ) : Qt::Unchecked;
125    }
126    else
127    {
128        return QFileSystemModel::data( index, role );
129    }
130}
131
132
133bool
134CheckDirModel::setData( const QModelIndex& index, const QVariant& value, int role )
135{
136    bool b = QFileSystemModel::setData( index, value, role );
137
138    if ( role == Qt::CheckStateRole )
139    {
140        m_checkTable.insert( index, (Qt::CheckState)value.toInt() );
141        emit dataChanged( index, index );
142        emit dataChangedByUser( index );
143    }
144
145    return b;
146}
147
148
149void
150CheckDirModel::setCheck( const QModelIndex& index, const QVariant& value )
151{
152    QFileSystemModel::setData( index, value, Qt::CheckStateRole );
153    m_checkTable.insert( index, (Qt::CheckState)value.toInt() );
154    emit dataChanged( index, index );
155}
156
157
158Qt::CheckState
159CheckDirModel::getCheck( const QModelIndex& index )
160{
161    return (Qt::CheckState)data( index, Qt::CheckStateRole ).toInt();
162}
163
164
165CheckDirTree::CheckDirTree( QWidget* parent )
166    : QTreeView( parent )
167{
168    m_dirModel.setFilter( QDir::Dirs | QDir::NoDotAndDotDot );
169    m_dirModel.setRootPath( "/" );
170
171    m_dirModel.setNameFilters( QStringList() << "[^\\.]*" );
172
173    setModel( &m_dirModel );
174    setColumnHidden( 1, true );
175    setColumnHidden( 2, true );
176    setColumnHidden( 3, true );
177    //header()->hide();
178
179    connect( &m_dirModel, SIGNAL( dataChangedByUser( QModelIndex ) ),
180                            SLOT( updateNode( QModelIndex ) ) );
181    connect( &m_dirModel, SIGNAL( dataChangedByUser( const QModelIndex& ) ),
182                          SIGNAL( changed() ) );
183    connect( &m_dirModel, SIGNAL( modelReset() ),
184                            SLOT( modelReset() ) );
185
186    connect( this, SIGNAL( collapsed( QModelIndex ) ),
187                     SLOT( onCollapse( QModelIndex ) ) );
188    connect( this, SIGNAL( expanded( QModelIndex ) ),
189                     SLOT( onExpand( QModelIndex ) ) );
190}
191
192
193void
194CheckDirTree::checkPath( const QString& path, Qt::CheckState state )
195{
196    QModelIndex index = m_dirModel.index( path );
197    m_dirModel.setCheck( index, state );
198    updateNode( index );
199}
200
201
202void
203CheckDirTree::setExclusions( const QStringList& list )
204{
205    foreach ( const QString& path, list )
206    {
207        checkPath( path, Qt::Unchecked );
208    }
209}
210
211
212QStringList
213CheckDirTree::getCheckedPaths()
214{
215    QStringList checks;
216    QModelIndex root = rootIndex();
217
218    getChecksForNode( root, checks );
219    return checks;
220}
221
222
223void
224CheckDirTree::getChecksForNode( const QModelIndex& index, QStringList& checks )
225{
226    // Look at first node
227    // Is it checked?
228    //  - move on to next node
229    // Is it unchecked?
230    //  - add to list
231    //  - move to next node
232    // Is it partially checked?
233    //  - recurse
234
235    int numChildren = m_dirModel.rowCount( index );
236    for ( int i = 0; i < numChildren; ++i )
237    {
238        QModelIndex kid = m_dirModel.index( i, 0, index );
239        Qt::CheckState check = m_dirModel.getCheck( kid );
240        if ( check == Qt::Checked )
241        {
242            checks.append( m_dirModel.filePath( kid ) );
243        }
244        else if ( check == Qt::Unchecked )
245        {
246            continue;
247        }
248        else if ( check == Qt::PartiallyChecked )
249        {
250            getChecksForNode( kid, checks );
251        }
252        else
253        {
254            Q_ASSERT( false );
255        }
256    }
257}
258
259
260QStringList
261CheckDirTree::getExclusions()
262{
263    QStringList exclusions;
264    QModelIndex root = rootIndex();
265
266    getExclusionsForNode( root, exclusions );
267    return exclusions;
268}
269
270
271void
272CheckDirTree::getExclusionsForNode( const QModelIndex& index, QStringList& exclusions )
273{
274    // Look at first node
275    // Is it checked?
276    //  - move on to next node
277    // Is it unchecked?
278    //  - add to list
279    //  - move to next node
280    // Is it partially checked?
281    //  - recurse
282
283    int numChildren = m_dirModel.rowCount( index );
284    for ( int i = 0; i < numChildren; ++i )
285    {
286        QModelIndex kid = m_dirModel.index( i, 0, index );
287        Qt::CheckState check = m_dirModel.getCheck( kid );
288        if ( check == Qt::Checked )
289        {
290            continue;
291        }
292        else if ( check == Qt::Unchecked )
293        {
294            exclusions.append( m_dirModel.filePath( kid ) );
295        }
296        else if ( check == Qt::PartiallyChecked )
297        {
298            getExclusionsForNode( kid, exclusions );
299        }
300        else
301        {
302            Q_ASSERT( false );
303        }
304    }
305}
306
307
308void
309CheckDirTree::onCollapse( const QModelIndex& /*idx*/ )
310{
311
312}
313
314
315void
316CheckDirTree::onExpand( const QModelIndex& idx )
317{
318    // If the node is partially checked, that means we have been below it
319    // setting some stuff, so only fill down if we are unchecked.
320    if ( m_dirModel.getCheck( idx ) != Qt::PartiallyChecked )
321    {
322        fillDown( idx );
323    }
324}
325
326
327void
328CheckDirTree::updateNode( const QModelIndex& idx )
329{
330    // Start by recursing down to the bottom and then work upwards
331    fillDown( idx );
332    updateParent( idx );
333}
334
335
336void
337CheckDirTree::modelReset()
338{
339    foreach ( const QString& dir, TomahawkSettings::instance()->scannerPaths() )
340    {
341        checkPath( dir, Qt::Checked );
342    }
343}
344
345
346void
347CheckDirTree::fillDown( const QModelIndex& parent )
348{
349    // Recursion stops when we reach a directory which has never been expanded
350    // or one that has no children.
351    if ( !isExpanded( parent ) || !m_dirModel.hasChildren( parent ) )
352    {
353        return;
354    }
355
356    Qt::CheckState state = m_dirModel.getCheck( parent );
357    int numChildren = m_dirModel.rowCount( parent );
358    for ( int i = 0; i < numChildren; ++i )
359    {
360        QModelIndex kid = m_dirModel.index( i, 0, parent );
361        m_dirModel.setCheck( kid, state );
362        fillDown( kid );
363    }
364}
365
366
367void
368CheckDirTree::updateParent( const QModelIndex& index )
369{
370    QModelIndex parent = index.parent();
371    if ( !parent.isValid() )
372    {
373        // We have reached the root
374        return;
375    }
376
377    // Initialise overall state to state of first child
378    QModelIndex kid = m_dirModel.index( 0, 0, parent );
379    Qt::CheckState overall = m_dirModel.getCheck( kid );
380
381    int numChildren = m_dirModel.rowCount( parent );
382    for ( int i = 1; i <= numChildren; ++i )
383    {
384        kid = m_dirModel.index( i, 0, parent );
385        Qt::CheckState state = m_dirModel.getCheck( kid );
386        if ( state != overall )
387        {
388            // If we ever come across a state different than the first child,
389            // we are partially checked
390            overall = Qt::PartiallyChecked;
391            break;
392        }
393    }
394
395    m_dirModel.setCheck( parent, overall );
396    updateParent( parent );
397}