#include #include #include "RsCollectionModel.h" // #define DEBUG_COLLECTION_MODEL 1 static const int COLLECTION_MODEL_FILENAME = 0; static const int COLLECTION_MODEL_SIZE = 1; static const int COLLECTION_MODEL_HASH = 2; static const int COLLECTION_MODEL_COUNT = 3; static const int COLLECTION_MODEL_NB_COLUMN = 4; RsCollectionModel::RsCollectionModel(const RsCollection& col, QObject *parent) : QAbstractItemModel(parent),mCollection(col) { postMods(); } static std::ostream& operator<<(std::ostream& o,const RsCollectionModel::EntryIndex& i) { return o << ((i.is_file)?("File"):"Dir") << " with index " << (int)i.index ; } static std::ostream& operator<<(std::ostream& o,const QModelIndex& i) { return o << "QModelIndex (row " << i.row() << ", ref " << i.internalId() << ")" ; } #ifdef DEBUG_COLLECTION_MODEL #endif // Indernal Id is always a quintptr_t (basically a uint with the size of a pointer). Depending on the // Indernal Id is always a quintptr_t (basically a uint with the size of a pointer). Depending on the // architecture, the pointer may have 4 or 8 bytes. We use the low-level bit for type (0=dir, 1=file) and // the remaining bits for the index (which will be accordingly understood as a FileIndex or a DirIndex) // This way, index 0 is always the top dir. bool RsCollectionModel::convertIndexToInternalId(const EntryIndex& e,quintptr& ref) { ref = (e.index << 1) | e.is_file; return true; } bool RsCollectionModel::convertInternalIdToIndex(quintptr ref, EntryIndex& e) { e.is_file = (bool)(ref & 1); e.index = ref >> 1; return true; } int RsCollectionModel::rowCount(const QModelIndex& parent) const { #ifdef DEBUG_COLLECTION_MODEL std::cerr << "Asking rowCount of " << parent << std::endl; #endif if(parent.column() >= COLLECTION_MODEL_NB_COLUMN) return 0; if(!parent.isValid()) { #ifdef DEBUG_COLLECTION_MODEL std::cerr << " root! returning " << mCollection.fileTree().directoryData(0).subdirs.size() + mCollection.fileTree().directoryData(0).subfiles.size() << std::endl; #endif return mCollection.fileTree().directoryData(0).subdirs.size() + mCollection.fileTree().directoryData(0).subfiles.size(); } EntryIndex i; if(!convertInternalIdToIndex(parent.internalId(),i)) return 0; if(i.is_file) { #ifdef DEBUG_COLLECTION_MODEL std::cerr << " file: returning 0" << std::endl; #endif return 0; } else { #ifdef DEBUG_COLLECTION_MODEL std::cerr << " dir: returning " << mCollection.fileTree().directoryData(i.index).subdirs.size() + mCollection.fileTree().directoryData(i.index).subfiles.size() << std::endl; #endif return mCollection.fileTree().directoryData(i.index).subdirs.size() + mCollection.fileTree().directoryData(i.index).subfiles.size(); } } bool RsCollectionModel::hasChildren(const QModelIndex & parent) const { if(!parent.isValid()) return true; EntryIndex i; if(!convertInternalIdToIndex(parent.internalId(),i)) return false; if(i.is_file) return false; else if(mCollection.fileTree().directoryData(i.index).subdirs.size() + mCollection.fileTree().directoryData(i.index).subfiles.size() > 0) return true; else return false; } int RsCollectionModel::columnCount(const QModelIndex&) const { return COLLECTION_MODEL_NB_COLUMN; } QVariant RsCollectionModel::headerData(int section, Qt::Orientation,int role) const { if(role == Qt::DisplayRole) switch(section) { case 0: return tr("File"); case 1: return tr("Size"); case 2: return tr("Hash"); case 3: return tr("Count"); default: return QVariant(); } return QVariant(); } QModelIndex RsCollectionModel::index(int row, int column, const QModelIndex & parent) const { if(row < 0 || column < 0 || column >= columnCount(parent) || row >= rowCount(parent)) return QModelIndex(); EntryIndex parent_index; if(!parent.isValid()) // root { parent_index.is_file = false; parent_index.index = 0; } else if(!convertInternalIdToIndex(parent.internalId(),parent_index)) return QModelIndex(); if(parent_index.is_file || parent_index.index >= mCollection.fileTree().numDirs()) return QModelIndex(); const auto& parentData(mCollection.fileTree().directoryData(parent_index.index)); if((size_t)row < parentData.subdirs.size()) { EntryIndex e; e.is_file = false; e.index = parentData.subdirs[row]; quintptr ref; convertIndexToInternalId(e,ref); #ifdef DEBUG_COLLECTION_MODEL std::cerr << "creating index for row " << row << " of parent " << parent << ". result is " << createIndex(row,column,ref) << std::endl; #endif return createIndex(row,column,ref); } if((size_t)row < parentData.subdirs.size() + parentData.subfiles.size()) { EntryIndex e; e.is_file = true; e.index = parentData.subfiles[row - parentData.subdirs.size()]; quintptr ref; convertIndexToInternalId(e,ref); #ifdef DEBUG_COLLECTION_MODEL std::cerr << "creating index for row " << row << " of parent " << parent << ". result is " << createIndex(row,column,ref) << std::endl; #endif return createIndex(row,column,ref); } return QModelIndex(); } Qt::ItemFlags RsCollectionModel::flags ( const QModelIndex & index ) const { if(index.isValid() && index.column() == COLLECTION_MODEL_FILENAME) { EntryIndex e; if(!convertInternalIdToIndex(index.internalId(),e)) return QAbstractItemModel::flags(index) ; if(e.is_file) return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; else return QAbstractItemModel::flags(index) | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable; } return QAbstractItemModel::flags(index) ; } QModelIndex RsCollectionModel::parent(const QModelIndex & index) const { if(!index.isValid()) return QModelIndex(); EntryIndex i; if(index.internalId()==0 || !convertInternalIdToIndex(index.internalId(),i)) return QModelIndex(); EntryIndex p; p.is_file = false; // all parents are directories int row; if(i.is_file) { p.index = mFileInfos[i.index].parent_index; row = mFileInfos[i.index].parent_row; } else { p.index = mDirInfos[i.index].parent_index; row = mDirInfos[i.index].parent_row; } quintptr ref; convertIndexToInternalId(p,ref); return createIndex(row,0,ref); } QVariant RsCollectionModel::data(const QModelIndex& index, int role) const { EntryIndex i; if(!convertInternalIdToIndex(index.internalId(),i)) return QVariant(); #ifdef DEBUG_COLLECTION_MODEL std::cerr << "Asking data of " << i << std::endl; #endif switch(role) { case Qt::DisplayRole: return displayRole(i,index.column()); case Qt::DecorationRole: return decorationRole(i,index.column()); case Qt::CheckStateRole: return checkStateRole(i,index.column()); case Qt::TextColorRole: return textColorRole(i,index.column()); default: return QVariant(); } } bool RsCollectionModel::setData(const QModelIndex& index,const QVariant& value,int role) { if(!index.isValid()) return false; EntryIndex e; if(role==Qt::CheckStateRole && convertInternalIdToIndex(index.internalId(), e)) { #ifdef DEBUG_COLLECTION_MODEL std::cerr << "Setting check state of item " << index << " to " << value.toBool() << std::endl; #endif RsFileTree::DirIndex dir_index ; if(e.is_file) { mFileInfos[e.index].is_checked = value.toBool(); dir_index = mFileInfos[e.index].parent_index; } else { std::function recursSetCheckFlag = [&](RsFileTree::DirIndex index,bool s) -> void { mDirInfos[index].check_state = (s)?SELECTED:UNSELECTED; auto& dir_data(mCollection.fileTree().directoryData(index)); mDirInfos[index].total_size = 0; mDirInfos[index].total_count = 0; for(uint32_t i=0;i& files) { mFilesBeingHashed.insert(files.begin(),files.end()); } void RsCollectionModel::fileHashingFinished(const RsFileHash& hash) { mFilesBeingHashed.erase(hash); } void RsCollectionModel::preMods() { mUpdating = true; emit layoutAboutToBeChanged(); } void RsCollectionModel::postMods() { // update all the local structures mDirInfos.clear(); mFileInfos.clear(); mDirInfos.resize(mCollection.fileTree().numDirs()); mFileInfos.resize(mCollection.fileTree().numFiles()); mDirInfos[0].parent_index = 0; #ifdef DEBUG_COLLECTION_MODEL std::cerr << "Updating from tree: " << std::endl; #endif recursUpdateLocalStructures(mCollection.fileTree().root(),0); mUpdating = false; emit layoutChanged(); emit sizesChanged(); // debugDump(); } void RsCollectionModel::recursUpdateLocalStructures(RsFileTree::DirIndex dir_index,int depth) { uint64_t total_size = 0; uint64_t total_count = 0; bool all_checked = true; bool all_unchecked = false; const auto& dd(mCollection.fileTree().directoryData(dir_index)); for(uint32_t i=0;i recursDump = [&](RsFileTree::DirIndex indx,int depth) { const auto& dir_data(mCollection.fileTree().directoryData(indx)); for(int i=0;i recursDump2 = [&](QModelIndex indx,int depth) { for(int i=0;i