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