// Copyright (C) 2021 Jakub Melka // // This file is part of PDF4QT. // // PDF4QT is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // with the written consent of the copyright owner, any later version. // // PDF4QT is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with PDF4QT. If not, see . #ifndef PDFPAGEMASTER_PAGEITEMMODEL_H #define PDFPAGEMASTER_PAGEITEMMODEL_H #include "pdfdocument.h" #include "pdfutils.h" #include "pdfdocumentmanipulator.h" #include #include #include namespace pdfpagemaster { enum PageType { PT_DocumentPage, PT_Image, PT_Empty, PT_Last }; struct PageGroupItem { QString groupName; QString pagesCaption; QStringList tags; struct GroupItem { auto operator<=>(const GroupItem&) const = default; int documentIndex = -1; pdf::PDFInteger pageIndex = -1; pdf::PDFInteger imageIndex = -1; QSizeF rotatedPageDimensionsMM; ///< Rotated page dimensions, but without additional rotation pdf::PageRotation pageAdditionalRotation = pdf::PageRotation::None; ///< Additional rotation applied to the page PageType pageType = PT_DocumentPage; }; std::vector groups; auto operator<=>(const PageGroupItem&) const = default; bool isGrouped() const { return groups.size() > 1; } std::set getDocumentIndices() const; void rotateLeft(); void rotateRight(); }; struct DocumentItem { QString fileName; pdf::PDFDocument document; }; struct ImageItem { QImage image; QByteArray imageData; }; class PageItemModel : public QAbstractItemModel { Q_OBJECT private: using BaseClass = QAbstractItemModel; public: explicit PageItemModel(QObject* parent); virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; virtual QModelIndex parent(const QModelIndex& index) const override; virtual int rowCount(const QModelIndex& parent) const override; virtual int columnCount(const QModelIndex& parent) const override; virtual QVariant data(const QModelIndex& index, int role) const override; virtual QStringList mimeTypes() const override; virtual QMimeData* mimeData(const QModelIndexList& indexes) const override; virtual bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override; virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; virtual Qt::DropActions supportedDropActions() const override; virtual Qt::DropActions supportedDragActions() const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; enum class AssembleMode { Unite, Separate, SeparateGrouped }; std::vector> getAssembledPages(AssembleMode mode) const; /// Clear all data and undo/redo void clear(); /// Adds document to the model, inserts one single page group containing /// the whole document. Returns index of a newly added document. If document /// cannot be added (for example, it already exists), -1 is returned. /// \param fileName File name /// \param document Document /// \param index Index, where image is inserted /// \returns Identifier of the document (internal index) int insertDocument(QString fileName, pdf::PDFDocument document, const QModelIndex& index); /// Adds image to the model, inserts one single page containing /// the image. Returns index of a newly added image. If image /// cannot be read from the file, -1 is returned. /// \param fileName Image file /// \param index Index, where image is inserted /// \returns Identifier of the image (internal index) int insertImage(QString fileName, const QModelIndex& index); /// Adds image to the model, inserts one single page containing /// the image. Returns index of a newly added image. /// \param image Image /// \param index Index, where image is inserted /// \returns Identifier of the image (internal index) int insertImage(QImage image, const QModelIndex& index); /// Returns item at a given index. If item doesn't exist, /// then nullptr is returned. /// \param index Index const PageGroupItem* getItem(const QModelIndex& index) const; /// Returns item at a given index. If item doesn't exist, /// then nullptr is returned. /// \param index Index PageGroupItem* getItem(const QModelIndex& index); /// Returns true, if grouped item exists in the indices bool isGrouped(const QModelIndexList& indices) const; /// Returns true, if trash bin is empty bool isTrashBinEmpty() const { return m_trashBin.empty(); } QItemSelection getSelectionEven() const; QItemSelection getSelectionOdd() const; QItemSelection getSelectionPortrait() const; QItemSelection getSelectionLandscape() const; void group(const QModelIndexList& list); void ungroup(const QModelIndexList& list); QModelIndexList restoreRemovedItems(); QModelIndexList cloneSelection(const QModelIndexList& list); void removeSelection(const QModelIndexList& list); void insertEmptyPage(const QModelIndexList& list); void rotateLeft(const QModelIndexList& list); void rotateRight(const QModelIndexList& list); static QString getMimeDataType() { return QLatin1String("application/pagemodel.PDF4QtPageMaster"); } const std::map& getDocuments() const { return m_documents; } const std::map& getImages() const { return m_images; } struct SelectionInfo { int documentCount = 0; int imageCount = 0; int blankPageCount = 0; int firstDocumentIndex = 0; bool isDocumentOnly() const { return documentCount > 0 && imageCount == 0 && blankPageCount == 0; } bool isSingleDocument() const { return isDocumentOnly() && documentCount == 1; } bool isTwoDocuments() const { return isDocumentOnly() && documentCount == 2; } }; SelectionInfo getSelectionInfo(const QModelIndexList& list) const; void regroupEvenOdd(const QModelIndexList& list); void regroupPaired(const QModelIndexList& list); void regroupOutline(const QModelIndexList& list, const std::vector& indices); void regroupAlternatingPages(const QModelIndexList& list, bool reversed); bool canUndo() const { return !m_undoSteps.empty(); } bool canRedo() const { return !m_redoSteps.empty(); } void undo(); void redo(); private: static const int MAX_UNDO_REDO_STEPS = 10; void createDocumentGroup(int index, const QModelIndex& insertIndex); QString getGroupNameFromDocument(int index) const; void updateItemCaptionAndTags(PageGroupItem& item) const; void insertEmptyPage(const QModelIndex& index); struct UndoRedoStep { auto operator<=>(const UndoRedoStep&) const = default; std::vector pageGroupItems; std::vector trashBin; }; class Modifier { public: explicit Modifier(PageItemModel* model); ~Modifier(); private: PageItemModel* m_model; UndoRedoStep m_stateBeforeModification; }; std::vector extractItems(std::vector& items, const QModelIndexList& selection) const; QItemSelection getSelectionImpl(std::function filter) const; UndoRedoStep getCurrentStep() const { return UndoRedoStep{ m_pageGroupItems, m_trashBin }; } void updateUndoRedoSteps(); void clearUndoRedo(); void performUndoRedo(std::vector& load, std::vector& save); std::vector m_pageGroupItems; std::map m_documents; std::map m_images; std::vector m_trashBin; std::vector m_undoSteps; // Oldest step is first, newest step is last std::vector m_redoSteps; }; } // namespace pdfpagemaster #endif // PDFPAGEMASTER_PAGEITEMMODEL_H