Issue #76: Basic functionality of outline editing

This commit is contained in:
Jakub Melka 2023-10-01 17:35:42 +02:00
parent a29a35e029
commit be0d02bb5c
10 changed files with 711 additions and 13 deletions

View File

@ -370,6 +370,17 @@ void PDFAction::fillActionList(std::vector<const PDFAction*>& actionList) const
}
}
void PDFAction::cloneActionList(const PDFAction* sourceAction)
{
if (sourceAction)
{
for (const auto& action : sourceAction->m_nextActions)
{
m_nextActions.push_back(action->clone());
}
}
}
PDFDestination PDFDestination::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDFDestination result;
@ -711,6 +722,16 @@ QString PDFActionURI::getURIString() const
return QString::fromUtf8(m_URI);
}
void PDFActionURI::setURI(const QByteArray& newURI)
{
m_URI = newURI;
}
void PDFActionURI::setIsMap(bool newIsMap)
{
m_isMap = newIsMap;
}
void PDFActionGoTo::setDestination(const PDFDestination& destination)
{
m_destination = destination;
@ -721,4 +742,153 @@ void PDFActionGoTo::setStructureDestination(const PDFDestination& structureDesti
m_structureDestination = structureDestination;
}
PDFActionPtr PDFActionGoTo::clone() const
{
PDFActionGoTo* clonedAction = new PDFActionGoTo(getDestination(), getStructureDestination());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionGoToR::clone() const
{
PDFActionGoToR* clonedAction = new PDFActionGoToR(getDestination(),
getStructureDestination(),
getFileSpecification(),
isNewWindow());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionGoToE::clone() const
{
PDFActionGoToE* clonedAction = new PDFActionGoToE(getDestination(),
getFileSpecification(),
isNewWindow(),
getTarget());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionGoToDp::clone() const
{
PDFActionGoToDp* clonedAction = new PDFActionGoToDp(getDocumentPart());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionLaunch::clone() const
{
PDFActionLaunch* clonedAction = new PDFActionLaunch(getFileSpecification(), isNewWindow(), getWinSpecification());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionThread::clone() const
{
PDFActionThread* clonedAction = new PDFActionThread(getFileSpecification(), getThread(), getBead());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionURI::clone() const
{
PDFActionURI* clonedAction = new PDFActionURI(getURI(), isMap());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionSound::clone() const
{
PDFActionSound* clonedAction = new PDFActionSound(*getSound(), getVolume(), isSynchronous(), isRepeat(), isMix());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionMovie::clone() const
{
PDFActionMovie* clonedAction = new PDFActionMovie(getAnnotation(), getTitle(), getOperation());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionHide::clone() const
{
PDFActionHide* clonedAction = new PDFActionHide(getAnnotations(), getFieldNames(), isHide());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionNamed::clone() const
{
PDFActionNamed* clonedAction = new PDFActionNamed(getNamedActionType(), getCustomNamedAction());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionSetOCGState::clone() const
{
PDFActionSetOCGState* clonedAction = new PDFActionSetOCGState(getStateChangeItems(), isRadioButtonsPreserved());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionTransition::clone() const
{
PDFActionTransition* clonedAction = new PDFActionTransition(getTransition());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionGoTo3DView::clone() const
{
PDFActionGoTo3DView* clonedAction = new PDFActionGoTo3DView(getAnnotation(), getView());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionJavaScript::clone() const
{
PDFActionJavaScript* clonedAction = new PDFActionJavaScript(getJavaScript());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionRichMediaExecute::clone() const
{
PDFActionRichMediaExecute* clonedAction = new PDFActionRichMediaExecute(getRichMediaAnnotation(),
getRichMediaInstance(),
getCommand(),
getArguments());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionSubmitForm::clone() const
{
PDFActionSubmitForm* clonedAction = new PDFActionSubmitForm(getFieldScope(), getFieldList(), getUrl(), getCharset(), getFlags());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionResetForm::clone() const
{
PDFActionResetForm* clonedAction = new PDFActionResetForm(getFieldScope(), getFieldList(), getFlags());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionImportDataForm::clone() const
{
PDFActionImportDataForm* clonedAction = new PDFActionImportDataForm(getFile());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
PDFActionPtr PDFActionRendition::clone() const
{
PDFActionRendition* clonedAction = new PDFActionRendition(m_rendition, getAnnotation(), getOperation(), getJavaScript());
clonedAction->cloneActionList(this);
return PDFActionPtr(clonedAction);
}
} // namespace pdf

View File

@ -169,6 +169,12 @@ public:
/// Returns list of actions to be executed
std::vector<const PDFAction*> getActionList() const;
/// Clone action
virtual PDFActionPtr clone() const = 0;
protected:
void cloneActionList(const PDFAction* sourceAction);
private:
static PDFActionPtr parseImpl(const PDFObjectStorage* storage, PDFObject object, std::set<PDFObjectReference>& usedReferences);
@ -180,7 +186,7 @@ private:
/// Regular go-to action. Can contain also structure destinations, both regular page destination
/// and structure destination are present, because if structure destination fails, then
/// page destination can be used as fallback resolution.
class PDFActionGoTo : public PDFAction
class PDF4QTLIBSHARED_EXPORT PDFActionGoTo : public PDFAction
{
public:
explicit inline PDFActionGoTo(PDFDestination destination, PDFDestination structureDestination) :
@ -194,6 +200,8 @@ public:
void setDestination(const PDFDestination& destination);
void setStructureDestination(const PDFDestination& structureDestination);
virtual PDFActionPtr clone() const override;
private:
PDFDestination m_destination;
PDFDestination m_structureDestination;
@ -218,6 +226,8 @@ public:
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
bool isNewWindow() const { return m_newWindow; }
virtual PDFActionPtr clone() const override;
private:
PDFDestination m_destination;
PDFDestination m_structureDestination;
@ -244,6 +254,8 @@ public:
bool isNewWindow() const { return m_newWindow; }
const PDFObject& getTarget() const { return m_target; }
virtual PDFActionPtr clone() const override;
private:
PDFDestination m_destination;
PDFFileSpecification m_fileSpecification;
@ -262,6 +274,8 @@ public:
PDFObjectReference getDocumentPart() const { return m_documentPart; }
virtual PDFActionPtr clone() const override;
private:
PDFObjectReference m_documentPart;
};
@ -293,6 +307,8 @@ public:
const Win& getWinSpecification() const { return m_win; }
bool isNewWindow() const { return m_newWindow; }
virtual PDFActionPtr clone() const override;
private:
PDFFileSpecification m_fileSpecification;
bool m_newWindow = false;
@ -319,6 +335,8 @@ public:
const Thread& getThread() const { return m_thread; }
const Bead& getBead() const { return m_bead; }
virtual PDFActionPtr clone() const override;
private:
PDFFileSpecification m_fileSpecification;
Thread m_thread;
@ -344,6 +362,11 @@ public:
/// to PDF specification, URI is UTF-8 encoded string.
QString getURIString() const;
virtual PDFActionPtr clone() const override;
void setURI(const QByteArray& newURI);
void setIsMap(bool newIsMap);
private:
QByteArray m_URI;
bool m_isMap;
@ -370,6 +393,8 @@ public:
bool isRepeat() const { return m_isRepeat; }
bool isMix() const { return m_isMix; }
virtual PDFActionPtr clone() const override;
private:
PDFSound m_sound;
PDFReal m_volume;
@ -403,6 +428,8 @@ public:
const QString& getTitle() const { return m_title; }
Operation getOperation() const { return m_operation; }
virtual PDFActionPtr clone() const override;
private:
PDFObjectReference m_annotation;
QString m_title;
@ -420,12 +447,22 @@ public:
}
explicit inline PDFActionHide(const std::vector<PDFObjectReference>& annotations, const std::vector<QString>& fieldNames, bool hide) :
m_annotations(annotations),
m_fieldNames(fieldNames),
m_hide(hide)
{
}
virtual ActionType getType() const override { return ActionType::Hide; }
const std::vector<PDFObjectReference>& getAnnotations() const { return m_annotations; }
const std::vector<QString>& getFieldNames() const { return m_fieldNames; }
bool isHide() const { return m_hide; }
virtual PDFActionPtr clone() const override;
private:
std::vector<PDFObjectReference> m_annotations;
std::vector<QString> m_fieldNames;
@ -451,11 +488,20 @@ public:
}
explicit inline PDFActionNamed(NamedActionType namedActionType, const QByteArray& customNamedAction) :
m_namedActionType(namedActionType),
m_customNamedAction(customNamedAction)
{
}
virtual ActionType getType() const override { return ActionType::Named; }
NamedActionType getNamedActionType() const { return m_namedActionType; }
const QByteArray& getCustomNamedAction() const { return m_customNamedAction; }
virtual PDFActionPtr clone() const override;
private:
NamedActionType m_namedActionType;
QByteArray m_customNamedAction;
@ -475,7 +521,7 @@ public:
using StateChangeItem = std::pair<SwitchType, PDFObjectReference>;
using StateChangeItems = std::vector<StateChangeItem>;
explicit inline PDFActionSetOCGState(StateChangeItems&& stateChangeItems, bool isRadioButtonsPreserved) :
explicit inline PDFActionSetOCGState(StateChangeItems stateChangeItems, bool isRadioButtonsPreserved) :
m_items(qMove(stateChangeItems)),
m_isRadioButtonsPreserved(isRadioButtonsPreserved)
{
@ -487,6 +533,8 @@ public:
const StateChangeItems& getStateChangeItems() const { return m_items; }
bool isRadioButtonsPreserved() const { return m_isRadioButtonsPreserved; }
virtual PDFActionPtr clone() const override;
private:
StateChangeItems m_items;
bool m_isRadioButtonsPreserved;
@ -505,7 +553,7 @@ public:
Play = 4
};
explicit inline PDFActionRendition(std::optional<PDFRendition>&& rendition, PDFObjectReference annotation, Operation operation, QString javascript) :
explicit inline PDFActionRendition(std::optional<PDFRendition> rendition, PDFObjectReference annotation, Operation operation, QString javascript) :
m_rendition(qMove(rendition)),
m_annotation(annotation),
m_operation(operation),
@ -521,6 +569,8 @@ public:
Operation getOperation() const { return m_operation; }
const QString& getJavaScript() const { return m_javascript; }
virtual PDFActionPtr clone() const override;
private:
std::optional<PDFRendition> m_rendition;
PDFObjectReference m_annotation;
@ -531,7 +581,7 @@ private:
class PDFActionTransition : public PDFAction
{
public:
explicit inline PDFActionTransition(PDFPageTransition&& transition) :
explicit inline PDFActionTransition(PDFPageTransition transition) :
m_transition(qMove(transition))
{
@ -541,6 +591,8 @@ public:
const PDFPageTransition& getTransition() const { return m_transition; }
virtual PDFActionPtr clone() const override;
private:
PDFPageTransition m_transition;
};
@ -560,6 +612,8 @@ public:
const PDFObject& getAnnotation() const { return m_annotation; }
const PDFObject& getView() const { return m_view; }
virtual PDFActionPtr clone() const override;
private:
PDFObject m_annotation;
PDFObject m_view;
@ -578,6 +632,8 @@ public:
const QString& getJavaScript() const { return m_javaScript; }
virtual PDFActionPtr clone() const override;
private:
QString m_javaScript;
};
@ -604,6 +660,8 @@ public:
QString getCommand() const { return m_command; }
PDFObject getArguments() const { return m_arguments; }
virtual PDFActionPtr clone() const override;
private:
PDFObjectReference m_richMediaAnnotation;
PDFObjectReference m_richMediaInstance;
@ -689,6 +747,8 @@ public:
const QByteArray& getCharset() const { return m_charset; }
SubmitFlags getFlags() const { return m_flags; }
virtual PDFActionPtr clone() const override;
private:
PDFFileSpecification m_url;
QByteArray m_charset;
@ -717,6 +777,8 @@ public:
ResetFlags getFlags() const { return m_flags; }
virtual PDFActionPtr clone() const override;
private:
ResetFlags m_flags = None;
};
@ -735,6 +797,8 @@ public:
const PDFFileSpecification& getFile() const { return m_file; }
virtual PDFActionPtr clone() const override;
private:
PDFFileSpecification m_file;
};

View File

@ -35,6 +35,13 @@ PDFTreeItem::~PDFTreeItem()
qDeleteAll(m_children);
}
PDFTreeItem* PDFTreeItem::takeChild(int index)
{
PDFTreeItem* item = m_children.at(index);
m_children.erase(m_children.begin() + index);
return item;
}
PDFTreeItemModel::PDFTreeItemModel(QObject* parent) :
QAbstractItemModel(parent),
m_document(nullptr)
@ -368,6 +375,7 @@ QVariant PDFOutlineTreeItemModel::data(const QModelIndex& index, int role) const
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
return outlineItem->getTitle();
case Qt::ForegroundRole:
@ -409,6 +417,11 @@ void PDFOutlineTreeItemModel::update()
}
if (outlineRoot)
{
if (m_editable)
{
outlineRoot = outlineRoot->clone();
}
m_rootItem.reset(new PDFOutlineTreeItem(nullptr, qMove(outlineRoot)));
}
else
@ -425,13 +438,16 @@ Qt::ItemFlags PDFOutlineTreeItemModel::flags(const QModelIndex& index) const
if (!index.isValid())
{
if (m_editable)
{
flags = flags | Qt::ItemIsDropEnabled;
}
return flags;
}
const PDFOutlineTreeItem* item = static_cast<const PDFOutlineTreeItem*>(index.internalPointer());
if (item->getChildCount() == 0)
if (m_editable)
{
flags = flags | Qt::ItemNeverHasChildren;
flags = flags | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
return flags;
@ -449,6 +465,259 @@ const PDFAction* PDFOutlineTreeItemModel::getAction(const QModelIndex& index) co
return nullptr;
}
const PDFOutlineItem* PDFOutlineTreeItemModel::getOutlineItem(const QModelIndex& index) const
{
if (index.isValid())
{
const PDFOutlineTreeItem* item = static_cast<const PDFOutlineTreeItem*>(index.internalPointer());
const PDFOutlineItem* outlineItem = item->getOutlineItem();
return outlineItem;
}
return nullptr;
}
PDFOutlineItem* PDFOutlineTreeItemModel::getOutlineItem(const QModelIndex& index)
{
if (index.isValid())
{
PDFOutlineTreeItem* item = static_cast<PDFOutlineTreeItem*>(index.internalPointer());
PDFOutlineItem* outlineItem = item->getOutlineItem();
return outlineItem;
}
return nullptr;
}
void PDFOutlineTreeItemModel::setFontBold(const QModelIndex& index, bool value)
{
if (PDFOutlineItem* outlineItem = getOutlineItem(index))
{
if (outlineItem->isFontBold() != value)
{
outlineItem->setFontBold(value);
Q_EMIT dataChanged(index, index);
}
}
}
void PDFOutlineTreeItemModel::setFontItalics(const QModelIndex& index, bool value)
{
if (PDFOutlineItem* outlineItem = getOutlineItem(index))
{
if (outlineItem->isFontItalics() != value)
{
outlineItem->setFontItalics(value);
Q_EMIT dataChanged(index, index);
}
}
}
void PDFOutlineTreeItemModel::setDestination(const QModelIndex& index, const PDFDestination& destination)
{
if (PDFOutlineItem* outlineItem = getOutlineItem(index))
{
outlineItem->setAction(PDFActionPtr(new PDFActionGoTo(destination, PDFDestination())));
Q_EMIT dataChanged(index, index);
}
}
bool PDFOutlineTreeItemModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!m_editable || !index.isValid() || role != Qt::EditRole)
{
return false;
}
PDFOutlineTreeItem* item = static_cast<PDFOutlineTreeItem*>(index.internalPointer());
PDFOutlineItem* outlineItem = item->getOutlineItem();
if (outlineItem->getTitle() != value.toString())
{
outlineItem->setTitle(value.toString());
Q_EMIT dataChanged(index, index);
}
return true;
}
Qt::DropActions PDFOutlineTreeItemModel::supportedDropActions() const
{
if (!m_editable)
{
return Qt::IgnoreAction;
}
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions PDFOutlineTreeItemModel::supportedDragActions() const
{
if (!m_editable)
{
return Qt::IgnoreAction;
}
return Qt::CopyAction | Qt::MoveAction;
}
QStringList PDFOutlineTreeItemModel::mimeTypes() const
{
return QStringList() << "application/PDF4QT_PDFOutlineTreeItemModel";
}
QMimeData* PDFOutlineTreeItemModel::mimeData(const QModelIndexList& indexes) const
{
QMimeData* mimeData = new QMimeData();
if (indexes.size() == 1)
{
QByteArray ba;
{
QDataStream stream(&ba, QDataStream::WriteOnly);
stream << indexes.front().internalId();
}
mimeData->setData(mimeTypes().front(), ba);
}
return mimeData;
}
bool PDFOutlineTreeItemModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
return action == Qt::MoveAction && data->hasFormat(mimeTypes().front());
}
bool PDFOutlineTreeItemModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
{
if (!data || action != Qt::MoveAction)
{
return false;
}
QByteArray pointerData = data->data(mimeTypes().front());
QDataStream stream(pointerData);
quintptr pointer = 0;
stream >> pointer;
PDFOutlineTreeItem* item = reinterpret_cast<PDFOutlineTreeItem*>(pointer);
QModelIndex sourceIndex = createIndex(item->getRow(), column, item);
return moveRow(sourceIndex.parent(), sourceIndex.row(), parent, row);
}
bool PDFOutlineTreeItemModel::insertRows(int row, int count, const QModelIndex& parent)
{
if (!m_editable || row < 0 || count <= 0 || row > rowCount(parent))
{
return false;
}
beginInsertRows(parent, row, row + count - 1);
PDFOutlineTreeItem* item = parent.isValid() ? static_cast<PDFOutlineTreeItem*>(parent.internalPointer()) : static_cast<PDFOutlineTreeItem*>(m_rootItem.get());
while (count > 0)
{
QSharedPointer<PDFOutlineItem> outlineItem(new PDFOutlineItem());
outlineItem->setTitle(tr("Item %1").arg(row + 1));
PDFOutlineTreeItem* newTreeItem = new PDFOutlineTreeItem(item, qMove(outlineItem));
item->insertCreatedChild(row, newTreeItem);
++row;
--count;
}
endInsertRows();
return true;
}
bool PDFOutlineTreeItemModel::removeRows(int row, int count, const QModelIndex& parent)
{
if (!m_editable || count <= 0 || row < 0 || row + count > rowCount(parent))
{
return false;
}
beginRemoveRows(parent, row, row + count - 1);
PDFOutlineTreeItem* item = parent.isValid() ? static_cast<PDFOutlineTreeItem*>(parent.internalPointer()) : static_cast<PDFOutlineTreeItem*>(m_rootItem.get());
while (count > 0)
{
delete item->takeChild(row);
--count;
}
endRemoveRows();
return false;
}
bool PDFOutlineTreeItemModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild)
{
if (sourceRow < 0 || count <= 0 || (sourceParent == destinationParent && (sourceRow == destinationChild || sourceRow + count <= destinationChild)))
{
return false;
}
PDFOutlineTreeItem* sourceNode = nullptr;
PDFOutlineTreeItem* destNode = nullptr;
if (sourceParent.isValid())
{
sourceNode = static_cast<PDFOutlineTreeItem*>(sourceParent.internalPointer());
}
else
{
sourceNode = static_cast<PDFOutlineTreeItem*>(m_rootItem.get());
}
if (destinationParent.isValid())
{
destNode = static_cast<PDFOutlineTreeItem*>(destinationParent.internalPointer());
}
else
{
destNode = static_cast<PDFOutlineTreeItem*>(m_rootItem.get());
}
if (sourceRow + count > sourceNode->getChildCount())
{
return false;
}
if (destinationChild < 0)
{
destinationChild = 0;
}
// Signalizace začátku přesunu řádků
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild))
{
return false;
}
QList<PDFOutlineTreeItem*> nodesToMove;
for (int i = 0; i < count; ++i)
{
nodesToMove.append(static_cast<PDFOutlineTreeItem*>(sourceNode->takeChild(sourceRow)));
}
for (PDFOutlineTreeItem* node : nodesToMove)
{
destNode->insertCreatedChild(destinationChild++, node);
}
// Signalizace konce přesunu řádků
endMoveRows();
return true;
}
int PDFAttachmentsTreeItemModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);

View File

@ -36,6 +36,7 @@ class PDFModifiedDocument;
class PDFFileSpecification;
class PDFOptionalContentActivity;
class PDFDrawWidgetProxy;
class PDFDestination;
/// Represents tree item in the GUI tree
class PDF4QTLIBSHARED_EXPORT PDFTreeItem
@ -53,6 +54,20 @@ public:
return item;
}
template<typename T, typename... Arguments>
inline T* insertChild(int position, Arguments&&... arguments)
{
T* item = new T(this, std::forward(arguments)...);
m_children.insert(std::next(m_children.begin(), position), item);
return item;
}
void insertCreatedChild(int position, PDFTreeItem* item)
{
item->m_parent = this;
m_children.insert(std::next(m_children.begin(), position), item);
}
void addCreatedChild(PDFTreeItem* item)
{
item->m_parent = this;
@ -64,6 +79,7 @@ public:
const PDFTreeItem* getChild(int index) const { return m_children.at(index); }
PDFTreeItem* getChild(int index) { return m_children.at(index); }
const PDFTreeItem* getParent() const { return m_parent; }
PDFTreeItem* takeChild(int index);
private:
PDFTreeItem* m_parent = nullptr;
@ -145,6 +161,7 @@ public:
explicit PDFOutlineTreeItem(PDFOutlineTreeItem* parent, QSharedPointer<PDFOutlineItem> outlineItem);
const PDFOutlineItem* getOutlineItem() const { return m_outlineItem.data(); }
PDFOutlineItem* getOutlineItem() { return m_outlineItem.data(); }
private:
QSharedPointer<PDFOutlineItem> m_outlineItem;
@ -154,9 +171,10 @@ class PDF4QTLIBSHARED_EXPORT PDFOutlineTreeItemModel : public PDFTreeItemModel
{
Q_OBJECT
public:
PDFOutlineTreeItemModel(QIcon icon, QObject* parent) :
PDFOutlineTreeItemModel(QIcon icon, bool editable, QObject* parent) :
PDFTreeItemModel(parent),
m_icon(qMove(icon))
m_icon(qMove(icon)),
m_editable(editable)
{
}
@ -165,14 +183,37 @@ public:
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual void update() override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override;
virtual Qt::DropActions supportedDropActions() const override;
virtual Qt::DropActions supportedDragActions() const override;
virtual bool insertRows(int row, int count, const QModelIndex& parent) override;
virtual bool removeRows(int row, int count, const QModelIndex& parent) override;
virtual bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild) 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;
/// Returns action assigned to the index. If index is invalid, or
/// points to the invalid item, nullptr is returned.
/// \param index Index of the outline item
const PDFAction* getAction(const QModelIndex& index) const;
/// Returns the outline item for the given index, or nullptr
/// if no outline item is assigned to that index.
const PDFOutlineItem* getOutlineItem(const QModelIndex& index) const;
/// Returns the outline item for the given index, or nullptr
/// if no outline item is assigned to that index.
PDFOutlineItem* getOutlineItem(const QModelIndex& index);
void setFontBold(const QModelIndex& index, bool value);
void setFontItalics(const QModelIndex& index, bool value);
void setDestination(const QModelIndex& index, const PDFDestination& destination);
private:
QIcon m_icon;
bool m_editable;
};
class PDF4QTLIBSHARED_EXPORT PDFSelectableOutlineTreeItemModel : public PDFOutlineTreeItemModel
@ -184,7 +225,7 @@ private:
public:
PDFSelectableOutlineTreeItemModel(QIcon icon, QObject* parent) :
BaseClass(qMove(icon), parent)
BaseClass(qMove(icon), false, parent)
{
}

View File

@ -156,6 +156,29 @@ void PDFOutlineItem::apply(const std::function<void (PDFOutlineItem*)>& functor)
}
}
QSharedPointer<PDFOutlineItem> PDFOutlineItem::clone() const
{
QSharedPointer<PDFOutlineItem> result(new PDFOutlineItem());
result->setTitle(getTitle());
result->setTextColor(getTextColor());
result->setStructureElement(getStructureElement());
result->setFontItalics(isFontItalics());
result->setFontBold(isFontBold());
if (auto action = getAction())
{
result->setAction(action->clone());
}
for (size_t i = 0; i < getChildCount(); ++i)
{
result->addChild(getChild(i)->clone());
}
return result;
}
bool PDFOutlineItem::isFontBold() const
{
return m_fontBold;

View File

@ -65,6 +65,8 @@ public:
void apply(const std::function<void(PDFOutlineItem*)>& functor);
QSharedPointer<PDFOutlineItem> clone() const;
private:
static void parseImpl(const PDFObjectStorage* storage,
PDFOutlineItem* parent,

View File

@ -23,6 +23,8 @@
#include "pdfwidgetutils.h"
#include "pdftexttospeech.h"
#include "pdfdbgheap.h"
#include "pdfdrawwidget.h"
#include "pdfwidgettool.h"
#include "pdfdocument.h"
#include "pdfitemmodels.h"
@ -53,6 +55,7 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore,
PDFViewerSettings* settings,
bool editableOutline,
QWidget* parent) :
QWidget(parent),
ui(new Ui::PDFSidebarWidget),
@ -73,9 +76,20 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
// Outline
QIcon bookmarkIcon(":/resources/bookmark.svg");
m_outlineTreeModel = new pdf::PDFOutlineTreeItemModel(qMove(bookmarkIcon), this);
m_outlineTreeModel = new pdf::PDFOutlineTreeItemModel(qMove(bookmarkIcon), editableOutline, this);
ui->bookmarksTreeView->setModel(m_outlineTreeModel);
ui->bookmarksTreeView->header()->hide();
if (editableOutline)
{
ui->bookmarksTreeView->setDragEnabled(true);
ui->bookmarksTreeView->setAcceptDrops(true);
ui->bookmarksTreeView->setDropIndicatorShown(true);
ui->bookmarksTreeView->setDragDropMode(QAbstractItemView::InternalMove);
ui->bookmarksTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->bookmarksTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onBookmarksTreeViewContextMenuRequested);
}
connect(ui->bookmarksTreeView, &QTreeView::clicked, this, &PDFSidebarWidget::onOutlineItemClicked);
// Thumbnails
@ -735,6 +749,119 @@ void PDFSidebarWidget::onSignatureCustomContextMenuRequested(const QPoint& pos)
}
}
void PDFSidebarWidget::onBookmarksTreeViewContextMenuRequested(const QPoint& pos)
{
QMenu contextMenu;
QModelIndex index = ui->bookmarksTreeView->indexAt(pos);
auto onFollow = [this, index]()
{
onOutlineItemClicked(index);
};
auto onInsert = [this, index]()
{
if (index.isValid())
{
ui->bookmarksTreeView->model()->insertRow(index.row() + 1, index.parent());
}
else
{
ui->bookmarksTreeView->model()->insertRow(ui->bookmarksTreeView->model()->rowCount());
}
};
auto onDelete = [this, index]()
{
ui->bookmarksTreeView->model()->removeRow(index.row(), index.parent());
};
auto onRename = [this, index]()
{
ui->bookmarksTreeView->edit(index);
};
QAction* followAction = contextMenu.addAction(tr("Follow"), onFollow);
followAction->setEnabled(index.isValid());
contextMenu.addSeparator();
QAction* deleteAction = contextMenu.addAction(tr("Delete"), onDelete);
QAction* insertAction = contextMenu.addAction(tr("Insert"), this, onInsert);
QAction* renameAction = contextMenu.addAction(tr("Rename"), this, onRename);
deleteAction->setEnabled(index.isValid());
insertAction->setEnabled(true);
renameAction->setEnabled(index.isValid());
contextMenu.addSeparator();
const pdf::PDFOutlineItem* outlineItem = m_outlineTreeModel->getOutlineItem(index);
const bool isFontBold = outlineItem && outlineItem->isFontBold();
const bool isFontItalics = outlineItem && outlineItem->isFontItalics();
auto onFontBold = [this, index, isFontBold]()
{
m_outlineTreeModel->setFontBold(index, !isFontBold);
};
auto onFontItalic = [this, index, isFontItalics]()
{
m_outlineTreeModel->setFontItalics(index, !isFontItalics);
};
QAction* fontBoldAction = contextMenu.addAction(tr("Font Bold"), onFontBold);
QAction* fontItalicAction = contextMenu.addAction(tr("Font Italic"), onFontItalic);
fontBoldAction->setCheckable(true);
fontItalicAction->setCheckable(true);
fontBoldAction->setChecked(isFontBold);
fontItalicAction->setChecked(isFontItalics);
fontBoldAction->setEnabled(index.isValid());
fontItalicAction->setEnabled(index.isValid());
QMenu* submenu = new QMenu(tr("Set Target"), &contextMenu);
QAction* targetAction = contextMenu.addMenu(submenu);
targetAction->setEnabled(index.isValid());
auto createOnSetTarget = [this, index](pdf::DestinationType destinationType)
{
auto onSetTarget = [this, index, destinationType]()
{
pdf::PDFToolManager* toolManager = m_proxy->getWidget()->getToolManager();
auto pickRectangle = [this, index, destinationType](pdf::PDFInteger pageIndex, QRectF rect)
{
pdf::PDFDestination destination;
destination.setDestinationType(destinationType);
destination.setPageIndex(pageIndex);
destination.setPageReference(m_document->getCatalog()->getPage(pageIndex)->getPageReference());
destination.setLeft(rect.left());
destination.setRight(rect.right());
destination.setTop(rect.bottom());
destination.setBottom(rect.top());
destination.setZoom(m_proxy->getZoom());
m_outlineTreeModel->setDestination(index, destination);
};
toolManager->pickRectangle(pickRectangle);
};
return onSetTarget;
};
submenu->addAction(tr("Named Destination"));
submenu->addAction(tr("Fit Page"), createOnSetTarget(pdf::DestinationType::Fit));
submenu->addAction(tr("Fit Page Horizontally"), createOnSetTarget(pdf::DestinationType::FitH));
submenu->addAction(tr("Fit Page Vertically"), createOnSetTarget(pdf::DestinationType::FitV));
submenu->addAction(tr("Fit Rectangle"), createOnSetTarget(pdf::DestinationType::FitR));
submenu->addAction(tr("Fit Bounding Box"), createOnSetTarget(pdf::DestinationType::FitB));
submenu->addAction(tr("Fit Bounding Box Horizontally"), createOnSetTarget(pdf::DestinationType::FitBH));
submenu->addAction(tr("Fit Bounding Box Vertically"), createOnSetTarget(pdf::DestinationType::FitBV));
submenu->addAction(tr("XYZ"), createOnSetTarget(pdf::DestinationType::XYZ));
contextMenu.exec(ui->bookmarksTreeView->mapToGlobal(pos));
}
void PDFSidebarWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);

View File

@ -62,6 +62,7 @@ public:
PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore,
PDFViewerSettings* settings,
bool editableOutline,
QWidget* parent);
virtual ~PDFSidebarWidget() override;
@ -111,6 +112,7 @@ private:
void onAttachmentCustomContextMenuRequested(const QPoint& pos);
void onThumbnailClicked(const QModelIndex& index);
void onSignatureCustomContextMenuRequested(const QPoint &pos);
void onBookmarksTreeViewContextMenuRequested(const QPoint &pos);
struct PageInfo
{

View File

@ -253,7 +253,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
setCentralWidget(m_programController->getPdfWidget());
setFocusProxy(m_programController->getPdfWidget());
m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), this);
m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), true, this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
m_sidebarDockWidget->setObjectName("SidebarDockWidget");
m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);

View File

@ -188,7 +188,7 @@ PDFViewerMainWindowLite::PDFViewerMainWindowLite(QWidget* parent) :
setCentralWidget(m_programController->getPdfWidget());
setFocusProxy(m_programController->getPdfWidget());
m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), this);
m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), false, this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
m_sidebarDockWidget->setObjectName("SidebarDockWidget");
m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);