QString PluginGroupNode::getTreeNodeID() const { PluginPtr plugin = getPlugin(); if (plugin) { return QString::fromUtf8(plugin->getPluginID().c_str()); } else { return _name; } }
PluginPtr NodeCreationDialog::getPlugin(QString* presetName) const { QString lineEditText = _imp->textEdit->text(); const CompleterLineEdit::PluginsNamesMap& names = _imp->textEdit->getPluginNamesMap(); for (CompleterLineEdit::PluginsNamesMap::const_iterator it = names.begin(); it!=names.end(); ++it) { if (it->second.comboLabel == lineEditText) { if (presetName) { *presetName = it->second.presetName; } PluginPtr ret = it->second.plugin.lock(); if (ret) { incrementPluginWeight(QString::fromUtf8(ret->getPluginID().c_str()), it->second.presetName, ret->getMajorVersion()); } return ret; } } return PluginPtr(); }
NATRON_NAMESPACE_ENTER // those parameters should be ignored (they are always secret in Natron) #define kOCIOParamInputSpace "ocioInputSpace" #define kOCIOParamOutputSpace "ocioOutputSpace" // those parameters should not have their options in the help file if generating markdown, // because the options are dinamlically constructed at run-time from the OCIO config. #define kOCIOParamInputSpaceChoice "ocioInputSpaceIndex" #define kOCIOParamOutputSpaceChoice "ocioOutputSpaceIndex" #define kOCIODisplayPluginIdentifier "fr.inria.openfx.OCIODisplay" #define kOCIODisplayParamDisplay "display" #define kOCIODisplayParamDisplayChoice "displayIndex" #define kOCIODisplayParamView "view" #define kOCIODisplayParamViewChoice "viewIndex" // not yet implemented (see OCIOCDLTransform.cpp) //#define kOCIOCDLTransformPluginIdentifier "fr.inria.openfx.OCIOCDLTransform" //#define kOCIOCDLTransformParamCCCID "cccId" //#define kOCIOCDLTransformParamCCCIDChoice "cccIdIndex" #define kOCIOFileTransformPluginIdentifier "fr.inria.openfx.OCIOFileTransform" #define kOCIOFileTransformParamCCCID "cccId" #define kOCIOFileTransformParamCCCIDChoice "cccIdIndex" // genHTML: true for live HTML output for the internal web-server, false for markdown output QString Node::makeDocumentation(bool genHTML) const { QString ret; QString markdown; QTextStream ts(&ret); QTextStream ms(&markdown); QString pluginID, pluginLabel, pluginDescription, pluginIcon; int majorVersion = getMajorVersion(); int minorVersion = getMinorVersion(); std::vector<std::string> pluginGroup; bool pluginDescriptionIsMarkdown = false; QVector<QStringList> inputs; QVector<QStringList> items; { PluginPtr plugin = getPlugin(); assert(plugin); pluginID = QString::fromUtf8(plugin->getPluginID().c_str()); pluginLabel = QString::fromUtf8( Plugin::makeLabelWithoutSuffix( plugin->getPluginLabel() ).c_str()); pluginDescription = QString::fromUtf8( plugin->getPropertyUnsafe<std::string>(kNatronPluginPropDescription).c_str() ); pluginIcon = QString::fromUtf8(plugin->getPropertyUnsafe<std::string>(kNatronPluginPropIconFilePath).c_str()); pluginGroup = plugin->getPropertyNUnsafe<std::string>(kNatronPluginPropGrouping); pluginDescriptionIsMarkdown = plugin->getPropertyUnsafe<bool>(kNatronPluginPropDescriptionIsMarkdown); for (int i = 0; i < _imp->effect->getNInputs(); ++i) { QStringList input; input << convertFromPlainTextToMarkdown( QString::fromStdString( getInputLabel(i) ), genHTML, true ); input << convertFromPlainTextToMarkdown( QString::fromStdString( getInputHint(i) ), genHTML, true ); input << ( isInputOptional(i) ? tr("Yes") : tr("No") ); inputs.push_back(input); // Don't show more than doc for 4 inputs otherwise it will just clutter the page if (i == 3) { break; } } } // check for plugin icon QString pluginIconUrl; if ( !pluginIcon.isEmpty() ) { QFile iconFile(pluginIcon); if ( iconFile.exists() ) { if (genHTML) { pluginIconUrl.append( QString::fromUtf8("/LOCAL_FILE/") ); pluginIconUrl.append(pluginIcon); pluginIconUrl.replace( QString::fromUtf8("\\"), QString::fromUtf8("/") ); } else { pluginIconUrl.append(pluginID); pluginIconUrl.append(QString::fromUtf8(".png")); } } } // check for extra markdown file QString extraMarkdown; QString pluginMD = pluginIcon; pluginMD.replace( QString::fromUtf8(".png"), QString::fromUtf8(".md") ); QFile pluginMarkdownFile(pluginMD); if ( pluginMarkdownFile.exists() ) { if ( pluginMarkdownFile.open(QIODevice::ReadOnly | QIODevice::Text) ) { extraMarkdown = QString::fromUtf8( pluginMarkdownFile.readAll() ); pluginMarkdownFile.close(); } } // generate knobs info KnobsVec knobs = getEffectInstance()->getKnobs_mt_safe(); for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { #pragma message WARN("TODO: restore getDefaultIsSecret from RB-2.2") //if ( (*it)->getDefaultIsSecret() ) { if ( (*it)->getIsSecret() ) { continue; } if ((*it)->getKnobDeclarationType() != KnobI::eKnobDeclarationTypePlugin) { continue; } // do not escape characters in the scriptName, since it will be put between backquotes QString knobScriptName = /*NATRON_NAMESPACE::convertFromPlainTextToMarkdown(*/ QString::fromUtf8( (*it)->getName().c_str() )/*, genHTML, true)*/; QString knobLabel = NATRON_NAMESPACE::convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getLabel().c_str() ), genHTML, true); QString knobHint = NATRON_NAMESPACE::convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getHintToolTip().c_str() ), genHTML, true); // totally ignore the documentation for these parameters (which are always secret in Natron) if ( knobScriptName.startsWith( QString::fromUtf8("NatronOfxParam") ) || knobScriptName == QString::fromUtf8("exportAsPyPlug") || knobScriptName == QString::fromUtf8(kOCIOParamInputSpace) || knobScriptName == QString::fromUtf8(kOCIOParamOutputSpace) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamDisplay) ) ) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamView) ) ) || //( ( pluginID == QString::fromUtf8(kOCIOCDLTransformPluginIdentifier) ) && // ( knobScriptName == QString::fromUtf8(kOCIOCDLTransformParamCCCID) ) ) || ( ( pluginID == QString::fromUtf8(kOCIOFileTransformPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIOFileTransformParamCCCID) ) ) || false) { continue; } QString defValuesStr, knobType; std::vector<std::pair<QString, QString> > dimsDefaultValueStr; KnobIntPtr isInt = toKnobInt(*it); KnobChoicePtr isChoice = toKnobChoice(*it); KnobBoolPtr isBool = toKnobBool(*it); KnobDoublePtr isDbl = toKnobDouble(*it); KnobStringPtr isString = toKnobString(*it); bool isLabel = isString && isString->isLabel(); KnobSeparatorPtr isSep = toKnobSeparator(*it); KnobButtonPtr isBtn = toKnobButton(*it); KnobParametricPtr isParametric = toKnobParametric(*it); KnobGroupPtr isGroup = toKnobGroup(*it); KnobPagePtr isPage = toKnobPage(*it); KnobColorPtr isColor = toKnobColor(*it); if (isInt) { knobType = tr("Integer"); } else if (isChoice) { knobType = tr("Choice"); } else if (isBool) { knobType = tr("Boolean"); } else if (isDbl) { knobType = tr("Double"); } else if (isString) { if (isLabel) { knobType = tr("Label"); } else { knobType = tr("String"); } } else if (isSep) { knobType = tr("Separator"); } else if (isBtn) { knobType = tr("Button"); } else if (isParametric) { knobType = tr("Parametric"); } else if (isGroup) { knobType = tr("Group"); } else if (isPage) { knobType = tr("Page"); } else if (isColor) { knobType = tr("Color"); } else { knobType = tr("N/A"); } if (!isGroup && !isPage) { for (int i = 0; i < (*it)->getNDimensions(); ++i) { QString valueStr; if (!isBtn && !isSep && !isParametric) { // If this is a ChoiceParam and we are not generating live HTML doc, // only add the list of entries and their halp if this node should not be // ignored (eg. OCIO colorspace knobs). if ( isChoice && (genHTML || !( knobScriptName == QString::fromUtf8(kOCIOParamInputSpaceChoice) || knobScriptName == QString::fromUtf8(kOCIOParamOutputSpaceChoice) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamDisplayChoice) ) ) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamViewChoice) ) ) || //( ( pluginID == QString::fromUtf8(kOCIOCDLTransformPluginIdentifier) ) && // ( knobScriptName == QString::fromUtf8(kOCIOCDLTransformParamCCCIDChoice) ) ) || ( ( pluginID == QString::fromUtf8(kOCIOFileTransformPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIOFileTransformParamCCCIDChoice) ) ) || ( ( pluginID == QString::fromUtf8("net.fxarena.openfx.Text") ) && ( knobScriptName == QString::fromUtf8("name") ) ) || // font family from Text plugin ( ( pluginID == QString::fromUtf8("net.fxarena.openfx.Polaroid") ) && ( knobScriptName == QString::fromUtf8("font") ) ) || // font family from Polaroid plugin ( ( pluginID == QString::fromUtf8(PLUGINID_NATRON_PRECOMP) ) && ( knobScriptName == QString::fromUtf8("writeNode") ) ) || ( ( pluginID == QString::fromUtf8(PLUGINID_NATRON_ONEVIEW) ) && ( knobScriptName == QString::fromUtf8("view") ) ) ) ) ) { // see also KnobChoice::getHintToolTipFull() int index = isChoice->getDefaultValue(DimIdx(i)); std::vector<ChoiceOption> entries = isChoice->getEntries(); if ( (index >= 0) && ( index < (int)entries.size() ) ) { valueStr = QString::fromUtf8( entries[index].id.c_str() ); } bool first = true; for (size_t i = 0; i < entries.size(); i++) { QString entryHelp = QString::fromUtf8( entries[i].tooltip.c_str() ); QString entry; if (entries[i].id != entries[i].label) { entry = QString::fromUtf8( "%1 (%2)" ).arg(QString::fromUtf8( entries[i].label.c_str() )).arg(QString::fromUtf8( entries[i].id.c_str() )); } else { entry = QString::fromUtf8( entries[i].label.c_str() ); } if (!entry.isEmpty()) { if (first) { // empty line before the option descriptions if (genHTML) { if ( !knobHint.isEmpty() ) { knobHint.append( QString::fromUtf8("<br />") ); } knobHint.append( tr("Possible values:") + QString::fromUtf8("<br />") ); } else { // we do a hack for multiline elements, because the markdown->rst conversion by pandoc doesn't use the line block syntax. // what we do here is put a supplementary dot at the beginning of each line, which is then converted to a pipe '|' in the // genStaticDocs.sh script by a simple sed command after converting to RsT if ( !knobHint.isEmpty() ) { if (!knobHint.startsWith( QString::fromUtf8(". ") )) { knobHint.prepend( QString::fromUtf8(". ") ); } knobHint.append( QString::fromUtf8("\\\n") ); } knobHint.append( QString::fromUtf8(". ") + tr("Possible values:") + QString::fromUtf8("\\\n") ); } first = false; } if (genHTML) { knobHint.append( QString::fromUtf8("<br />") ); } else { knobHint.append( QString::fromUtf8("\\\n") ); // we do a hack for multiline elements, because the markdown->rst conversion by pandoc doesn't use the line block syntax. // what we do here is put a supplementary dot at the beginning of each line, which is then converted to a pipe '|' in the // genStaticDocs.sh script by a simple sed command after converting to RsT knobHint.append( QString::fromUtf8(". ") ); } if (entryHelp.isEmpty()) { knobHint.append( QString::fromUtf8("**%1**").arg( convertFromPlainTextToMarkdown(entry, genHTML, true) ) ); } else { knobHint.append( QString::fromUtf8("**%1**: %2").arg( convertFromPlainTextToMarkdown(entry, genHTML, true) ).arg( convertFromPlainTextToMarkdown(entryHelp, genHTML, true) ) ); } } } } else if (isInt) { valueStr = QString::number( isInt->getDefaultValue( DimIdx(i) ) ); } else if (isDbl) { valueStr = QString::number( isDbl->getDefaultValue( DimIdx(i) ) ); } else if (isBool) { valueStr = isBool->getDefaultValue( DimIdx(i) ) ? tr("On") : tr("Off"); } else if (isString) { valueStr = QString::fromUtf8( isString->getDefaultValue( DimIdx(i) ).c_str() ); } else if (isColor) { valueStr = QString::number( isColor->getDefaultValue( DimIdx(i) ) ); } } dimsDefaultValueStr.push_back( std::make_pair(convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getDimensionName( DimIdx(i) ).c_str() ), genHTML, true ), convertFromPlainTextToMarkdown(valueStr, genHTML, true)) ); } for (std::size_t i = 0; i < dimsDefaultValueStr.size(); ++i) { if ( !dimsDefaultValueStr[i].second.isEmpty() ) { if (dimsDefaultValueStr.size() > 1) { defValuesStr.append(dimsDefaultValueStr[i].first); defValuesStr.append( QString::fromUtf8(": ") ); } defValuesStr.append(dimsDefaultValueStr[i].second); if (i < dimsDefaultValueStr.size() - 1) { defValuesStr.append( QString::fromUtf8(" ") ); } } } if ( defValuesStr.isEmpty() ) { defValuesStr = tr("N/A"); } } if (!isPage && !isSep && !isGroup && !isLabel) { QStringList row; row << knobLabel << knobScriptName << knobType << defValuesStr << knobHint; items.append(row); } } // for (KnobsVec::const_iterator it = knobs.begin(); it!=knobs.end(); ++it) { // generate plugin info ms << tr("%1 node").arg(pluginLabel) << "\n==========\n\n"; // a hack to avoid repeating the documentation for the various merge plugins if ( pluginID.startsWith( QString::fromUtf8("net.sf.openfx.Merge") ) ) { std::string id = pluginID.toStdString(); std::string op; if (id == PLUGINID_OFX_MERGE) { // do nothing } else if (id == "net.sf.openfx.MergeDifference") { op = "difference (a.k.a. absminus)"; } else if (id == "net.sf.openfx.MergeIn") { op = "in"; } else if (id == "net.sf.openfx.MergeMatte") { op = "matte"; } else if (id == "net.sf.openfx.MergeMax") { op = "max"; } else if (id == "net.sf.openfx.MergeMin") { op = "min"; } else if (id == "net.sf.openfx.MergeMultiply") { op = "multiply"; } else if (id == "net.sf.openfx.MergeOut") { op = "out"; } else if (id == "net.sf.openfx.MergePlus") { op = "plus"; } else if (id == "net.sf.openfx.MergeScreen") { op = "screen"; } if ( !op.empty() ) { // we should use the custom link "[Merge node](|http::/plugins/" PLUGINID_OFX_MERGE ".html||rst::net.sf.openfx.MergePlugin|)" // but pandoc borks it ms << tr("The *%1* node is a convenience node identical to the %2, except that the operator is set to *%3* by default.") .arg(pluginLabel) .arg(genHTML ? QString::fromUtf8("<a href=\"" PLUGINID_OFX_MERGE ".html\">Merge node</a>") : QString::fromUtf8(":ref:`" PLUGINID_OFX_MERGE "`") //QString::fromUtf8("[Merge node](http::/plugins/" PLUGINID_OFX_MERGE ".html)") ) .arg( QString::fromUtf8( op.c_str() ) ); goto OUTPUT; } } if (!pluginIconUrl.isEmpty()) { // add a nonbreaking space so that pandoc doesn't use the alt-text as a caption // http://pandoc.org/MANUAL.html#images ms << "![pluginIcon](" << pluginIconUrl << ")"; if (!genHTML) { // specify image width for pandoc-generated printed doc // (for hoedown-generated HTML, this handled by the CSS using the alt=pluginIcon attribute) // see http://pandoc.org/MANUAL.html#images // note that only % units are understood both by pandox and sphinx ms << "{ width=10% }"; } ms << " \n\n"; // required so that there is no legend when converted to rst by pandoc } ms << tr("*This documentation is for version %2.%3 of %1.*").arg(pluginLabel).arg(majorVersion).arg(minorVersion) << "\n\n"; ms << "\n" << tr("Description") << "\n--------------------------------------------------------------------------------\n\n"; if (!pluginDescriptionIsMarkdown) { if (genHTML) { pluginDescription = NATRON_NAMESPACE::convertFromPlainText(pluginDescription, NATRON_NAMESPACE::WhiteSpaceNormal); // replace URLs with links QRegExp re( QString::fromUtf8("((http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?)") ); pluginDescription.replace( re, QString::fromUtf8("<a href=\"\\1\">\\1</a>") ); } else { pluginDescription = convertFromPlainTextToMarkdown(pluginDescription, genHTML, false); } } ms << pluginDescription << "\n"; // create markdown table ms << "\n" << tr("Inputs") << "\n--------------------------------------------------------------------------------\n\n"; ms << tr("Input") << " | " << tr("Description") << " | " << tr("Optional") << "\n"; ms << "--- | --- | ---\n"; if (inputs.size() > 0) { Q_FOREACH(const QStringList &input, inputs) { QString inputName = input.at(0); QString inputDesc = input.at(1); QString inputOpt = input.at(2); ms << inputName << " | " << inputDesc << " | " << inputOpt << "\n"; }
PluginGroupNodePtr GuiApplicationManagerPrivate::findPluginToolButtonOrCreateInternal(const std::list<PluginGroupNodePtr>& children, const PluginGroupNodePtr& parent, const PluginPtr& plugin, const QStringList& grouping, const QStringList& groupingIcon) { assert(plugin); assert(groupingIcon.size() == grouping.size() || groupingIcon.isEmpty() ); // On first call of this function, children are top-level toolbuttons // Otherwise this tree node has children // We ensure that the path in the tree leading to the plugin in parameter is created by recursing on the children // If there are no children that means we reached the wanted PluginGroupNode QString nodeIDToFind; if (grouping.empty()) { // Look for plugin ID nodeIDToFind = QString::fromUtf8(plugin->getPluginID().c_str()); } else { // Look for grouping menu item nodeIDToFind = grouping[0]; } for (std::list<PluginGroupNodePtr>::const_iterator it = children.begin(); it != children.end(); ++it) { // If we find a node with the same ID, then we found it already. if ( (*it)->getTreeNodeID() == nodeIDToFind ) { if (grouping.empty()) { // This is a leaf (plug-in), return it return *it; } else { // This is an intermidiate menu item, recurse QStringList newGrouping, newIconsGrouping; for (int i = 1; i < grouping.size(); ++i) { newGrouping.push_back(grouping[i]); } for (int i = 1; i < groupingIcon.size(); ++i) { newIconsGrouping.push_back(groupingIcon[i]); } return findPluginToolButtonOrCreateInternal( (*it)->getChildren(), *it, plugin, newGrouping, newIconsGrouping); } } } // Ok the PluginGroupNode does not exist yet, create it QString treeNodeName, iconFilePath; if (grouping.empty()) { // This is a leaf (plug-in), take the plug-in label and icon treeNodeName = QString::fromUtf8(plugin->getLabelWithoutSuffix().c_str()); iconFilePath = QString::fromUtf8(plugin->getProperty<std::string>(kNatronPluginPropIconFilePath).c_str()); } else { // For menu items, take from grouping treeNodeName = grouping[0]; iconFilePath = groupingIcon.isEmpty() ? QString() : groupingIcon[0]; } PluginGroupNodePtr ret(new PluginGroupNode(grouping.empty() ? plugin : PluginPtr(), treeNodeName, iconFilePath)); // If there is a parent, add it as a child if (parent) { parent->tryAddChild(ret); ret->setParent(parent); } else { // No parent, this is a top-level toolbutton _topLevelToolButtons.push_back(ret); } // If we still did not reach the desired tree node, find it, advancing (removing the first item) in the grouping if (!grouping.empty()) { QStringList newGrouping, newIconsGrouping; for (int i = 1; i < grouping.size(); ++i) { newGrouping.push_back(grouping[i]); } for (int i = 1; i < groupingIcon.size(); ++i) { newIconsGrouping.push_back(groupingIcon[i]); } return findPluginToolButtonOrCreateInternal(ret->getChildren(), ret, plugin, newGrouping, newIconsGrouping); } return ret; } // GuiApplicationManagerPrivate::findPluginToolButtonOrCreateInternal
ToolButton* Gui::findOrCreateToolButton(const PluginGroupNodePtr & treeNode) { // Do not create an action for non user creatable plug-ins bool isUserCreatable = true; PluginPtr internalPlugin = treeNode->getPlugin(); if (internalPlugin && treeNode->getChildren().empty() && !internalPlugin->getIsUserCreatable()) { isUserCreatable = false; } if (!isUserCreatable) { return 0; } // Check for existing toolbuttons for (std::size_t i = 0; i < _imp->_toolButtons.size(); ++i) { if (_imp->_toolButtons[i]->getPluginToolButton() == treeNode) { return _imp->_toolButtons[i]; } } // Check for parent toolbutton ToolButton* parentToolButton = NULL; if ( treeNode->getParent() ) { assert(treeNode->getParent() != treeNode); if (treeNode->getParent() != treeNode) { parentToolButton = findOrCreateToolButton( treeNode->getParent() ); } } QString resourcesPath; if (internalPlugin) { resourcesPath = QString::fromUtf8(internalPlugin->getProperty<std::string>(kNatronPluginPropResourcesPath).c_str()); } QString iconFilePath = resourcesPath; StrUtils::ensureLastPathSeparator(iconFilePath); iconFilePath += treeNode->getTreeNodeIconFilePath(); QIcon toolButtonIcon, menuIcon; // Create tool icon if ( !iconFilePath.isEmpty() && QFile::exists(iconFilePath) ) { QPixmap pix(iconFilePath); int menuSize = TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE); int toolButtonSize = !treeNode->getParent() ? TO_DPIX(NATRON_TOOL_BUTTON_ICON_SIZE) : TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE); QPixmap menuPix = pix, toolbuttonPix = pix; if ( (std::max( menuPix.width(), menuPix.height() ) != menuSize) && !menuPix.isNull() ) { menuPix = menuPix.scaled(menuSize, menuSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } if ( (std::max( toolbuttonPix.width(), toolbuttonPix.height() ) != toolButtonSize) && !toolbuttonPix.isNull() ) { toolbuttonPix = toolbuttonPix.scaled(toolButtonSize, toolButtonSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } menuIcon.addPixmap(menuPix); toolButtonIcon.addPixmap(toolbuttonPix); } else { // Set default icon only if it has no parent, otherwise leave action without an icon if ( !treeNode->getParent() ) { QPixmap toolbuttonPix, menuPix; getPixmapForGrouping( &toolbuttonPix, TO_DPIX(NATRON_TOOL_BUTTON_ICON_SIZE), treeNode->getTreeNodeName() ); toolButtonIcon.addPixmap(toolbuttonPix); getPixmapForGrouping( &menuPix, TO_DPIX(NATRON_TOOL_BUTTON_ICON_SIZE), treeNode->getTreeNodeName() ); menuIcon.addPixmap(menuPix); } } // If the tool-button has no children, this is a leaf, we must create an action // At this point any plug-in MUST be in a toolbutton, so it must have a parent. assert(!treeNode->getChildren().empty() || treeNode->getParent()); int majorVersion = internalPlugin ? internalPlugin->getProperty<unsigned int>(kNatronPluginPropVersion, 0) : 1; int minorVersion = internalPlugin ? internalPlugin->getProperty<unsigned int>(kNatronPluginPropVersion, 1) : 0; ToolButton* pluginsToolButton = new ToolButton(getApp(), treeNode, treeNode->getTreeNodeID(), majorVersion, minorVersion, treeNode->getTreeNodeName(), toolButtonIcon, menuIcon); if (!treeNode->getChildren().empty()) { // For grouping items, create the menu Menu* menu = new Menu(this); menu->setTitle( pluginsToolButton->getLabel() ); menu->setIcon(menuIcon); pluginsToolButton->setMenu(menu); pluginsToolButton->setAction( menu->menuAction() ); } else { // This is a leaf (plug-in) assert(internalPlugin); assert(parentToolButton); // If this is the highest major version for this plug-in use normal label, otherwise also append the major version bool isHighestMajorVersionForPlugin = internalPlugin->getIsHighestMajorVersion(); std::string pluginLabel = !isHighestMajorVersionForPlugin ? internalPlugin->getLabelVersionMajorEncoded() : internalPlugin->getLabelWithoutSuffix(); QKeySequence defaultNodeShortcut; QString shortcutGroup = QString::fromUtf8(kShortcutGroupNodes); std::vector<std::string> groupingSplit = internalPlugin->getPropertyN<std::string>(kNatronPluginPropGrouping); for (std::size_t j = 0; j < groupingSplit.size(); ++j) { shortcutGroup.push_back( QLatin1Char('/') ); shortcutGroup.push_back(QString::fromUtf8(groupingSplit[j].c_str())); } { // If the plug-in has a shortcut get it std::list<QKeySequence> keybinds = getKeybind(shortcutGroup, QString::fromUtf8(internalPlugin->getPluginID().c_str())); if (!keybinds.empty()) { defaultNodeShortcut = keybinds.front(); } } QAction* defaultPresetAction = new QAction(this); defaultPresetAction->setShortcut(defaultNodeShortcut); defaultPresetAction->setShortcutContext(Qt::WidgetShortcut); defaultPresetAction->setText(QString::fromUtf8(pluginLabel.c_str())); defaultPresetAction->setIcon( pluginsToolButton->getMenuIcon() ); QObject::connect( defaultPresetAction, SIGNAL(triggered()), pluginsToolButton, SLOT(onTriggered()) ); const std::vector<PluginPresetDescriptor>& presets = internalPlugin->getPresetFiles(); if (presets.empty()) { // If the node has no presets, just make an action, otherwise make a menu pluginsToolButton->setAction(defaultPresetAction); } else { Menu* menu = new Menu(this); menu->setTitle( pluginsToolButton->getLabel() ); menu->setIcon(menuIcon); pluginsToolButton->setMenu(menu); pluginsToolButton->setAction( menu->menuAction() ); defaultPresetAction->setText(QString::fromUtf8(pluginLabel.c_str()) + tr(" (Default)")); menu->addAction(defaultPresetAction); for (std::vector<PluginPresetDescriptor>::const_iterator it = presets.begin(); it!=presets.end(); ++it) { QKeySequence presetShortcut; { // If the preset has a shortcut get it std::string shortcutKey = internalPlugin->getPluginID(); shortcutKey += "_preset_"; shortcutKey += it->presetLabel.toStdString(); std::list<QKeySequence> keybinds = getKeybind(shortcutGroup, QString::fromUtf8(shortcutKey.c_str())); if (!keybinds.empty()) { presetShortcut = keybinds.front(); } } QString presetLabel = QString::fromUtf8(pluginLabel.c_str()); presetLabel += QLatin1String(" ("); presetLabel += it->presetLabel; presetLabel += QLatin1String(")"); QAction* presetAction = new QAction(this); QPixmap presetPix; if (getPresetIcon(it->presetFilePath, it->presetIconFile, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &presetPix)) { presetAction->setIcon( presetPix ); } presetAction->setShortcut(presetShortcut); presetAction->setShortcutContext(Qt::WidgetShortcut); presetAction->setText(presetLabel); presetAction->setData(it->presetLabel); QObject::connect( presetAction, SIGNAL(triggered()), pluginsToolButton, SLOT(onTriggered()) ); menu->addAction(presetAction); } } } // if (!treeNode->getChildren().empty()) // If it has a parent, add the new tool button as a child if (parentToolButton) { parentToolButton->tryAddChild(pluginsToolButton); } _imp->_toolButtons.push_back(pluginsToolButton); return pluginsToolButton; } // findOrCreateToolButton