// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include #include #include #include #include #include #include // priorities #include "FileTreeItem.h" #include "FileTreeModel.h" #include "Formatter.h" #include "IconCache.h" std::unordered_map const& FileTreeItem::getMyChildRows() const { // ensure that all the rows are hashed for (int const n = childCount(); first_unhashed_row_ < n; ++first_unhashed_row_) { child_rows_.emplace(children_[first_unhashed_row_]->name(), first_unhashed_row_); } return child_rows_; } FileTreeItem::~FileTreeItem() { assert(std::empty(children_)); if (parent_ == nullptr) { return; } // find the parent's reference to this child auto& siblings = parent_->children_; auto it = std::find(std::begin(siblings), std::end(siblings), this); if (it == std::end(siblings)) { return; } // remove this child from the parent parent_->child_rows_.erase(name()); it = siblings.erase(it); // invalidate the row numbers of the siblings that came after this child parent_->first_unhashed_row_ = std::distance(std::begin(siblings), it); } void FileTreeItem::appendChild(FileTreeItem* child) { int const n = childCount(); child->parent_ = this; children_.push_back(child); first_unhashed_row_ = n; } FileTreeItem* FileTreeItem::child(QString const& filename) { auto const& child_rows = getMyChildRows(); if (auto const iter = child_rows.find(filename); iter != std::end(child_rows)) { auto* const item = child(iter->second); assert(filename == item->name()); return item; } return {}; } int FileTreeItem::row() const { auto const& child_rows = parent_->getMyChildRows(); if (auto const iter = child_rows.find(name()); iter != std::end(child_rows)) { auto const idx = iter->second; assert(this == parent_->children_[idx]); return idx; } return -1; } QVariant FileTreeItem::data(int column, int role) const { QVariant value; switch (role) { case FileTreeModel::FileIndexRole: value.setValue(file_index_); break; case FileTreeModel::WantedRole: value.setValue(isSubtreeWanted()); break; case FileTreeModel::CompleteRole: value.setValue(isComplete()); break; case Qt::ToolTipRole: case Qt::EditRole: if (column == FileTreeModel::COL_NAME) { value.setValue(name()); } break; case Qt::TextAlignmentRole: if (column == FileTreeModel::COL_SIZE) { value = static_cast(Qt::AlignRight | Qt::AlignVCenter); } break; case Qt::DisplayRole: case FileTreeModel::SortRole: switch (column) { case FileTreeModel::COL_NAME: value.setValue(name()); break; case FileTreeModel::COL_SIZE: if (role == Qt::DisplayRole) { value.setValue(sizeString()); } else { value.setValue(size()); } break; case FileTreeModel::COL_PROGRESS: value.setValue(progress()); break; case FileTreeModel::COL_WANTED: value.setValue(isSubtreeWanted()); break; case FileTreeModel::COL_PRIORITY: if (role == Qt::DisplayRole) { value.setValue(priorityString()); } else { value.setValue(priority()); } break; } break; case Qt::DecorationRole: if (column == FileTreeModel::COL_NAME) { if (file_index_ < 0) { value = QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon); } else { auto const& icon_cache = IconCache::get(); value = childCount() > 0 ? icon_cache.folderIcon() : icon_cache.guessMimeIcon(name(), icon_cache.fileIcon()); } } break; } return value; } std::pair FileTreeItem::get_subtree_wanted_size() const { auto have = uint64_t{ is_wanted_ ? have_size_ : 0U }; auto total = uint64_t{ is_wanted_ ? total_size_ : 0U }; for (auto const* const child : children_) { auto const [child_have, child_total] = child->get_subtree_wanted_size(); have += child_have; total += child_total; } return { have, total }; } double FileTreeItem::progress() const { auto const [have, total] = get_subtree_wanted_size(); return have >= total ? 1.0 : static_cast(have) / static_cast(total); } QString FileTreeItem::sizeString() const { return Formatter::storage_to_string(size()); } uint64_t FileTreeItem::size() const { auto const [have, total] = get_subtree_wanted_size(); return total; } std::pair FileTreeItem::update(QString const& name, bool wanted, int priority, uint64_t have_size, bool update_fields) { auto changed_columns = small::max_size_set{}; if (name_ != name) { if (parent_ != nullptr) { parent_->first_unhashed_row_ = row(); } name_ = name; changed_columns.insert(FileTreeModel::COL_NAME); } if (fileIndex() != -1) { if (have_size_ != have_size) { have_size_ = have_size; changed_columns.insert(FileTreeModel::COL_PROGRESS); } if (update_fields) { if (is_wanted_ != wanted) { is_wanted_ = wanted; changed_columns.insert(FileTreeModel::COL_WANTED); } if (priority_ != priority) { priority_ = priority; changed_columns.insert(FileTreeModel::COL_PRIORITY); } } } std::pair changed(-1, -1); if (!changed_columns.empty()) { changed.first = *std::cbegin(changed_columns); changed.second = *std::crbegin(changed_columns); } return changed; } QString FileTreeItem::priorityString() const { int const i = priority(); switch (i) { case Low: return tr("Low"); case High: return tr("High"); case Normal: return tr("Normal"); default: return tr("Mixed"); } } int FileTreeItem::priority() const { int i(0); if (std::empty(children_)) { switch (priority_) { case TR_PRI_LOW: i |= Low; break; case TR_PRI_HIGH: i |= High; break; default: i |= Normal; break; } } for (FileTreeItem const* const child : children_) { i |= child->priority(); } return i; } void FileTreeItem::setSubtreePriority(int priority, file_indices_t& setme_changed_ids) { if (priority_ != priority) { priority_ = priority; if (file_index_ >= 0) { setme_changed_ids.insert(file_index_); } } for (FileTreeItem* const child : children_) { child->setSubtreePriority(priority, setme_changed_ids); } } int FileTreeItem::isSubtreeWanted() const { if (std::empty(children_)) { return is_wanted_ ? Qt::Checked : Qt::Unchecked; } int wanted(-1); for (FileTreeItem const* const child : children_) { int const child_wanted = child->isSubtreeWanted(); if (wanted == -1) { wanted = child_wanted; } if (wanted != child_wanted) { wanted = Qt::PartiallyChecked; } if (wanted == Qt::PartiallyChecked) { return wanted; } } return wanted; } void FileTreeItem::setSubtreeWanted(bool wanted, file_indices_t& setme_changed_ids) { if (is_wanted_ != wanted) { is_wanted_ = wanted; if (file_index_ >= 0) { setme_changed_ids.insert(file_index_); } } for (FileTreeItem* const child : children_) { child->setSubtreeWanted(wanted, setme_changed_ids); } } QString FileTreeItem::path() const { QString item_path; FileTreeItem const* item = this; while (item != nullptr && !item->name().isEmpty()) { if (item_path.isEmpty()) { item_path = item->name(); } else { item_path = item->name() + QLatin1Char('/') + item_path; } item = item->parent(); } return item_path; }