/**************************************************************************** * Copyright (c) 2017 Zheng Lei (realthunder) * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "Action.h" #include "Application.h" #include "Command.h" #include "Document.h" #include "MainWindow.h" #include "Selection.h" #include "Tree.h" #include "ViewProviderDocumentObject.h" #include "WaitCursor.h" FC_LOG_LEVEL_INIT("CommandLink", true, true) using namespace Gui; static void setLinkLabel(App::DocumentObject* obj, const char* doc, const char* name) { std::string label = obj->Label.getValue(); label = Base::Tools::escapeEncodeString(label); Command::doCommand( Command::Doc, "App.getDocument('%s').getObject('%s').Label='%s'", doc, name, label.c_str() ); } //////////////////////////////////////////////////////////////////////////////////////////// class StdCmdLinkMakeGroup: public Gui::Command { public: StdCmdLinkMakeGroup(); const char* className() const override { return "StdCmdLinkMakeGroup"; } protected: void activated(int iMsg) override; bool isActive() override; Action* createAction() override; void languageChange() override; }; StdCmdLinkMakeGroup::StdCmdLinkMakeGroup() : Command("Std_LinkMakeGroup") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Link Group"); sToolTipText = QT_TR_NOOP("Creates a group of links"); sWhatsThis = "Std_LinkMakeGroup"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "LinkGroup"; } bool StdCmdLinkMakeGroup::isActive() { return !!App::GetApplication().getActiveDocument(); } Action* StdCmdLinkMakeGroup::createAction() { auto pcAction = new ActionGroup(this, getMainWindow()); pcAction->setDropDownMenu(true); applyCommandData(this->className(), pcAction); // add the action items QAction* action = nullptr; action = pcAction->addAction(QObject::tr("Simple Group")); action->setWhatsThis(QString::fromLatin1(getWhatsThis())); action = pcAction->addAction(QObject::tr("Group With Links")); action->setWhatsThis(QString::fromLatin1(getWhatsThis())); action = pcAction->addAction(QObject::tr("Group With Transform Links")); action->setWhatsThis(QString::fromLatin1(getWhatsThis())); return pcAction; } void StdCmdLinkMakeGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } auto pcAction = qobject_cast(_pcAction); QList acts = pcAction->actions(); acts[0]->setText(QObject::tr("Simple Group")); acts[1]->setText(QObject::tr("Group With Links")); acts[2]->setText(QObject::tr("Group With Transform Links")); } void StdCmdLinkMakeGroup::activated(int option) { std::vector objs; std::set objset; auto doc = App::GetApplication().getActiveDocument(); if (!doc) { FC_ERR("no active document"); return; } for (auto& sel : Selection().getCompleteSelection()) { if (sel.pObject && sel.pObject->isAttachedToDocument() && objset.insert(sel.pObject).second) { objs.push_back(sel.pObject); } } Selection().selStackPush(); Selection().clearCompleteSelection(); Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make link group")); try { std::string groupName = doc->getUniqueObjectName("LinkGroup"); Command::doCommand( Command::Doc, "App.getDocument('%s').addObject('App::LinkGroup','%s')", doc->getName(), groupName.c_str() ); if (objs.empty()) { Selection().addSelection(doc->getName(), groupName.c_str()); Selection().selStackPush(); } else { Command::doCommand(Command::Doc, "__objs__ = []"); for (auto obj : objs) { std::string name; if (option != 0 || doc != obj->getDocument()) { name = doc->getUniqueObjectName("Link"); Command::doCommand( Command::Doc, "App.getDocument('%s').addObject('App::Link','%s').setLink(" "App.getDocument('%s').getObject('%s'))", doc->getName(), name.c_str(), obj->getDocument()->getName(), obj->getNameInDocument() ); setLinkLabel(obj, doc->getName(), name.c_str()); if (option == 2) { Command::doCommand( Command::Doc, "App.getDocument('%s').getObject('%s').LinkTransform = True", doc->getName(), name.c_str() ); } else if (obj->getPropertyByName("Placement")) { Command::doCommand( Command::Doc, "App.getDocument('%s').getObject('%s').Placement = " "App.getDocument('%s').getObject('%s').Placement", doc->getName(), name.c_str(), obj->getDocument()->getName(), obj->getNameInDocument() ); } } else { name = obj->getNameInDocument(); } Command::doCommand( Command::Doc, "__objs__.append(App.getDocument('%s').getObject('%s'))", doc->getName(), name.c_str() ); Command::doCommand( Command::Doc, "App.getDocument('%s').getObject('%s').ViewObject.Visibility=False", doc->getName(), name.c_str() ); } Command::doCommand( Command::Doc, "App.getDocument('%s').getObject('%s').setLink(__objs__)", doc->getName(), groupName.c_str() ); Command::doCommand(Command::Doc, "del __objs__"); for (size_t i = 0; i < objs.size(); ++i) { auto name = std::to_string(i) + "."; Selection().addSelection(doc->getName(), groupName.c_str(), name.c_str()); } Selection().selStackPush(); } if (option != 0) { Command::doCommand( Command::Doc, "App.getDocument('%s').getObject('%s').LinkMode = 'Auto Delete'", doc->getName(), groupName.c_str() ); } Command::commitCommand(); } catch (const Base::Exception& e) { QMessageBox::critical( getMainWindow(), QObject::tr("Create link group failed"), QString::fromLatin1(e.what()) ); Command::abortCommand(); e.reportException(); } } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkMake) StdCmdLinkMake::StdCmdLinkMake() : Command("Std_LinkMake") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Make Link"); sToolTipText = QT_TR_NOOP( "A link is an object that references another object, either within the same " "or in another document. Unlike clones, links reference the original shape directly, " "making them more memory-efficient, which helps with the creation of complex assemblies." ); sWhatsThis = "Std_LinkMake"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "Link"; } bool StdCmdLinkMake::isActive() { return App::GetApplication().getActiveDocument(); } void StdCmdLinkMake::activated(int) { auto doc = App::GetApplication().getActiveDocument(); if (!doc) { FC_ERR("no active document"); return; } std::set objs; for (auto& sel : Selection().getCompleteSelection()) { if (sel.pObject && sel.pObject->isAttachedToDocument()) { objs.insert(sel.pObject); } } Selection().selStackPush(); Selection().clearCompleteSelection(); Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make link")); try { if (objs.empty()) { std::string name = doc->getUniqueObjectName("Link"); Command::doCommand( Command::Doc, "App.getDocument('%s').addObject('App::Link','%s')", doc->getName(), name.c_str() ); Selection().addSelection(doc->getName(), name.c_str()); } else { for (auto obj : objs) { std::string name = doc->getUniqueObjectName("Link"); Command::doCommand( Command::Doc, "App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument('%s'" ").%s)", doc->getName(), name.c_str(), obj->getDocument()->getName(), obj->getNameInDocument() ); setLinkLabel(obj, doc->getName(), name.c_str()); Selection().addSelection(doc->getName(), name.c_str()); } } Selection().selStackPush(); Command::commitCommand(); } catch (const Base::Exception& e) { Command::abortCommand(); QMessageBox::critical( getMainWindow(), QObject::tr("Create link failed"), QString::fromLatin1(e.what()) ); e.reportException(); } } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkMakeRelative) StdCmdLinkMakeRelative::StdCmdLinkMakeRelative() : Command("Std_LinkMakeRelative") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Make Sub-Link"); sToolTipText = QT_TR_NOOP("Creates a sub-object or sub-element link"); sWhatsThis = "Std_LinkMakeRelative"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "LinkSub"; } bool StdCmdLinkMakeRelative::isActive() { return Selection().hasSubSelection(nullptr, true); } void StdCmdLinkMakeRelative::activated(int) { auto doc = App::GetApplication().getActiveDocument(); if (!doc) { FC_ERR("no active document"); return; } Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make sub-link")); try { std::map< std::pair, std::pair>> linkInfo; for (auto& sel : Selection().getCompleteSelection(ResolveMode::NoResolve)) { if (!sel.pObject || !sel.pObject->isAttachedToDocument()) { continue; } auto key = std::make_pair(sel.pObject, Data::noElementName(sel.SubName)); auto element = Data::findElementName(sel.SubName); auto& info = linkInfo[key]; info.first = sel.pResolvedObject; if (!Base::Tools::isNullOrEmpty(element)) { info.second.emplace_back(element); } } Selection().selStackPush(); Selection().clearCompleteSelection(); for (auto& v : linkInfo) { auto& key = v.first; auto& info = v.second; std::string name = doc->getUniqueObjectName("Link"); std::ostringstream ss; ss << '['; for (auto& s : info.second) { ss << "'" << s << "',"; } ss << ']'; FCMD_DOC_CMD( doc, "addObject('App::Link','" << name << "').setLink(" << getObjectCmd(key.first) << ",'" << key.second << "'," << ss.str() << ")" ); auto link = doc->getObject(name.c_str()); FCMD_OBJ_CMD(link, "LinkTransform = True"); setLinkLabel(info.first, doc->getName(), name.c_str()); Selection().addSelection(doc->getName(), name.c_str()); } Selection().selStackPush(); Command::commitCommand(); } catch (const Base::Exception& e) { Command::abortCommand(); QMessageBox::critical( getMainWindow(), QObject::tr("Failed to create relative link"), QString::fromLatin1(e.what()) ); e.reportException(); } return; } ///////////////////////////////////////////////////////////////////////////////////// struct Info { bool inited = false; App::DocumentObjectT topParent; std::string subname; App::DocumentObjectT parent; App::DocumentObjectT obj; }; static void linkConvert(bool unlink) { // We are trying to replace an object with a link (App::Link), or replace a // link back to its linked object (i.e. unlink). This is a very complex // operation. It works by reassign the link property of the parent of the // selected object(s) to a newly created link to the original object. // Everything should remain the same. This complexity is now largely handled // by ViewProviderDocumentObject::replaceObject(), which in turn relies on // PropertyLinkBase::CopyOnLinkReplace(). std::map, Info> infos; for (const auto& sel : TreeWidget::getSelection()) { auto obj = sel.vp->getObject(); auto parent = sel.parentVp; if (!parent) { FC_WARN("skip '" << obj->getFullName() << "' with no parent"); continue; } auto parentObj = parent->getObject(); auto& info = infos[std::make_pair(parentObj, obj)]; if (info.inited) { continue; } info.inited = true; if (unlink) { auto linked = obj->getLinkedObject(false); if (!linked || !linked->isAttachedToDocument() || linked == obj) { FC_WARN("skip non link"); continue; } } info.topParent = sel.topParent; info.parent = parentObj; info.obj = obj; } if (infos.empty()) { return; } Selection().selStackPush(); Selection().clearCompleteSelection(); // now, do actual operation const char* transactionName = unlink ? "Unlink" : "Replace with link"; Command::openCommand(transactionName); try { std::unordered_map recomputeSet; for (auto& v : infos) { auto& info = v.second; auto parent = info.parent.getObject(); auto parentVp = freecad_cast( Application::Instance->getViewProvider(parent) ); auto obj = info.obj.getObject(); if (!parent || !obj || !parentVp) { continue; } if (!recomputeSet.contains(parent)) { recomputeSet.emplace(parent, parent); } auto doc = parent->getDocument(); App::DocumentObject* replaceObj; if (unlink) { replaceObj = obj->getLinkedObject(false); if (!replaceObj || !replaceObj->isAttachedToDocument() || replaceObj == obj) { continue; } } else { auto name = doc->getUniqueObjectName("Link"); auto link = doc->addObject(name.c_str()); if (!link) { FC_THROWM(Base::RuntimeError, "Failed to create link"); } link->setLink(-1, obj); link->Label.setValue(obj->Label.getValue()); auto pla = freecad_cast(obj->getPropertyByName("Placement")); if (pla) { link->Placement.setValue(pla->getValue()); } else { link->LinkTransform.setValue(true); } replaceObj = link; } // adjust subname for the new object auto pos = info.subname.rfind('.'); if (pos == std::string::npos && pos) { info.subname.clear(); } else { pos = info.subname.rfind('.', pos - 1); if (pos == std::string::npos) { info.subname.clear(); } else { info.subname.resize(pos + 1); info.subname += replaceObj->getNameInDocument(); info.subname += "."; } } // do the replacement operation if (parentVp->replaceObject(obj, replaceObj) <= 0) { FC_THROWM(Base::RuntimeError, "Failed to change link for " << parent->getFullName()); } } std::vector recomputes; for (auto& v : recomputeSet) { auto obj = v.second.getObject(); if (obj) { recomputes.push_back(obj); } } if (!recomputes.empty()) { recomputes.front()->getDocument()->recompute(recomputes); } Command::commitCommand(); } catch (const Base::Exception& e) { Command::abortCommand(); auto title = unlink ? QObject::tr("Unlink failed") : QObject::tr("Replace link failed"); QMessageBox::critical(getMainWindow(), title, QString::fromLatin1(e.what())); e.reportException(); return; } } static bool linkConvertible(bool unlink) { int count = 0; for (auto& sel : TreeWidget::getSelection()) { auto parent = sel.parentVp; if (!parent) { return false; } auto obj = sel.vp->getObject(); if (unlink) { auto linked = obj->getLinkedObject(false); if (!linked || linked == obj) { return false; } } ++count; } return count != 0; } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkReplace) StdCmdLinkReplace::StdCmdLinkReplace() : Command("Std_LinkReplace") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Replace With Link"); sToolTipText = QT_TR_NOOP("Replaces the selected objects with links"); sWhatsThis = "Std_LinkReplace"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "LinkReplace"; } bool StdCmdLinkReplace::isActive() { return linkConvertible(false); } void StdCmdLinkReplace::activated(int) { linkConvert(false); } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkUnlink) StdCmdLinkUnlink::StdCmdLinkUnlink() : Command("Std_LinkUnlink") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Unlink"); sToolTipText = QT_TR_NOOP("Unlinks the object by placing it directly in the container"); sWhatsThis = "Std_LinkUnlink"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "Unlink"; } bool StdCmdLinkUnlink::isActive() { return linkConvertible(true); } void StdCmdLinkUnlink::activated(int) { linkConvert(true); } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkImport) StdCmdLinkImport::StdCmdLinkImport() : Command("Std_LinkImport") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Import Links"); sToolTipText = QT_TR_NOOP("Imports selected external links"); sWhatsThis = "Std_LinkImport"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "LinkImport"; } static std::map> getLinkImportSelections() { std::map> objMap; for (auto& sel : Selection().getCompleteSelection(ResolveMode::NoResolve)) { auto obj = sel.pObject->resolve(sel.SubName); if (!obj || !obj->isAttachedToDocument()) { continue; } for (auto o : obj->getOutList()) { if (o && o->isAttachedToDocument() && o->getDocument() != obj->getDocument()) { objMap[obj->getDocument()].push_back(obj); break; } } } return objMap; } bool StdCmdLinkImport::isActive() { auto links = getLinkImportSelections(); if (links.empty()) { return false; } for (auto& v : links) { if (v.first->testStatus(App::Document::PartialDoc)) { return false; } } return true; } void StdCmdLinkImport::activated(int) { Command::openCommand(QT_TRANSLATE_NOOP("Command", "Import links")); try { WaitCursor wc; wc.setIgnoreEvents(WaitCursor::NoEvents); for (auto& v : getLinkImportSelections()) { auto doc = v.first; // TODO: Is it possible to do this using interpreter? for (auto obj : doc->importLinks(v.second)) { obj->Visibility.setValue(false); } } Command::commitCommand(); } catch (const Base::Exception& e) { Command::abortCommand(); QMessageBox::critical( getMainWindow(), QObject::tr("Failed to import links"), QString::fromLatin1(e.what()) ); e.reportException(); } } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkImportAll) StdCmdLinkImportAll::StdCmdLinkImportAll() : Command("Std_LinkImportAll") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Import All Links"); sToolTipText = QT_TR_NOOP("Imports all links of the active document"); sWhatsThis = "Std_LinkImportAll"; sStatusTip = sToolTipText; eType = AlterDoc; sPixmap = "LinkImportAll"; } bool StdCmdLinkImportAll::isActive() { auto doc = App::GetApplication().getActiveDocument(); return doc && !doc->testStatus(App::Document::PartialDoc) && App::PropertyXLink::hasXLink(doc); } void StdCmdLinkImportAll::activated(int) { Command::openCommand(QT_TRANSLATE_NOOP("Command", "Import all links")); try { WaitCursor wc; wc.setIgnoreEvents(WaitCursor::NoEvents); auto doc = App::GetApplication().getActiveDocument(); if (doc) { for (auto obj : doc->importLinks()) { obj->Visibility.setValue(false); } } Command::commitCommand(); } catch (const Base::Exception& e) { QMessageBox::critical( getMainWindow(), QObject::tr("Failed to import all links"), QString::fromLatin1(e.what()) ); Command::abortCommand(); e.reportException(); } } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkSelectLinked) StdCmdLinkSelectLinked::StdCmdLinkSelectLinked() : Command("Std_LinkSelectLinked") { sGroup = "Link"; sMenuText = QT_TR_NOOP("&Go to Linked Object"); sToolTipText = QT_TR_NOOP("Selects the linked object and switches to its original document"); sWhatsThis = "Std_LinkSelectLinked"; sStatusTip = sToolTipText; eType = AlterSelection; sPixmap = "LinkSelect"; sAccel = "S, G"; } static App::DocumentObject* getSelectedLink(bool finalLink, std::string* subname = nullptr) { const auto& sels = Selection().getSelection("*", ResolveMode::NoResolve, true); if (sels.empty()) { return nullptr; } auto sobj = sels[0].pObject->getSubObject(sels[0].SubName); if (!sobj) { return nullptr; } auto vp = freecad_cast(Application::Instance->getViewProvider(sobj)); if (!vp) { return nullptr; } auto linkedVp = vp->getLinkedViewProvider(subname, finalLink); if (!linkedVp || linkedVp == vp) { if (sobj->getDocument() == sels[0].pObject->getDocument()) { return nullptr; } for (const char* dot = strchr(sels[0].SubName, '.'); dot; dot = strchr(dot + 1, '.')) { std::string sub(sels[0].SubName, dot + 1 - sels[0].SubName); auto obj = sels[0].pObject->getSubObject(sub.c_str()); if (!obj) { break; } obj = obj->getLinkedObject(true); if (obj->getDocument() != sels[0].pObject->getDocument()) { if (finalLink) { return sobj == obj ? nullptr : sobj; } if (subname) { *subname = std::string(dot + 1); } return obj; } } return finalLink ? nullptr : sobj; } if (finalLink && linkedVp == vp->getLinkedViewProvider()) { return nullptr; } auto linked = linkedVp->getObject(); if (!linked || !linked->isAttachedToDocument()) { return nullptr; } if (subname && sels[0].pObject != sobj && sels[0].SubName) { bool found = false; int pre_len = 0; std::size_t post_len = 0; std::string prefix; std::string prefix2; // An object can be claimed by multiple objects. Let's try select one // that causes minimum jump in tree view, and prefer upper over lower // hierarchy (because of less depth/complexity of tree expansion) for (auto& v : linked->getParents()) { if (v.first != sels[0].pObject) { continue; } const char* sub = v.second.c_str(); const char* dot = sub; for (const char* s = sels[0].SubName; *s && *sub == *s; ++s, ++sub) { if (*sub == '.') { dot = sub; } } found = true; if (dot - v.second.c_str() > pre_len || (dot - v.second.c_str() == pre_len && v.second.size() < post_len)) { pre_len = dot - v.second.c_str(); prefix = std::string(sels[0].SubName, pre_len) + (v.second.c_str() + pre_len); post_len = v.second.size(); } else if (!pre_len) { if (prefix2.empty() || prefix2.size() > v.second.size()) { prefix2 = v.second; } } } if (found) { linked = sels[0].pObject; *subname = !prefix.empty() ? prefix : prefix2 + *subname; } } return linked; } bool StdCmdLinkSelectLinked::isActive() { return getSelectedLink(false) != nullptr; } void StdCmdLinkSelectLinked::activated(int) { std::string subname; auto linked = getSelectedLink(false, &subname); if (!linked) { FC_WARN("invalid selection"); return; } Selection().selStackPush(); Selection().clearCompleteSelection(); if (!subname.empty()) { Selection().addSelection( linked->getDocument()->getName(), linked->getNameInDocument(), subname.c_str() ); auto doc = Application::Instance->getDocument(linked->getDocument()); if (doc) { auto vp = freecad_cast( Application::Instance->getViewProvider(linked) ); doc->setActiveView(vp); } } else { const auto trees = getMainWindow()->findChildren(); for (auto tree : trees) { tree->selectLinkedObject(linked); } } Selection().selStackPush(); } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkSelectLinkedFinal) StdCmdLinkSelectLinkedFinal::StdCmdLinkSelectLinkedFinal() : Command("Std_LinkSelectLinkedFinal") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Go to &Deepest Linked Object"); sToolTipText = QT_TR_NOOP( "Selects the deepest linked object and switches to its original document" ); sWhatsThis = "Std_LinkSelectLinkedFinal"; sStatusTip = sToolTipText; eType = AlterSelection; sPixmap = "LinkSelectFinal"; sAccel = "S, D"; } bool StdCmdLinkSelectLinkedFinal::isActive() { return getSelectedLink(true) != nullptr; } void StdCmdLinkSelectLinkedFinal::activated(int) { auto linked = getSelectedLink(true); if (!linked) { FC_WARN("invalid selection"); return; } Selection().selStackPush(); Selection().clearCompleteSelection(); const auto trees = getMainWindow()->findChildren(); for (auto tree : trees) { tree->selectLinkedObject(linked); } Selection().selStackPush(); } //////////////////////////////////////////////////////////////////////////////////////////// DEF_STD_CMD_A(StdCmdLinkSelectAllLinks) StdCmdLinkSelectAllLinks::StdCmdLinkSelectAllLinks() : Command("Std_LinkSelectAllLinks") { sGroup = "Link"; sMenuText = QT_TR_NOOP("Select &All Links"); sToolTipText = QT_TR_NOOP("Selects all links to the current selected object"); sWhatsThis = "Std_LinkSelectAllLinks"; sStatusTip = sToolTipText; eType = AlterSelection; sPixmap = "LinkSelectAll"; } bool StdCmdLinkSelectAllLinks::isActive() { const auto& sels = Selection().getSelection("*", ResolveMode::OldStyleElement, true); if (sels.empty()) { return false; } return App::GetApplication().hasLinksTo(sels[0].pObject); } void StdCmdLinkSelectAllLinks::activated(int) { auto sels = Selection().getSelection("*", ResolveMode::OldStyleElement, true); if (sels.empty()) { return; } Selection().selStackPush(); Selection().clearCompleteSelection(); const auto trees = getMainWindow()->findChildren(); for (auto tree : trees) { tree->selectAllLinks(sels[0].pObject); } Selection().selStackPush(); } //====================================================================== // Std_LinkSelectActions //=========================================================================== class StdCmdLinkSelectActions: public GroupCommand { public: StdCmdLinkSelectActions() : GroupCommand("Std_LinkSelectActions") { sGroup = "View"; sMenuText = QT_TR_NOOP("&Link Navigation"); sToolTipText = QT_TR_NOOP("Link navigation actions"); sWhatsThis = "Std_LinkSelectActions"; sStatusTip = sToolTipText; eType = AlterSelection; bCanLog = false; addCommand(new StdCmdLinkSelectLinked()); addCommand(new StdCmdLinkSelectLinkedFinal()); addCommand(new StdCmdLinkSelectAllLinks()); } const char* className() const override { return "StdCmdLinkSelectActions"; } }; //====================================================================== // Std_LinkActions //=========================================================================== class StdCmdLinkActions: public GroupCommand { public: StdCmdLinkActions() : GroupCommand("Std_LinkActions") { sGroup = "View"; sMenuText = QT_TR_NOOP("Link Actions"); sToolTipText = QT_TR_NOOP("Commands that operate on link objects"); sWhatsThis = "Std_LinkActions"; sStatusTip = sToolTipText; eType = AlterDoc; bCanLog = false; setCheckable(false); addCommand(new StdCmdLinkMake()); addCommand(new StdCmdLinkMakeRelative()); addCommand(new StdCmdLinkReplace()); addCommand(new StdCmdLinkUnlink()); addCommand(new StdCmdLinkImport()); addCommand(new StdCmdLinkImportAll()); } const char* className() const override { return "StdCmdLinkActions"; } bool isActive() override { return hasActiveDocument(); } }; //=========================================================================== // Instantiation //=========================================================================== namespace Gui { void CreateLinkCommands() { CommandManager& rcCmdMgr = Application::Instance->commandManager(); rcCmdMgr.addCommand(new StdCmdLinkActions()); rcCmdMgr.addCommand(new StdCmdLinkMakeGroup()); rcCmdMgr.addCommand(new StdCmdLinkSelectActions()); } } // namespace Gui