mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Group/Ungroup functionality
This commit is contained in:
@ -253,6 +253,7 @@ bool MainWindow::canPerformOperation(Operation operation) const
|
|||||||
{
|
{
|
||||||
QModelIndexList selection = ui->documentItemsView->selectionModel()->selection().indexes();
|
QModelIndexList selection = ui->documentItemsView->selectionModel()->selection().indexes();
|
||||||
const bool isSelected = !selection.isEmpty();
|
const bool isSelected = !selection.isEmpty();
|
||||||
|
const bool isMultiSelected = selection.size() > 1;
|
||||||
const bool isModelEmpty = m_model->rowCount(QModelIndex()) == 0;
|
const bool isModelEmpty = m_model->rowCount(QModelIndex()) == 0;
|
||||||
|
|
||||||
switch (operation)
|
switch (operation)
|
||||||
@ -263,7 +264,7 @@ bool MainWindow::canPerformOperation(Operation operation) const
|
|||||||
return isSelected;
|
return isSelected;
|
||||||
|
|
||||||
case Operation::RestoreRemovedItems:
|
case Operation::RestoreRemovedItems:
|
||||||
return !m_trashBin.empty();
|
return !m_model->isTrashBinEmpty();
|
||||||
|
|
||||||
case Operation::Cut:
|
case Operation::Cut:
|
||||||
case Operation::Copy:
|
case Operation::Copy:
|
||||||
@ -277,7 +278,7 @@ bool MainWindow::canPerformOperation(Operation operation) const
|
|||||||
return isSelected;
|
return isSelected;
|
||||||
|
|
||||||
case Operation::Group:
|
case Operation::Group:
|
||||||
return isSelected;
|
return isMultiSelected;
|
||||||
case Operation::Ungroup:
|
case Operation::Ungroup:
|
||||||
return m_model->isGrouped(selection);
|
return m_model->isGrouped(selection);
|
||||||
|
|
||||||
@ -323,10 +324,15 @@ void MainWindow::performOperation(Operation operation)
|
|||||||
case Operation::Paste:
|
case Operation::Paste:
|
||||||
case Operation::RotateLeft:
|
case Operation::RotateLeft:
|
||||||
case Operation::RotateRight:
|
case Operation::RotateRight:
|
||||||
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Operation::Group:
|
case Operation::Group:
|
||||||
|
m_model->group(ui->documentItemsView->selectionModel()->selection().indexes());
|
||||||
|
break;
|
||||||
|
|
||||||
case Operation::Ungroup:
|
case Operation::Ungroup:
|
||||||
|
m_model->ungroup(ui->documentItemsView->selectionModel()->selection().indexes());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Operation::SelectNone:
|
case Operation::SelectNone:
|
||||||
|
@ -102,7 +102,6 @@ private:
|
|||||||
PageItemDelegate* m_delegate;
|
PageItemDelegate* m_delegate;
|
||||||
Settings m_settings;
|
Settings m_settings;
|
||||||
QSignalMapper m_mapper;
|
QSignalMapper m_mapper;
|
||||||
std::vector<PageGroupItem> m_trashBin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdfdocpage
|
} // namespace pdfdocpage
|
||||||
|
@ -125,6 +125,19 @@ const PageGroupItem* PageItemModel::getItem(const QModelIndex& index) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PageGroupItem* PageItemModel::getItem(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (index.isValid())
|
||||||
|
{
|
||||||
|
if (index.row() < m_pageGroupItems.size())
|
||||||
|
{
|
||||||
|
return &m_pageGroupItems.at(index.row());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool PageItemModel::isGrouped(const QModelIndexList& indices) const
|
bool PageItemModel::isGrouped(const QModelIndexList& indices) const
|
||||||
{
|
{
|
||||||
for (const QModelIndex& index : indices)
|
for (const QModelIndex& index : indices)
|
||||||
@ -161,6 +174,88 @@ QItemSelection PageItemModel::getSelectionLandscape() const
|
|||||||
return getSelectionImpl([](const PageGroupItem::GroupItem& groupItem) { return groupItem.rotatedPageDimensionsMM.width() >= groupItem.rotatedPageDimensionsMM.height(); });
|
return getSelectionImpl([](const PageGroupItem::GroupItem& groupItem) { return groupItem.rotatedPageDimensionsMM.width() >= groupItem.rotatedPageDimensionsMM.height(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PageItemModel::group(const QModelIndexList& list)
|
||||||
|
{
|
||||||
|
if (list.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> groupedIndices;
|
||||||
|
groupedIndices.reserve(list.size());
|
||||||
|
std::transform(list.cbegin(), list.cend(), std::back_inserter(groupedIndices), [](const auto& index) { return index.row(); });
|
||||||
|
std::sort(groupedIndices.begin(), groupedIndices.end());
|
||||||
|
|
||||||
|
std::vector<PageGroupItem> newPageGroupItems;
|
||||||
|
std::vector<PageGroupItem::GroupItem> newGroups;
|
||||||
|
newPageGroupItems.reserve(m_pageGroupItems.size());
|
||||||
|
for (size_t i = 0; i < m_pageGroupItems.size(); ++i)
|
||||||
|
{
|
||||||
|
const PageGroupItem& item = m_pageGroupItems[i];
|
||||||
|
if (std::binary_search(groupedIndices.cbegin(), groupedIndices.cend(), i))
|
||||||
|
{
|
||||||
|
newGroups.insert(newGroups.end(), item.groups.begin(), item.groups.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newPageGroupItems.push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PageGroupItem newItem;
|
||||||
|
newItem.groups = qMove(newGroups);
|
||||||
|
updateItemCaptionAndTags(newItem);
|
||||||
|
newPageGroupItems.insert(std::next(newPageGroupItems.begin(), groupedIndices.front()), qMove(newItem));
|
||||||
|
|
||||||
|
if (newPageGroupItems != m_pageGroupItems)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_pageGroupItems = std::move(newPageGroupItems);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageItemModel::ungroup(const QModelIndexList& list)
|
||||||
|
{
|
||||||
|
if (list.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> ungroupedIndices;
|
||||||
|
ungroupedIndices.reserve(list.size());
|
||||||
|
std::transform(list.cbegin(), list.cend(), std::back_inserter(ungroupedIndices), [](const auto& index) { return index.row(); });
|
||||||
|
std::sort(ungroupedIndices.begin(), ungroupedIndices.end());
|
||||||
|
|
||||||
|
std::vector<PageGroupItem> newPageGroupItems;
|
||||||
|
newPageGroupItems.reserve(m_pageGroupItems.size());
|
||||||
|
for (size_t i = 0; i < m_pageGroupItems.size(); ++i)
|
||||||
|
{
|
||||||
|
const PageGroupItem& item = m_pageGroupItems[i];
|
||||||
|
if (item.isGrouped() && std::binary_search(ungroupedIndices.cbegin(), ungroupedIndices.cend(), i))
|
||||||
|
{
|
||||||
|
for (const PageGroupItem::GroupItem& groupItem : item.groups)
|
||||||
|
{
|
||||||
|
PageGroupItem newItem;
|
||||||
|
newItem.groups = { groupItem };
|
||||||
|
updateItemCaptionAndTags(newItem);
|
||||||
|
newPageGroupItems.push_back(qMove(newItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newPageGroupItems.push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPageGroupItems != m_pageGroupItems)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_pageGroupItems = std::move(newPageGroupItems);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QItemSelection PageItemModel::getSelectionImpl(std::function<bool (const PageGroupItem::GroupItem&)> filter) const
|
QItemSelection PageItemModel::getSelectionImpl(std::function<bool (const PageGroupItem::GroupItem&)> filter) const
|
||||||
{
|
{
|
||||||
QItemSelection result;
|
QItemSelection result;
|
||||||
@ -235,5 +330,50 @@ QString PageItemModel::getGroupNameFromDocument(int index) const
|
|||||||
return fileInfo.fileName();
|
return fileInfo.fileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PageItemModel::updateItemCaptionAndTags(PageGroupItem& item) const
|
||||||
|
{
|
||||||
|
std::set<int> documentIndices = item.getDocumentIndices();
|
||||||
|
const size_t pageCount = item.groups.size();
|
||||||
|
|
||||||
|
if (documentIndices.size() == 1)
|
||||||
|
{
|
||||||
|
pdf::PDFClosedIntervalSet pageSet;
|
||||||
|
for (const auto& groupItem : item.groups)
|
||||||
|
{
|
||||||
|
pageSet.addInterval(groupItem.pageIndex, groupItem.pageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.groupName = getGroupNameFromDocument(*documentIndices.begin());
|
||||||
|
item.pagesCaption = pageSet.toText(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.groupName = tr("Document collection");
|
||||||
|
item.pagesCaption = tr("Page Count: %1").arg(item.groups.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
item.tags.clear();
|
||||||
|
if (pageCount > 1)
|
||||||
|
{
|
||||||
|
item.tags << QString("#00CC00@+%1").arg(pageCount - 1);
|
||||||
|
}
|
||||||
|
if (documentIndices.size() > 1)
|
||||||
|
{
|
||||||
|
item.tags << QString("#BBBB00@Collection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<int> PageGroupItem::getDocumentIndices() const
|
||||||
|
{
|
||||||
|
std::set<int> result;
|
||||||
|
|
||||||
|
for (const auto& groupItem : groups)
|
||||||
|
{
|
||||||
|
result.insert(groupItem.documentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace pdfdocpage
|
} // namespace pdfdocpage
|
||||||
|
@ -34,6 +34,8 @@ struct PageGroupItem
|
|||||||
|
|
||||||
struct GroupItem
|
struct GroupItem
|
||||||
{
|
{
|
||||||
|
auto operator<=>(const GroupItem&) const = default;
|
||||||
|
|
||||||
int documentIndex = 0;
|
int documentIndex = 0;
|
||||||
pdf::PDFInteger pageIndex;
|
pdf::PDFInteger pageIndex;
|
||||||
QSizeF rotatedPageDimensionsMM;
|
QSizeF rotatedPageDimensionsMM;
|
||||||
@ -41,7 +43,11 @@ struct PageGroupItem
|
|||||||
|
|
||||||
std::vector<GroupItem> groups;
|
std::vector<GroupItem> groups;
|
||||||
|
|
||||||
|
auto operator<=>(const PageGroupItem&) const = default;
|
||||||
|
|
||||||
bool isGrouped() const { return groups.size() > 1; }
|
bool isGrouped() const { return groups.size() > 1; }
|
||||||
|
|
||||||
|
std::set<int> getDocumentIndices() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DocumentItem
|
struct DocumentItem
|
||||||
@ -77,21 +83,35 @@ public:
|
|||||||
/// \param index Index
|
/// \param index Index
|
||||||
const PageGroupItem* getItem(const QModelIndex& index) const;
|
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
|
/// Returns true, if grouped item exists in the indices
|
||||||
bool isGrouped(const QModelIndexList& indices) const;
|
bool isGrouped(const QModelIndexList& indices) const;
|
||||||
|
|
||||||
|
/// Returns true, if trash bin is empty
|
||||||
|
bool isTrashBinEmpty() const { return m_trashBin.empty(); }
|
||||||
|
|
||||||
QItemSelection getSelectionEven() const;
|
QItemSelection getSelectionEven() const;
|
||||||
QItemSelection getSelectionOdd() const;
|
QItemSelection getSelectionOdd() const;
|
||||||
QItemSelection getSelectionPortrait() const;
|
QItemSelection getSelectionPortrait() const;
|
||||||
QItemSelection getSelectionLandscape() const;
|
QItemSelection getSelectionLandscape() const;
|
||||||
|
|
||||||
|
void group(const QModelIndexList& list);
|
||||||
|
void ungroup(const QModelIndexList& list);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createDocumentGroup(int index);
|
void createDocumentGroup(int index);
|
||||||
QString getGroupNameFromDocument(int index) const;
|
QString getGroupNameFromDocument(int index) const;
|
||||||
|
void updateItemCaptionAndTags(PageGroupItem& item) const;
|
||||||
|
|
||||||
QItemSelection getSelectionImpl(std::function<bool(const PageGroupItem::GroupItem&)> filter) const;
|
QItemSelection getSelectionImpl(std::function<bool(const PageGroupItem::GroupItem&)> filter) const;
|
||||||
|
|
||||||
std::vector<PageGroupItem> m_pageGroupItems;
|
std::vector<PageGroupItem> m_pageGroupItems;
|
||||||
std::map<int, DocumentItem> m_documents;
|
std::map<int, DocumentItem> m_documents;
|
||||||
|
std::vector<PageGroupItem> m_trashBin;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdfdocpage
|
} // namespace pdfdocpage
|
||||||
|
Reference in New Issue
Block a user