diff --git a/source/common.h b/source/common.h index 545220b..6f40f7d 100644 --- a/source/common.h +++ b/source/common.h @@ -25,6 +25,9 @@ inline constexpr std::nullopt_t None{std::nullopt}; template using Option = std::optional; +template +using Variant = std::variant; + struct Error : public std::runtime_error { using std::runtime_error::runtime_error; }; @@ -122,17 +125,20 @@ static inline QDebug operator<<(QDebug debug, std::string const &t) { return debug; } -template -static inline QDebug operator<<(QDebug debug, std::unique_ptr const &t) { - debug << *t; - return debug; -} - template -static inline QDebug operator<<(QDebug debug, - std::variant const &t) { +static inline QDebug operator<<(QDebug debug, Variant const &t) { std::visit([&](auto &&arg) {debug << arg;}, t); return debug; } +template +static inline QDebug operator<<(QDebug debug, Option const &t) { + if(t) { + debug << *t; + } else { + debug << "None"; + } + return debug; +} + // EOF diff --git a/source/quam/archive.cc b/source/quam/archive.cc index 0cb0e8f..5dcca3a 100644 --- a/source/quam/archive.cc +++ b/source/quam/archive.cc @@ -6,148 +6,150 @@ #include namespace Arc { - -Option getArchiveType(std::istream &st) noexcept { - try { - auto pos = st.tellg(); - auto magic = readBytes<4>(st); - st.seekg(pos); - if(magic == std::array{'P', 'A', 'C', 'K'}) {return ArcPack;} - if(magic == std::array{'W', 'A', 'D', '2'}) {return ArcWad2;} - } catch(...) { + Option getArchiveType(std::istream &st) noexcept { + try { + auto pos = st.tellg(); + auto magic = readBytes<4>(st); + st.seekg(pos); + if(magic == std::array{'P', 'A', 'C', 'K'}) {return ArcType::Pack;} + if(magic == std::array{'W', 'A', 'D', '2'}) {return ArcType::Wad2;} + } catch(...) { + } + return None; } - return None; -} -Dir readArchive(std::istream &st) { - switch(auto v = getArchiveType(st); - v.has_value() ? *v : throw FileFormatError("not an archive")) { - case ArcPack: return readPack(st); - case ArcWad2: return readWad2(st); + Dir readArchive(std::istream &st) { + switch(auto v = getArchiveType(st); + v.has_value() ? *v : throw FileFormatError("not an archive")) { + case ArcType::Pack: return readPack(st); + case ArcType::Wad2: return readWad2(st); + } } -} -Dir::iterator Dir::findNode(std::string const &name) { - return std::find_if(begin(), end(), [&name](Node const &node) { - return node.name == name; - }); -} + Node *Dir::findNode(std::string const &name) { + auto it = std::find_if(begin(), end(), [&name](Node &node) { + return node.name == name; + }); + return it != end() ? &*it : nullptr; + } -Node::Node(Dir &&f, std::string &&n, FileType t) : - super_type(std::move(f)), - name(std::move(n)), - type(t) -{ -} + Node::Node(Dir &&f, std::string &&n, FileType t) : + superType(std::move(f)), + name(std::move(n)), + type(t) + { + } -Node::Node(File &&f, std::string &&n, FileType t) : - super_type(std::move(f)), - name(std::move(n)), - type(t) -{ -} + Node::Node(File &&f, std::string &&n, FileType t) : + superType(std::move(f)), + name(std::move(n)), + type(t) + { + } -Model::Model(QObject *parent) : - QAbstractItemModel{parent}, - m_dir{nullptr} -{ -} + Model::Model(QObject *parent) : + QAbstractItemModel{parent}, + m_dir{nullptr} + { + } -Model::~Model() { -} + Model::~Model() { + } -QVariant Model::data(QModelIndex const &index, int role) const { - if(!index.isValid()) { + QVariant Model::data(QModelIndex const &index, int role) const { + if(!index.isValid()) { + return QVariant{}; + } + + auto node = static_cast(index.internalPointer()); + auto col = Column(index.column()); + + switch(role) { + case Qt::DecorationRole: + if(col == Column::Name) { + auto icon = + std::holds_alternative(*node) ? "folder" + : "text-x-generic"; + return QVariant{QIcon::fromTheme(icon)}; + } + break; + case Qt::DisplayRole: + switch(col) { + case Column::Size: + if(auto file = std::get_if(node)) { + return QVariant{QString::number(file->size())}; + } + break; + case Column::Type: + return QVariant{tr(enumToString(node->type))}; + case Column::Name: + return QVariant{tr(node->name.data())}; + } + case Qt::UserRole: + return QVariant::fromValue(static_cast(node)); + default: + break; + } return QVariant{}; } - auto node = static_cast(index.internalPointer()); - - switch(role) { - case Qt::DecorationRole: - if(index.column() == ColumnName) { - auto icon = - std::holds_alternative(*node) ? "folder" - : "text-x-generic"; - return QVariant{QIcon::fromTheme(icon)}; - } - break; - case Qt::DisplayRole: - switch(index.column()) { - case ColumnSize: - if(auto file = std::get_if(node)) { - return QVariant{QString::number(file->size())}; - } - break; - case ColumnType: - return QVariant{tr(enumToString(node->type))}; - case ColumnName: - return QVariant{tr(node->name.data())}; - } - default: - break; - } - return QVariant{}; -} - -Qt::ItemFlags Model::flags(QModelIndex const &index) const { - if(!index.isValid()) { - return Qt::NoItemFlags; - } else { - return Qt::ItemIsSelectable | - Qt::ItemIsDragEnabled | - Qt::ItemIsEnabled | - Qt::ItemNeverHasChildren; - } -} - -QVariant Model::headerData(int section, - Qt::Orientation orientation, - int role) const { - if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { - switch(section) { - case ColumnSize: return QVariant{tr("Size")}; - case ColumnType: return QVariant{tr("Type")}; - case ColumnName: return QVariant{tr("Name")}; + Qt::ItemFlags Model::flags(QModelIndex const &index) const { + if(!index.isValid()) { + return Qt::NoItemFlags; + } else { + return Qt::ItemIsSelectable | + Qt::ItemIsDragEnabled | + Qt::ItemIsEnabled | + Qt::ItemNeverHasChildren; } } - return QVariant{}; -} -QModelIndex Model::index(int row, int col, QModelIndex const &parent) const { - if(!m_dir || !hasIndex(row, col, parent) || row > m_dir->size()) { + QVariant Model::headerData(int section, + Qt::Orientation ori, + int role) const { + if(ori == Qt::Horizontal && role == Qt::DisplayRole) { + switch(Column(section)) { + case Column::Size: return QVariant{tr("Size")}; + case Column::Type: return QVariant{tr("Type")}; + case Column::Name: return QVariant{tr("Name")}; + } + } + return QVariant{}; + } + + QModelIndex Model::index(int row, + int col, + QModelIndex const &parent) const { + if(!m_dir || !hasIndex(row, col, parent) || row > m_dir->size()) { + return QModelIndex{}; + } else { + return createIndex(row, col, &m_dir->at(row)); + } + } + + QModelIndex Model::parent(QModelIndex const &) const { return QModelIndex{}; - } else { - // despite index data being const, this function does not take a const - // pointer, which is very annoying! - return createIndex(row, col, const_cast(&m_dir->at(row))); } -} -QModelIndex Model::parent(QModelIndex const &) const { - return QModelIndex{}; -} - -int Model::rowCount(QModelIndex const &) const { - return m_dir ? m_dir->size() : 0; -} - -int Model::columnCount(QModelIndex const &) const { - return enumMax(); -} - -Dir *Model::dir() const { - return m_dir; -} - -void Model::setDir(Dir *dir) { - if(dir != m_dir) { - m_dir = dir; - emit layoutChanged(); - emit dirChanged(dir); + int Model::rowCount(QModelIndex const &) const { + return m_dir ? m_dir->size() : 0; } -} + int Model::columnCount(QModelIndex const &) const { + return enumMax(); + } + + Dir *Model::dir() const { + return m_dir; + } + + void Model::setDir(Dir *dir) { + if(dir != m_dir) { + m_dir = dir; + emit layoutChanged(); + emit dirChanged(dir); + } + } } // EOF diff --git a/source/quam/archive.h b/source/quam/archive.h index c4eeb9a..2ad12b6 100644 --- a/source/quam/archive.h +++ b/source/quam/archive.h @@ -12,10 +12,10 @@ namespace Arc { struct Node; struct Dir; - enum Column { - ColumnSize, - ColumnType, - ColumnName, + enum class Column { + Size, + Type, + Name, }; Q_ENUM_NS(Column) @@ -30,28 +30,28 @@ namespace Arc { }; Q_ENUM_NS(FileType) - enum ArchiveType { - ArcPack, - ArcWad2, + enum class ArcType { + Pack, + Wad2, }; - Q_ENUM_NS(ArchiveType) + Q_ENUM_NS(ArcType) - Option getArchiveType(std::istream &st) noexcept; + Option getArchiveType(std::istream &st) noexcept; Dir readArchive(std::istream &st); struct Dir : public std::vector { using std::vector::vector; - Dir::iterator findNode(std::string const &name); + Node *findNode(std::string const &name); }; struct File : public QByteArray { using QByteArray::QByteArray; }; - struct Node : public std::variant { - using super_type = std::variant; + struct Node : public Variant { + using superType = Variant; Node() = default; @@ -74,13 +74,14 @@ namespace Arc { QVariant data(QModelIndex const &index, int role) const override; Qt::ItemFlags flags(QModelIndex const &index) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) + QVariant headerData(int section, Qt::Orientation ori, int role) const override; QModelIndex index(int row, - int col, - QModelIndex const &parent = QModelIndex()) + int col, + QModelIndex const &parent = QModelIndex()) + const override; + QModelIndex parent(QModelIndex const &index = QModelIndex()) const override; - QModelIndex parent(QModelIndex const &index = QModelIndex()) const override; int rowCount(QModelIndex const &index = QModelIndex()) const override; int columnCount(QModelIndex const &index = QModelIndex()) const override; @@ -100,4 +101,12 @@ Q_DECLARE_METATYPE(Arc::Dir) Q_DECLARE_METATYPE(Arc::File) Q_DECLARE_METATYPE(Arc::Node) +static inline QDebug operator<<(QDebug debug, Arc::Node const &t) { + debug << "Arc::Node(" << t.name << ", "; + std::visit([&](auto &&arg) {debug << arg;}, + static_cast>(t)); + debug << ")"; + return debug; +} + // EOF diff --git a/source/quam/pack.cc b/source/quam/pack.cc index b2cc772..6ccff84 100644 --- a/source/quam/pack.cc +++ b/source/quam/pack.cc @@ -59,7 +59,7 @@ static PackEntry readPackEntry(std::istream &st) { return ent; } -static void insertFile(Arc::Dir &dir, std::string name, Arc::File file) { +static void insertFile(Arc::Dir &dir, std::string &name, Arc::File &file) { Option next; if(auto slash = name.find('/'); slash != std::string::npos) { @@ -68,18 +68,14 @@ static void insertFile(Arc::Dir &dir, std::string name, Arc::File file) { } auto existing = dir.findNode(name); - if(next) { - auto ref = - existing != dir.end() - ? *existing - : dir.emplace_back(Arc::Dir{}, std::move(name)); - insertFile(std::get(ref), *std::move(next), std::move(file)); - } else { - if(existing != dir.end()) { - throw FileFormatError("duplicate file"); - } + auto &ref = existing ? *existing + : dir.emplace_back(Arc::Dir{}, std::move(name)); + insertFile(std::get(ref), *next, file); + } else if(!existing) { dir.emplace_back(std::move(file), std::move(name)); + } else { + throw FileFormatError("duplicate file"); } } @@ -91,7 +87,7 @@ Arc::Dir readPack(std::istream &st) { for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readPackEntry(st); - insertFile(root, std::move(ent.name), std::move(ent.file)); + insertFile(root, ent.name, ent.file); } return root; diff --git a/source/quam/project.cc b/source/quam/project.cc index 0eaa21f..c13805c 100644 --- a/source/quam/project.cc +++ b/source/quam/project.cc @@ -19,7 +19,10 @@ Project::Project(Arc::Dir &&arc, QErrorMessage *errors, QMdiArea *parent) : setAttribute(Qt::WA_DeleteOnClose); showMaximized(); - connect(m_model, &Arc::Model::dirChanged, this, &Project::dirChanged); + connect(m_model, &Arc::Model::dirChanged, + this, &Project::dirChanged); + connect(tableView, &QAbstractItemView::doubleClicked, + this, &Project::viewDoubleClicked); m_sorter->setSourceModel(m_model); tableView->setModel(m_sorter); @@ -33,4 +36,12 @@ void Project::dirChanged(Arc::Dir *) { tableView->resizeColumnsToContents(); } +void Project::viewDoubleClicked(QModelIndex const &index) { + auto node = static_cast(index.data(Qt::UserRole) + .value()); + if(auto dir = std::get_if(node)) { + m_model->setDir(dir); + } +} + // EOF diff --git a/source/quam/project.h b/source/quam/project.h index 8ffcc52..73a1826 100644 --- a/source/quam/project.h +++ b/source/quam/project.h @@ -22,6 +22,7 @@ public: private slots: void dirChanged(Arc::Dir *dir); + void viewDoubleClicked(QModelIndex const &index); private: Arc::Dir m_arc; diff --git a/source/quam/wad2.cc b/source/quam/wad2.cc index 6ec9d21..996bb2b 100644 --- a/source/quam/wad2.cc +++ b/source/quam/wad2.cc @@ -82,11 +82,6 @@ Arc::Dir readWad2(std::istream &st) { for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readWad2Entry(st); - /* - if(root.findNode(ent.name) != root.end()) { - throw FileFormatError("duplicate file"); - } - */ root.emplace_back(std::move(ent.file), std::move(ent.name), ent.type); }