/** * RobloxStudioVerbs.cpp */ #include "RobloxStudioVerbs.hpp" // Qt Headers #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 3rd Party Headers #include "System.hpp" #include // Roblox Headers #include "DataModel/Animation.hpp" #include "DataModel/BasicPartInstance.hpp" #include "DataModel/ChangeHistory.hpp" #include "DataModel/ContentProvider.hpp" #include "DataModel/CSGDictionaryService.hpp" #include "DataModel/DataModel.hpp" #include "DataModel/Decal.hpp" #include "DataModel/GameBasicSettings.hpp" #include "DataModel/KeyframeSequence.hpp" #include "DataModel/KeyframeSequenceProvider.hpp" #include "DataModel/NonReplicatedCSGDictionaryService.hpp" #include "DataModel/PartOperation.hpp" #include "DataModel/PartOperationAsset.hpp" #include "DataModel/PhysicsSettings.hpp" #include "DataModel/PlayerGui.hpp" #include "DataModel/SpecialMesh.hpp" #include "DataModel/Tool.hpp" #include "DataModel/Visit.hpp" #include "DataModel/Workspace.hpp" #include "DataModel/ToolsPart.hpp" #include "Tree/Instance.hpp" #include "Xml/SerializerBinary.hpp" #include "Xml/WebParser.hpp" #include "Xml/XmlSerializer.hpp" #include "API.hpp" #include "Players.hpp" #include "Tool/AdvMoveTool.hpp" #include "Tool/AdvRotateTool.hpp" #include "Utility/Statistics.hpp" #include "Script/LuaSourceContainer.hpp" #include "Script/ScriptContext.hpp" #include "Script/script.hpp" #include "Log.hpp" #include "Tool/ToolsArrow.hpp" #include "Render/GeometryGenerator.hpp" #include "Utility/CSGKernel.hpp" #include "Utility/RobloxServicesTools.hpp" #include "Tool/MoveResizeJoinTool.hpp" // Roblox Studio Headers #include "StudioUtilities.hpp" #include "WebDialog.hpp" #include "RbxWorkspace.hpp" #include "RobloxSettings.hpp" #include "RobloxDocManager.hpp" #include "CommonInsertWidget.hpp" #include "DataModel/RenderSettingsItem.hpp" #include "QtUtilities.hpp" #include "RobloxMainWindow.hpp" #include "UpdateUIManager.hpp" #include "RobloxApplicationManager.hpp" #include "RobloxIDEDoc.hpp" #include "StudioSerializerHelper.hpp" #include "AuthenticationHelper.hpp" #include "Roblox.hpp" #include "NameValueStoreManager.hpp" #include "RobloxUser.hpp" #include "RobloxToolBox.hpp" #include "Base/ViewBase.hpp" #include "CSGOperations.hpp" #include "RobloxGameExplorer.hpp" #include "ScriptPickerDialog.hpp" #include "Xml/SerializerBinary.hpp" // Video record related includes #ifdef _WIN32 // #include "VideoControl.hpp" // #include "DSVideoCaptureEngine.hpp" #endif #include "ManageEmulationDeviceDialog.hpp" FASTFLAG(PrefetchResourcesEnabled) FASTFLAG(LuaDebugger) FASTFLAG(StudioCSGAssets) FASTFLAG(GameExplorerUseV2AliasEndpoint) FASTFLAG(StudioEnableGameAnimationsTab) FASTFLAG(PhysicsAnalyzerEnabled) DYNAMIC_FASTFLAG(DraggerUsesNewPartOnDuplicate) DYNAMIC_FASTFLAG(RestoreTransparencyOnToolChange) FASTFLAGVARIABLE(StudioMimeDataContainsInstancePath, false) FASTFLAGVARIABLE(StudioOperationFailureHangFix, true) FASTINTVARIABLE(StudioWebDialogMinimumWidth, 970) FASTINTVARIABLE(StudioWebDialogMinimumHeight, 480) DYNAMIC_FASTFLAG(UseRemoveTypeIDTricks) static const char* sCollisionToggleModeSetting = "CollisionToggleMode"; static const char* sLocalTranslationModeSetting = "LocalTranslationMode"; static const char* sLocalRotationModeSetting = "LocalRotationMode"; static const char* sRibbonJointCreationMode = "rbxRibbonJointCreationMode"; static const char* sRibbonStartServerSetting = "rbxRibbonStartServer"; static const char* sRibbonNumPlayerSetting = "rbxRibbonNumPlayer"; static const char* sRobloxMimeType = "application/x-roblox-studio"; bool StudioMaterialVerb::sMaterialActionActAsTool = false; bool StudioColorVerb::sColorActionActAsTool = false; using namespace Aya; GroupSelectionVerb::GroupSelectionVerb(DataModel* dataModel) : EditSelectionVerb("Group", dataModel) { ; } bool GroupSelectionVerb::isEnabled() const { if (!Super::isEnabled()) return false; else { Selection* sel = ServiceProvider::find(dataModel); return sel && sel->size() > 0; } } void GroupSelectionVerb::doIt(IDataState* dataState) { FASTLOG(FLog::Verbs, "Gui:GroupSelection"); Selection* sel = ServiceProvider::create(dataModel); ModelInstance* groupInstance = workspace->group(sel->begin(), sel->end()).get(); sel->setSelection(groupInstance); // TODO: Undo/Redo... { DataModel::LegacyLock lock(dataModel, DataModelJob::Write); ChangeHistoryService::requestWaypoint(getName().c_str(), workspace.get()); } dataState->setDirty(true); } /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// namespace { static void operationFailed(const char* title, const char* msg) { QMetaObject::invokeMethod(&UpdateUIManager::Instance(), "showErrorMessage", FFlag::StudioOperationFailureHangFix ? Qt::QueuedConnection : Qt::BlockingQueuedConnection, Q_ARG(QString, title), Q_ARG(QString, msg)); } } // anonymous namespace /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// UngroupSelectionVerb::UngroupSelectionVerb(DataModel* dataModel) : EditSelectionVerb("UnGroup", dataModel) { // Force creation here, rather than in isEnabled ServiceProvider::create>(dataModel); } bool canUngroup(ModelInstance* wInstance) { return (wInstance->numChildren() > 0); } bool UngroupSelectionVerb::isEnabled() const { if (!Super::isEnabled()) return false; // enabled if one ore more selected items has children and can be ungrouped Selection* selection = ServiceProvider::create(dataModel); for (std::vector>::const_iterator iter = selection->begin(); iter != selection->end(); ++iter) { if (shared_ptr m = Aya::Instance::fastSharedDynamicCast(*iter)) return true; } return false; } class Ungroup { public: std::vector& ungroupedItems; bool& didSomething; Ungroup(std::vector& ungroupedItems, bool& didSomething) : ungroupedItems(ungroupedItems) , didSomething(didSomething) { } void operator()(const shared_ptr& wInstance) { if (wInstance->numChildren() > 0) { for (size_t j = 0; j < wInstance->numChildren(); ++j) { ungroupedItems.push_back(wInstance->getChild(j)); } wInstance->promoteChildren(); wInstance->setParent(NULL); didSomething = true; } } }; void UngroupSelectionVerb::doIt(IDataState* dataState) { FASTLOG(FLog::Verbs, "Gui:UngroupSelection"); Selection* selection = ServiceProvider::create(dataModel); // First make a copy of the selection list std::vector> itemsToUngroup; for (std::vector>::const_iterator iter = selection->begin(); iter != selection->end(); ++iter) { if (shared_ptr m = Aya::Instance::fastSharedDynamicCast(*iter)) itemsToUngroup.push_back(m); } // Now iterate over the objects to be ungrouped and ungroup them std::vector ungroupedItems; bool didSomething = false; std::for_each(itemsToUngroup.begin(), itemsToUngroup.end(), Ungroup(ungroupedItems, didSomething)); if (didSomething) { selection->setSelection(ungroupedItems.begin(), ungroupedItems.end()); } else { debugAssertM(0, "Calling ungroup command without checking is-enabled"); } // TODO: Undo/Redo... { DataModel::LegacyLock lock(dataModel, DataModelJob::Write); ChangeHistoryService::requestWaypoint(getName().c_str(), workspace.get()); } dataState->setDirty(true); } /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// UnionSelectionVerb::UnionSelectionVerb(DataModel* dataModel) : EditSelectionVerb("UnionSelection", dataModel) { } bool UnionSelectionVerb::isEnabled() const { if (!Super::isEnabled()) return false; else { Selection* sel = ServiceProvider::find(dataModel); return sel && ((sel->size() > 0 && !(sel->size() == 1 && Aya::Instance::fastSharedDynamicCast(*sel->begin()))) || (sel->size() == 1 && Aya::Instance::fastSharedDynamicCast(*sel->begin()))); } } void UnionSelectionVerb::performUnion(IDataState* dataState) { DataModel::LegacyLock lock(dataModel, Aya::DataModelJob::Write); FASTLOG(FLog::Verbs, "Gui:UnionSelection"); CSGOperations csgOps(dataModel, operationFailed); Selection* selection = ServiceProvider::create(dataModel); shared_ptr partOperation; if (csgOps.doUnion(selection->begin(), selection->end(), partOperation)) ChangeHistoryService::requestWaypoint(getName().c_str(), workspace.get()); if (partOperation) selection->setSelection(partOperation.get()); dataState->setDirty(true); } void UnionSelectionVerb::doIt(IDataState* dataState) { UpdateUIManager::Instance().waitForLongProcess("Union", boost::bind(&UnionSelectionVerb::performUnion, this, dataState)); } NegateSelectionVerb::NegateSelectionVerb(DataModel* dataModel) : EditSelectionVerb("NegateSelection", dataModel) { } bool NegateSelectionVerb::isEnabled() const { if (!Super::isEnabled()) return false; else { Selection* sel = ServiceProvider::find(dataModel); return sel && sel->size() > 0; } } void NegateSelectionVerb::doIt(IDataState* dataState) { FASTLOG(FLog::Verbs, "Gui:NegateSelection"); CSGOperations csgOps(dataModel, operationFailed); Selection* selection = ServiceProvider::create(dataModel); std::vector> toSelect; if (csgOps.doNegate(selection->begin(), selection->end(), toSelect)) { DataModel::LegacyLock lock(dataModel, DataModelJob::Write); ChangeHistoryService::requestWaypoint(getName().c_str(), workspace.get()); } selection->setSelection(toSelect.begin(), toSelect.end()); dataState->setDirty(true); } SeparateSelectionVerb::SeparateSelectionVerb(DataModel* dataModel) : EditSelectionVerb("SeparateSelection", dataModel) { } bool SeparateSelectionVerb::isEnabled() const { if (!Super::isEnabled()) return false; // enabled if one ore more selected items has children and can be ungrouped Selection* selection = ServiceProvider::create(dataModel); for (std::vector>::const_iterator iter = selection->begin(); iter != selection->end(); ++iter) if (shared_ptr o = Aya::Instance::fastSharedDynamicCast(*iter)) return true; return false; } void SeparateSelectionVerb::doIt(IDataState* dataState) { UpdateUIManager::Instance().waitForLongProcess("Separate", boost::bind(&SeparateSelectionVerb::performSeparate, this, dataState)); } void SeparateSelectionVerb::performSeparate(IDataState* dataState) { DataModel::LegacyLock lock(dataModel, Aya::DataModelJob::Write); FASTLOG(FLog::Verbs, "Gui:SeparateSelection"); CSGOperations csgOps(dataModel, operationFailed); Selection* selection = ServiceProvider::create(dataModel); std::vector> ungroupedItems; if (csgOps.doSeparate(selection->begin(), selection->end(), ungroupedItems)) Aya::ChangeHistoryService::requestWaypoint(getName().c_str(), workspace.get()); selection->setSelection(ungroupedItems.begin(), ungroupedItems.end()); dataState->setDirty(true); } CutVerb::CutVerb(Aya::DataModel* dataModel) : DeleteSelectionVerb(dataModel, dataModel, "Cut") { } void CutVerb::doIt(Aya::IDataState* dataState) { // First copy selection to clipboard Aya::Verb* pCopyVerb = getContainer()->getVerb("Copy"); if (!pCopyVerb) return; pCopyVerb->doIt(dataState); // now call delete DeleteSelectionVerb::doIt(dataState); } CopyVerb::CopyVerb(Aya::DataModel* dataModel) : EditSelectionVerb("Copy", dataModel) { } void CopyVerb::doIt(Aya::IDataState*) { QClipboard* pClipboard = QApplication::clipboard(); pClipboard->clear(); Aya::Selection* pSelection = Aya::ServiceProvider::create(dataModel); Aya::CSGDictionaryService* dictionaryService = Aya::ServiceProvider::create(dataModel); Aya::NonReplicatedCSGDictionaryService* nrDictionaryService = Aya::ServiceProvider::create(dataModel); for (std::vector>::const_iterator iter = pSelection->begin(); iter != pSelection->end(); ++iter) { dictionaryService->retrieveAllDescendants(*iter); nrDictionaryService->retrieveAllDescendants(*iter); } std::ostringstream selectionStream; Aya::Instances instances(pSelection->begin(), pSelection->end()); Aya::SerializerBinary::serialize(selectionStream, instances); std::string contents = selectionStream.str(); QMimeData* pMimeDataForClipboard = new QMimeData; pMimeDataForClipboard->setData(sRobloxMimeType, QByteArray(contents.data(), contents.length())); for (std::vector>::const_iterator iter = pSelection->begin(); iter != pSelection->end(); ++iter) { dictionaryService->storeAllDescendants(*iter); nrDictionaryService->storeAllDescendants(*iter); } // enable following code for debugging if (FFlag::StudioMimeDataContainsInstancePath && pSelection->size() == 1) pMimeDataForClipboard->setText(("Game." + (*pSelection->begin())->getFullName()).c_str()); // set mime data in clipboard pClipboard->setMimeData(pMimeDataForClipboard); } PasteVerb::PasteVerb(Aya::DataModel* dataModel, bool pasteInto) : Aya::Verb(dataModel, pasteInto ? "PasteInto" : "Paste") , m_pDataModel(dataModel) , m_bPasteInto(pasteInto) { } bool PasteVerb::isEnabled() const { if (!isPasteInfoAvailable()) return false; if (!m_bPasteInto) return true; // for paste into (used in tree view contextual menu) Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel); return (pSelection && pSelection->size() == 1); } void PasteVerb::onClipboardModified() { m_bIsPasteInfoAvailable = isPasteInfoAvailable(); } bool PasteVerb::isPasteInfoAvailable() const { QClipboard* pClipboard = QApplication::clipboard(); if (pClipboard) { const QMimeData* pMimeData = pClipboard->mimeData(); if (pMimeData && pMimeData->hasFormat(sRobloxMimeType)) return true; } return false; } void PasteVerb::doIt(Aya::IDataState* dataState) { shared_ptr itemsToPaste(new Instances); createInstancesFromClipboard(itemsToPaste); if (itemsToPaste->empty()) return; insertInstancesIntoParent(itemsToPaste); // Save Undo/Redo state { DataModel::LegacyLock lock(m_pDataModel, DataModelJob::Write); Aya::ChangeHistoryService::requestWaypoint(getName().c_str(), m_pDataModel); } dataState->setDirty(true); } void PasteVerb::createInstancesFromClipboard(shared_ptr itemsToPaste) { QClipboard* pClipboard = QApplication::clipboard(); const QMimeData* pMimeData = pClipboard->mimeData(); if (!pMimeData || !pMimeData->hasFormat(sRobloxMimeType)) return; QByteArray robloxData = pMimeData->data(sRobloxMimeType); std::istringstream stream(std::string(robloxData.data(), robloxData.data() + robloxData.size())); try { Serializer().loadInstances(stream, *itemsToPaste); } catch (...) { // clear clipboard pClipboard->clear(); } } void PasteVerb::insertInstancesIntoParent(shared_ptr itemsToPaste) { Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel); if (!pSelection) return; shared_ptr sel = pSelection->front(); Aya::Instance* pParentInstance; bool isDecal = false; isDecal = itemsToPaste->size() == 1 && Aya::Instance::fastSharedDynamicCast(*itemsToPaste->begin()); if (pSelection->size() != 1 || (isDecal && !m_bPasteInto)) pParentInstance = m_pDataModel->getWorkspace(); else pParentInstance = m_bPasteInto || !sel->getParent() || !sel->getParent()->getParent() ? sel.get() : sel->getParent(); if (!pParentInstance) return; // insert items into appropriate parent Aya::InsertMode insertMode = m_bPasteInto ? Aya::INSERT_TO_TREE : Aya::INSERT_TO_3D_VIEW; if (isDecal) insertMode = Aya::INSERT_TO_TREE; m_pDataModel->getWorkspace()->insertPasteInstances(*itemsToPaste, pParentInstance, insertMode, Aya::SUPPRESS_PROMPTS); pSelection->setSelection(itemsToPaste); } void PasteVerb::createInstancesFromClipboardDep(Aya::Instances& itemsToPaste) { QClipboard* pClipboard = QApplication::clipboard(); const QMimeData* pMimeData = pClipboard->mimeData(); if (!pMimeData || !pMimeData->hasFormat(sRobloxMimeType)) return; QByteArray robloxData = pMimeData->data(sRobloxMimeType); std::istringstream stream(std::string(robloxData.data(), robloxData.data() + robloxData.size())); try { Serializer().loadInstances(stream, itemsToPaste); } catch (...) { // clear clipboard pClipboard->clear(); } } void PasteVerb::insertInstancesIntoParentDep(Aya::Instances& itemsToPaste) { Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel); if (!pSelection) return; shared_ptr sel = pSelection->front(); Aya::Instance* pParentInstance; bool isDecal = false; isDecal = itemsToPaste.size() == 1 && Aya::Instance::fastSharedDynamicCast(*itemsToPaste.begin()); if (pSelection->size() != 1 || (isDecal && !m_bPasteInto)) pParentInstance = m_pDataModel->getWorkspace(); else pParentInstance = m_bPasteInto || !sel->getParent() || !sel->getParent()->getParent() ? sel.get() : sel->getParent(); if (!pParentInstance) return; // insert items into appropriate parent Aya::InsertMode insertMode = m_bPasteInto ? Aya::INSERT_TO_TREE : Aya::INSERT_TO_3D_VIEW; if (isDecal) insertMode = Aya::INSERT_TO_TREE; m_pDataModel->getWorkspace()->insertPasteInstances(itemsToPaste, pParentInstance, insertMode, Aya::SUPPRESS_PROMPTS); pSelection->setSelection(itemsToPaste.begin(), itemsToPaste.end()); } DuplicateSelectionVerb::DuplicateSelectionVerb(Aya::DataModel* dataModel) : EditSelectionVerb("Duplicate", dataModel) { } void DuplicateSelectionVerb::doIt(Aya::IDataState*) { Aya::Selection* selection = Aya::ServiceProvider::create(dataModel); if (!selection) return; if (DFFlag::RestoreTransparencyOnToolChange) Aya::AdvArrowToolBase::restoreSavedPartsTransparency(); shared_ptr instances(new Instances); std::vector pvInstances; CSGDictionaryService* dictionaryService = Aya::ServiceProvider::create(dataModel); NonReplicatedCSGDictionaryService* nrDictionaryService = Aya::ServiceProvider::create(dataModel); for (std::vector>::const_iterator iter = selection->begin(); iter != selection->end(); ++iter) { if (shared_ptr partOperation = Aya::Instance::fastSharedDynamicCast(*iter)) { dictionaryService->retrieveData(*partOperation); nrDictionaryService->retrieveData(*partOperation); } if ((*iter)->getParent() && (*iter)->getParent()->getParent() && !(*iter)->getIsParentLocked()) { if (shared_ptr clonedInstance = (*iter)->clone(Aya::SerializationCreator)) { clonedInstance->setParent((*iter)->getParent()); instances->push_back(clonedInstance); if (DFFlag::DraggerUsesNewPartOnDuplicate) MoveResizeJoinTool::setSelection(*iter, clonedInstance); if (Aya::AdvArrowTool::advCollisionCheckMode && clonedInstance->isDescendantOf(dataModel->getWorkspace())) if (shared_ptr pvInstance = Aya::Instance::fastSharedDynamicCast(clonedInstance)) pvInstances.push_back(pvInstance.get()); } } if (shared_ptr partOperation = Aya::Instance::fastSharedDynamicCast(*iter)) { dictionaryService->storeData(*partOperation); nrDictionaryService->storeData(*partOperation); } } if (Aya::AdvArrowTool::advCollisionCheckMode && pvInstances.size() > 0) { Aya::MegaDragger megaDragger(NULL, pvInstances, dataModel->getWorkspace()); megaDragger.startDragging(); megaDragger.safeMoveNoDrop(Aya::Vector3(0.0f, 0.0f, 0.0f)); megaDragger.finishDragging(); } if (instances->size() == 0) return; selection->setSelection(instances); { DataModel::LegacyLock lock(dataModel, DataModelJob::Write); Aya::ChangeHistoryService::requestWaypoint(getName().c_str(), dataModel); } dataModel->setDirty(true); } UndoVerb::UndoVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "UndoVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { m_pChangeHistory = Aya::shared_from(m_pDataModel->create()); AYAASSERT(m_pChangeHistory); } void UndoVerb::doIt(Aya::IDataState*) { UpdateUIManager::Instance().waitForLongProcess("Undo", boost::bind(&Aya::ChangeHistoryService::unplay, m_pChangeHistory.get())); } bool UndoVerb::isEnabled() const { return m_pChangeHistory->canUnplay(); } std::string UndoVerb::getText() const { std::string name; m_pChangeHistory->getUnplayWaypoint(name); return std::string("Undo " + name); } RedoVerb::RedoVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "RedoVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { m_pChangeHistory = Aya::shared_from(m_pDataModel->create()); AYAASSERT(m_pChangeHistory); } void RedoVerb::doIt(Aya::IDataState*) { UpdateUIManager::Instance().waitForLongProcess("Redo", boost::bind(&Aya::ChangeHistoryService::play, m_pChangeHistory.get())); } bool RedoVerb::isEnabled() const { return m_pChangeHistory->canPlay(); } std::string RedoVerb::getText() const { std::string name; m_pChangeHistory->getPlayWaypoint(name); return std::string("Redo " + name); } InsertModelVerb::InsertModelVerb(Aya::VerbContainer* pVerbContainer, bool insertInto) : Verb(pVerbContainer, insertInto ? "InsertIntoFromFileVerb" : "InsertModelVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) , m_bInsertInto(insertInto) { } void InsertModelVerb::doIt(Aya::IDataState*) { insertModel(); } void InsertModelVerb::insertModel() { RobloxSettings settings; QString rbxmLastDir = settings.value("rbxm_last_directory").toString(); if (rbxmLastDir.isEmpty()) rbxmLastDir = RobloxMainWindow::getDefaultSavePath(); QString dlgTitle = m_bInsertInto ? QObject::tr("Open file to Insert") : QObject::tr("Open Roblox Model"); QString fileExtn = m_bInsertInto ? QObject::tr("Roblox Model Files (*.rbxm *.rbxmx);;Scripts (*.rbxs *.lua *.txt)") : QObject::tr("Roblox Model Files (*.rbxm *.rbxmx)"); QString fileName; fileName = QFileDialog::getOpenFileName(&UpdateUIManager::Instance().getMainWindow(), dlgTitle, rbxmLastDir, fileExtn); if (fileName.isEmpty()) return; if (fileName.endsWith(".rbxm", Qt::CaseInsensitive) || fileName.endsWith(".rbxmx", Qt::CaseInsensitive)) StudioUtilities::insertModel(Aya::shared_from(m_pDataModel), fileName, m_bInsertInto); else StudioUtilities::insertScript(Aya::shared_from(m_pDataModel), fileName); settings.setValue("rbxm_last_directory", QFileInfo(fileName).absolutePath()); } SelectionSaveToFileVerb::SelectionSaveToFileVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "SelectionSaveToFile") , m_pDataModel(dynamic_cast(pVerbContainer)) { } void SelectionSaveToFileVerb::doIt(Aya::IDataState*) { saveToFile(); } void SelectionSaveToFileVerb::saveToFile() { RobloxSettings settings; QString rbxmLastDir = settings.value("rbxm_last_directory").toString(); if (rbxmLastDir.isEmpty()) rbxmLastDir = RobloxMainWindow::getDefaultSavePath(); QString fileExtnModels("Roblox XML Model Files (*.rbxmx);;Roblox Model Files (*.rbxm)"); QString fileExtnScripts("Roblox Lua Scripts (*.lua)"); QString fileExtn = fileExtnModels; Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel); if (pSelection && (pSelection->size() == 1) && Aya::Instance::fastDynamicCast(pSelection->front().get())) { // update default file name for save rbxmLastDir.append("/"); rbxmLastDir.append(pSelection->front()->getName().c_str()); // if script doesn't have any children then default to lua if (!pSelection->front()->numChildren()) { fileExtn = fileExtnScripts + ";;" + fileExtnModels; rbxmLastDir.append(".lua"); } else { fileExtn = fileExtnModels + ";;" + fileExtnScripts; rbxmLastDir.append(".rbxm"); } } QString fileName = QFileDialog::getSaveFileName(&UpdateUIManager::Instance().getMainWindow(), "Save As", rbxmLastDir, fileExtn); if (fileName.isEmpty()) return; DataModel::LegacyLock lock(m_pDataModel, DataModelJob::Write); Aya::CSGDictionaryService* dictionaryService = Aya::ServiceProvider::create(m_pDataModel); Aya::NonReplicatedCSGDictionaryService* nrDictionaryService = Aya::ServiceProvider::create(m_pDataModel); for (std::vector>::const_iterator iter = pSelection->begin(); iter != pSelection->end(); ++iter) { dictionaryService->retrieveAllDescendants(*iter); nrDictionaryService->retrieveAllDescendants(*iter); } if (fileName.endsWith(".rbxm", Qt::CaseInsensitive) || fileName.endsWith(".rbxmx", Qt::CaseInsensitive)) { QByteArray ba = fileName.toUtf8(); const char* c_str = ba.constData(); // Stream the XML data std::ofstream stream; stream.open(c_str, std::ios_base::out | std::ios::binary); const bool useBinaryFormat = !(fileName.endsWith(".rbxmx", Qt::CaseInsensitive)); if (useBinaryFormat) { Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel->getWorkspace()); Aya::Instances instances(pSelection->begin(), pSelection->end()); Aya::SerializerBinary::serialize(stream, instances); } else { TextXmlWriter machine(stream); UpdateUIManager::Instance().waitForLongProcess("Saving", boost::bind(&TextXmlWriter::serialize, &machine, writeSelection().get())); } } else { QString scriptText; { shared_ptr spScriptInstance = Aya::Instance::fastSharedDynamicCast(pSelection->front()); if (spScriptInstance) { if (spScriptInstance->isCodeEmbedded()) { scriptText = spScriptInstance->getEmbeddedCodeSafe().getSource().c_str(); } else { Aya::ContentProvider* pContentProvider = Aya::ServiceProvider::create(spScriptInstance.get()); if (pContentProvider) { std::auto_ptr stream = pContentProvider->getContent(spScriptInstance->getScriptId()); std::string data = std::string(static_cast(std::stringstream() << stream->rdbuf()).str()); scriptText = data.c_str(); } } } } QFile file(fileName); if (!scriptText.isEmpty() && file.open(QFile::WriteOnly | QFile::Text)) { QTextStream out(&file); out << scriptText; } } settings.setValue("rbxm_last_directory", QFileInfo(fileName).absolutePath()); for (std::vector>::const_iterator iter = pSelection->begin(); iter != pSelection->end(); ++iter) { dictionaryService->storeAllDescendants(*iter); nrDictionaryService->storeAllDescendants(*iter); } } bool SelectionSaveToFileVerb::isEnabled() const { Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel); return (pSelection && (pSelection->size() > 0)); } std::auto_ptr SelectionSaveToFileVerb::writeSelection() { std::auto_ptr root(Serializer::newRootElement()); { Aya::DataModel::LegacyLock lock(m_pDataModel, Aya::DataModelJob::Write); Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel->getWorkspace()); Aya::AddSelectionToRoot(root.get(), pSelection, Aya::SerializationCreator); } return root; } PublishToRobloxAsVerb::PublishToRobloxAsVerb(Aya::VerbContainer* pVerbContainer, RobloxMainWindow* mainWnd) : Verb(pVerbContainer, "PublishToRobloxAsVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) , m_pMainWindow(mainWnd) , m_dlg(NULL) { } PublishToRobloxAsVerb::~PublishToRobloxAsVerb() { if (m_dlg) delete m_dlg; } bool PublishToRobloxAsVerb::isEnabled() const { return true; } void PublishToRobloxAsVerb::initDialog() { static QMutex mutex; mutex.lock(); try { QString initialUrl = QString("%1/IDE/publishas").arg(RobloxSettings::getBaseURL()); if (!m_dlg) { m_dlg = new WebDialog(m_pMainWindow, initialUrl, m_pDataModel); m_dlg->setMinimumSize(FInt::StudioWebDialogMinimumWidth, FInt::StudioWebDialogMinimumHeight); } else m_dlg->load(initialUrl); } catch (std::exception& e) { // Aya::Log::current()->writeEntry(Aya::Log::Error, e.what()); } mutex.unlock(); } void PublishToRobloxAsVerb::doIt(Aya::IDataState*) { // autosave before we publish just in case RobloxDocManager::Instance().getPlayDoc()->autoSave(true); if (!StudioUtilities::checkNetworkAndUserAuthentication()) return; Aya::DataModel::LegacyLock lock(m_pDataModel, Aya::DataModelJob::Write); if (FFlag::PrefetchResourcesEnabled) initDialog(); else { // "http://www.roblox.com/IDE/Upload.aspx" QString initialUrl = QString("%1/IDE/Upload.aspx").arg(RobloxSettings::getBaseURL()); if (!m_dlg) { m_dlg = new WebDialog(m_pMainWindow, initialUrl, m_pDataModel); m_dlg->setMinimumSize(FInt::StudioWebDialogMinimumWidth, FInt::StudioWebDialogMinimumHeight); } else m_dlg->load(initialUrl); } m_dlg->show(); m_dlg->raise(); m_dlg->activateWindow(); } QDialog* PublishToRobloxAsVerb::getPublishDialog() { return qobject_cast(m_dlg); } /////////////////////////////////////////////////////////////////////////////////////////////////////// // Bind fn for Publish Selection to Roblox static void AnimationResponse(std::string* response, std::exception*, shared_ptr animation) { if (response) { Aya::DataModel::LegacyLock lock(Aya::DataModel::get(animation.get()), Aya::DataModelJob::Write); int newAssetId; std::stringstream istream(*response); istream >> newAssetId; QString baseUrl = RobloxSettings::getBaseURL(); animation->setAssetId(Aya::format("%s/Asset?ID=%d", qPrintable(baseUrl), newAssetId)); } } static void SaveDecalAssetId(shared_ptr decal, std::string assetId) { decal->setTexture(assetId); } static void SaveMeshMeshId(shared_ptr mesh, std::string assetId) { mesh->setMeshId(assetId); } static void SaveMeshTextureId(shared_ptr mesh, std::string assetId) { mesh->setTextureId(assetId); } static void PostSaveHelper(boost::function saveFunction, std::string assetId) { saveFunction(assetId); } static void PostFromContentProvider(Aya::AsyncHttpQueue::RequestResult result, std::istream* stream, int type, std::string name, weak_ptr weakDataModel, boost::function saveFunction) { QString baseUrl = RobloxSettings::getBaseURL(); if (result == Aya::AsyncHttpQueue::Succeeded) { Aya::Http http(Aya::format("%s/Data/NewAsset.ashx?type=%d&Name=%s&Description=%s", qPrintable(baseUrl), type, name.c_str(), name.c_str())); try { std::string response; http.post(*stream, Aya::Http::kContentTypeDefaultUnspecified, true, response); int newAssetId; std::stringstream istream(response); istream >> newAssetId; if (shared_ptr dataModel = weakDataModel.lock()) { dataModel->submitTask(boost::bind(&PostSaveHelper, saveFunction, Aya::format("%s/Asset?ID=%d", qPrintable(baseUrl), newAssetId)), Aya::DataModelJob::Write); } } catch (std::exception&) { } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// PublishSelectionToRobloxVerb::PublishSelectionToRobloxVerb(Aya::VerbContainer* pVerbContainer, RobloxMainWindow* mainWnd) : Verb(pVerbContainer, "PublishSelectionToRobloxVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) , m_pMainWindow(mainWnd) , m_dlg(NULL) { } PublishSelectionToRobloxVerb::~PublishSelectionToRobloxVerb() { if (m_dlg) delete m_dlg; } bool PublishSelectionToRobloxVerb::isEnabled() const { Aya::Selection* pSelection = Aya::ServiceProvider::create(m_pDataModel); return (pSelection && (pSelection->size() > 0)); } void PublishSelectionToRobloxVerb::doIt(Aya::IDataState*) { // autosave before we publish just in case RobloxDocManager::Instance().getPlayDoc()->autoSave(true); if (!StudioUtilities::checkNetworkAndUserAuthentication()) return; Aya::DataModel::LegacyLock lock(m_pDataModel, Aya::DataModelJob::Write); bool isScript = false; if (RbxWorkspace::isScriptAssetUploadEnabled) { Aya::Selection* sel = Aya::ServiceProvider::find(m_pDataModel); if (sel && sel->size() == 1) if (dynamic_cast(sel->front().get())) isScript = true; } if (RbxWorkspace::isImageModelAssetUploadEnabled) { Aya::Selection* sel = Aya::ServiceProvider::find(m_pDataModel); if (sel && sel->size() == 1) { if (Aya::Decal* decal = dynamic_cast(sel->front().get())) { if (!decal->getTexture().isHttp()) { boost::function foo = boost::bind(&SaveDecalAssetId, shared_from(decal), _1); Aya::ServiceProvider::create(m_pDataModel) ->getContent(decal->getTexture(), Aya::ContentProvider::PRIORITY_MFC, boost::bind(&PostFromContentProvider, _1, _2, 1, decal->getName(), weak_from(Aya::DataModel::get(decal)), foo)); return; } } if (Aya::SpecialShape* mesh = dynamic_cast(sel->front().get())) { bool uploadedSomething = false; if (!mesh->getMeshId().isHttp()) { boost::function foo = boost::bind(&SaveMeshMeshId, shared_from(mesh), _1); Aya::ServiceProvider::create(m_pDataModel) ->getContent(mesh->getMeshId(), Aya::ContentProvider::PRIORITY_MFC, boost::bind(&PostFromContentProvider, _1, _2, 4, mesh->getName(), weak_from(Aya::DataModel::get(mesh)), foo)); uploadedSomething = true; } if (!mesh->getTextureId().isHttp()) { boost::function foo = boost::bind(&SaveMeshTextureId, shared_from(mesh), _1); Aya::ServiceProvider::create(m_pDataModel) ->getContent(mesh->getTextureId(), Aya::ContentProvider::PRIORITY_MFC, boost::bind(&PostFromContentProvider, _1, _2, 1, mesh->getName(), weak_from(Aya::DataModel::get(mesh)), foo)); uploadedSomething = true; } if (uploadedSomething) { return; } } } } bool isAnimation = false; Aya::Selection* sel = Aya::ServiceProvider::find(m_pDataModel); if (sel && sel->size() == 1) { if (dynamic_cast(sel->front().get())) { isAnimation = true; } } // "http://www.roblox.com/UI/Save.aspx" QString initialUrl; if (isScript) initialUrl = QString("%1/UI/Save.aspx?type=Lua").arg(RobloxSettings::getBaseURL()); else if (isAnimation) { initialUrl = QString("%1/studio/animations/publish").arg(RobloxSettings::getBaseURL()); if (FFlag::StudioEnableGameAnimationsTab) { RobloxGameExplorer& gameExplorer = UpdateUIManager::Instance().getViewWidget(eDW_GAME_EXPLORER); if (gameExplorer.getCurrentGameId() > 0) initialUrl.append(QString("?universeId=%1").arg(gameExplorer.getCurrentGameId())); } } else initialUrl = QString("%1/UI/Save.aspx?type=Model").arg(RobloxSettings::getBaseURL()); if (!m_dlg) { m_dlg = new WebDialog(m_pMainWindow, initialUrl, m_pDataModel); m_dlg->setMinimumSize(FInt::StudioWebDialogMinimumWidth, FInt::StudioWebDialogMinimumHeight); } else m_dlg->load(initialUrl); m_dlg->show(); m_dlg->raise(); m_dlg->activateWindow(); } ///////////////////////////////////////////////////////////////////////////////////////////////////// CreateNewLinkedSourceVerb::CreateNewLinkedSourceVerb(DataModel* pVerbContainer) : Verb(pVerbContainer, "CreateNewLinkedSourceVerb") , m_pDataModel(pVerbContainer) { } bool CreateNewLinkedSourceVerb::isEnabled() const { DataModel::LegacyLock lock(m_pDataModel, DataModelJob::Read); shared_ptr lsc = getLuaSourceContainer(); return (UpdateUIManager::Instance().getViewWidget(eDW_GAME_EXPLORER).getCurrentGameId() > 0) && lsc && lsc->getScriptId().isNull() && (RobloxUser::singleton().getUserId() > 0); } void CreateNewLinkedSourceVerb::doIt(IDataState*) { std::string instanceName; std::string source; { DataModel::LegacyLock lock(m_pDataModel, DataModelJob::Read); shared_ptr instance = getLuaSourceContainer(); instanceName = instance->getName(); source = LuaSourceBuffer::fromInstance(instance).getScriptText(); } ScriptPickerDialog::CompletedState state; QString newName; ScriptPickerDialog dialog; dialog.runModal(NULL, QString::fromStdString(instanceName), &state, &newName); if (state != ScriptPickerDialog::Completed) { AYAASSERT(state == ScriptPickerDialog::Abandoned); return; } bool success; RobloxGameExplorer& rge = UpdateUIManager::Instance().getViewWidget(eDW_GAME_EXPLORER); UpdateUIManager::Instance().waitForLongProcess("Creating LinkedSource", boost::bind(&CreateNewLinkedSourceVerb::doItThread, this, source, rge.getCurrentGameId(), rge.getCurrentGameGroupId(), newName, &success)); rge.reloadDataFromWeb(); if (success) { try { DataModel::LegacyLock lock(m_pDataModel, DataModelJob::Write); shared_ptr lsc = getLuaSourceContainer(); LuaSourceBuffer lsb = LuaSourceBuffer::fromInstance(lsc); ContentId scriptName = ContentId::fromGameAssetName(newName.toStdString()); lsc->setScriptId(scriptName); ContentProvider* cp = m_pDataModel->create(); // clear content provider cache, in case the script has been fetched previously cp->invalidateCache(scriptName); m_pDataModel->create()->requestWaypoint("Store LinkedSource in cloud"); } catch (const Aya::base_exception&) { // catch failure to grab legacy lock, do nothing } } else { QMessageBox mb; mb.setText("Unable to create LinkedSource, see output for details."); mb.setStandardButtons(QMessageBox::Ok); mb.exec(); } } void CreateNewLinkedSourceVerb::doItThread(std::string source, int currentGameId, boost::optional groupId, QString newName, bool* success) { *success = false; try { EntityProperties createScriptAssetResponse; createScriptAssetResponse.setFromJsonFuture(RobloxGameExplorer::publishScriptAsset(source, boost::optional(), groupId)); int assetId = createScriptAssetResponse.get("AssetId").get(); EntityProperties createNameRequest; createNameRequest.set("Name", newName.toStdString()); if (FFlag::GameExplorerUseV2AliasEndpoint) { createNameRequest.set("Type", (int)ALIAS_TYPE_Asset); createNameRequest.set("TargetId", assetId); } else { createNameRequest.set("AssetId", assetId); } QString postUrl; postUrl = QString(FFlag::GameExplorerUseV2AliasEndpoint ? "%1/universes/create-alias-v2?universeId=%2" : "%1/universes/create-alias?universeId=%2") .arg(QString::fromStdString(ContentProvider::getApiBaseUrl(RobloxSettings::getBaseURL().toStdString()))) .arg(currentGameId); Http http(postUrl.toStdString()); std::string propertiesJson = createNameRequest.asJson(); std::istringstream propertiesStream(propertiesJson); std::string postResponse; // perform synchronous post http.post(propertiesStream, Http::kContentTypeApplicationJson, false, postResponse); *success = true; Aya::StandardOut::singleton()->print(MESSAGE_INFO, "Successfully created new LinkedSource"); } catch (const Aya::base_exception&) { *success = false; } } shared_ptr CreateNewLinkedSourceVerb::getLuaSourceContainer() const { if (Selection* selection = m_pDataModel->find()) { if (selection->size() == 1) { return Instance::fastSharedDynamicCast(selection->front()); } } return shared_ptr(); } ///////////////////////////////////////////////////////////////////////////////////////////////////// PublishAsPluginVerb::PublishAsPluginVerb(Aya::VerbContainer* pVerbContainer, RobloxMainWindow* mainWnd) : Verb(pVerbContainer, "PublishAsPluginVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) , m_pMainWindow(mainWnd) , m_dlg(NULL) { } bool PublishAsPluginVerb::isEnabled() const { return true; } void PublishAsPluginVerb::doIt(Aya::IDataState*) { // autosave before we publish just in case RobloxDocManager::Instance().getPlayDoc()->autoSave(false /*do not force autosave*/); if (!StudioUtilities::checkNetworkAndUserAuthentication()) return; Aya::DataModel::LegacyLock lock(m_pDataModel, Aya::DataModelJob::Write); QString initialUrl = QString("%1/studio/plugins/publish").arg(RobloxSettings::getBaseURL()); if (!m_dlg) m_dlg.reset(new WebDialog(m_pMainWindow, initialUrl, m_pDataModel)); else m_dlg->load(initialUrl); m_dlg->show(); m_dlg->raise(); m_dlg->activateWindow(); } ///////////////////////////////////////////////////////////////////////////////////////////////////// LaunchInstancesVerb::LaunchInstancesVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "LaunchInstancesVerb") , m_pVerbContainer(pVerbContainer) { } void LaunchInstancesVerb::doIt(Aya::IDataState* dataState) { Aya::Verb* pVerb; switch (NameValueStoreManager::singleton().getValue("clientsAndServersOptions", "user_value").toInt()) { case PLAYSOLO: pVerb = m_pVerbContainer->getVerb("PlaySoloVerb"); pVerb->doIt(dataState); break; case SERVERONEPLAYER: pVerb = m_pVerbContainer->getVerb("StartServerVerb"); pVerb->doIt(dataState); pVerb = m_pVerbContainer->getVerb("StartPlayerVerb"); pVerb->doIt(dataState); break; case SERVERFOURPLAYERS: pVerb = m_pVerbContainer->getVerb("StartServerVerb"); pVerb->doIt(dataState); pVerb = m_pVerbContainer->getVerb("StartPlayerVerb"); pVerb->doIt(dataState); pVerb->doIt(dataState); pVerb->doIt(dataState); pVerb->doIt(dataState); break; default: break; } } ///////////////////////////////////////////////////////////////////////////////////////////////////// StartServerAndPlayerVerb::StartServerAndPlayerVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "StartServerAndPlayerVerb") , m_pVerbContainer(pVerbContainer) { } void StartServerAndPlayerVerb::doIt(Aya::IDataState* dataState) { bool startServer = NameValueStoreManager::singleton().getValue("startServerCB", "checked").toBool(); int numPlayers = NameValueStoreManager::singleton().getValue("playersMode", "user_value").toInt(); // save values in settings (so it can be used for launching players from launched server) RobloxSettings settings; settings.setValue(sRibbonStartServerSetting, startServer); settings.setValue(sRibbonNumPlayerSetting, numPlayers); if (startServer) { // cleanup existing servers and players QAction* cleanupAction = UpdateUIManager::Instance().getMainWindow().cleanupServersAndPlayersAction; if (cleanupAction && cleanupAction->isEnabled()) cleanupAction->trigger(); // server will launch the players (this will make sure players are launched once the server is loaded) Aya::Verb* pVerb = m_pVerbContainer->getVerb("StartServerVerb"); if (pVerb) pVerb->doIt(dataState); } else if (numPlayers > 0) { // if there's no server to be launched then directly launch the number of players launchPlayers(numPlayers); } } void StartServerAndPlayerVerb::launchPlayers(int numPlayers) { UpdateUIManager::Instance().waitForLongProcess( "Starting players", boost::bind(&StartServerAndPlayerVerb::launchStudioInstances, m_pVerbContainer, numPlayers)); } void StartServerAndPlayerVerb::launchStudioInstances(Aya::VerbContainer* pVerbContainer, int numPlayers) { // this function will be called from a new thread if (numPlayers > 0) { Aya::Verb* pVerb = pVerbContainer->getVerb("StartPlayerVerb"); if (pVerb) { for (int ii = 1; ii <= numPlayers; ++ii) { pVerb->doIt(NULL); if (ii < numPlayers) QtUtilities::sleep(1000); } } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// ServerPlayersStateInitVerb::ServerPlayersStateInitVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "ServerPlayersStateInitVerb") { RobloxSettings settings; // initialize start server mode NameValueStoreManager::singleton().setValue("startServerCB", "checked", settings.value(sRibbonStartServerSetting, true).toBool()); // initialize default number of players int index = settings.value(sRibbonNumPlayerSetting, 1).toInt(); NameValueStoreManager::singleton().setValue("playersMode", "user_value", index); QComboBox* pComboBox = UpdateUIManager::Instance().getMainWindow().findChild("playersMode"); if (pComboBox) pComboBox->setCurrentIndex(index); } void ServerPlayersStateInitVerb::doIt(Aya::IDataState*) { RobloxSettings().setValue(sRibbonStartServerSetting, NameValueStoreManager::singleton().getValue("startServerCB", "checked").toBool()); } bool ServerPlayersStateInitVerb::isChecked() const { return RobloxSettings().value(sRibbonStartServerSetting, true).toBool(); } CreatePluginVerb::CreatePluginVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "CreatePluginVerb") , m_pVerbContainer(pVerbContainer) { } void CreatePluginVerb::doIt(Aya::IDataState* dataState) {} PlaySoloVerb::PlaySoloVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "PlaySoloVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { } void PlaySoloVerb::doIt(Aya::IDataState*) { // These should instead come from the Settings infra, once it is ready to go to the temp location QString fileSaveLocation = QString("%1/visit.rbxl").arg(GetAssetFolder().c_str()); QString errorMessage; // Remove debug data file, before starting new process if (FFlag::LuaDebugger) QFile::remove(StudioUtilities::getDebugInfoFile(fileSaveLocation)); if (!StudioSerializerHelper::saveAs(fileSaveLocation, "Play Solo", false, true, m_pDataModel, errorMessage, true)) { QMessageBox::critical(&UpdateUIManager::Instance().getMainWindow(), "Play Solo - Save Failure", errorMessage); return; } // loadfile('http://www.roblox.com/game/visit.ashx')() QString script; script = QString("loadfile(\"%1/game/visit.ashx?IsPlaySolo=1&placeId=%2&universeId=%3\")()\n") .arg(RobloxSettings::getBaseURL()) .arg(m_pDataModel->getPlaceID()) .arg(m_pDataModel->getUniverseId()); RobloxApplicationManager::instance().createNewStudioInstance(script, fileSaveLocation, true, true); } ManageEmulationDevVerb::ManageEmulationDevVerb(Aya::VerbContainer* pVerbContainer, QWidget* newParent) : Verb(pVerbContainer, "ManageEmulationDevVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { } void ManageEmulationDevVerb::doIt(Aya::IDataState* dataState) { ManageEmulationDeviceDialog dialog(NULL); dialog.exec(); } AudioToggleVerb::AudioToggleVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "AudioEnableVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { } void AudioToggleVerb::doIt(Aya::IDataState* dataState) { if (m_pDataModel) { if (Aya::Soundscape::SoundService* soundService = Aya::ServiceProvider::find(m_pDataModel)) { soundService->muteAllChannels(!soundService->isMuted()); } } } bool AudioToggleVerb::isChecked() const { if (m_pDataModel) { if (Aya::Soundscape::SoundService* soundService = Aya::ServiceProvider::find(m_pDataModel)) { return soundService->isMuted(); } } return false; } AnalyzePhysicsToggleVerb::AnalyzePhysicsToggleVerb(Aya::DataModel* pDataModel) : Aya::Verb(pDataModel, "AnalyzeEnableVerb") , m_pDataModel(pDataModel) { } bool AnalyzePhysicsToggleVerb::isEnabled() const { return FFlag::PhysicsAnalyzerEnabled; } bool AnalyzePhysicsToggleVerb::isChecked() const { return PhysicsSettings::singleton().getPhysicsAnalyzerState(); } void AnalyzePhysicsToggleVerb::startAnalyze() { PhysicsSettings::singleton().setPhysicsAnalyzerState(true); } void AnalyzePhysicsToggleVerb::stopAnalyze() { PhysicsSettings::singleton().setPhysicsAnalyzerState(false); } void AnalyzePhysicsToggleVerb::doIt(Aya::IDataState*) { PhysicsSettings::singleton().getPhysicsAnalyzerState() ? stopAnalyze() : startAnalyze(); } StartServerVerb::StartServerVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "StartServerVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { } void StartServerVerb::doIt(Aya::IDataState*) { // These should instead come from the Settings infra, once it is ready to go to the temp location QString fileSaveLocation = QString("ayaasset://server.rbxl"); QString errorMessage; if (!StudioSerializerHelper::saveAs(fileSaveLocation, "Start Server", false, true, m_pDataModel, errorMessage, true)) { QMessageBox::critical(&UpdateUIManager::Instance().getMainWindow(), "Start Server Failure", errorMessage); return; } // loadfile('http://www.roblox.com/game/gameserver.ashx')(, 53640) QString script; script = QString("loadfile(\"%1/game/gameserver.ashx\")(" "%2, 53640, nil, nil, nil, " "\"%1\", nil, nil, nil, nil, " "nil, nil, nil, nil, nil, " "nil, nil, %3)\n") .arg(::trim_trailing_slashes(RobloxSettings::getBaseURL().toStdString()).c_str()) .arg(m_pDataModel->getPlaceID()) .arg(m_pDataModel->getUniverseId()); if (RobloxIDEDoc::isEditMode(m_pDataModel)) RobloxApplicationManager::instance().createNewStudioInstance(script, fileSaveLocation, true, false, NewInstanceMode_Server); } StartPlayerVerb::StartPlayerVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "StartPlayerVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) { } void StartPlayerVerb::doIt(Aya::IDataState*) { // loadfile('http://www.roblox.com//game/join.ashx?UserID=0&serverPort=53640')() QString script; script = QString("loadfile(\"%1/game/join.ashx?UserID=0&serverPort=53640&universeId=%2\")()\n") .arg(RobloxSettings::getBaseURL()) .arg(m_pDataModel->getUniverseId()); if ((!Aya::Network::Players::clientIsPresent(m_pDataModel) && !Aya::Network::Players::findConstLocalPlayer(m_pDataModel))) RobloxApplicationManager::instance().createNewStudioInstance(script, QString(), true, true, NewInstanceMode_Player); } ToggleFullscreenVerb::ToggleFullscreenVerb(Aya::VerbContainer* container) : Verb(container, "ToggleFullScreen") { } void ToggleFullscreenVerb::doIt(Aya::IDataState*) { QAction& action = UpdateUIManager::Instance().getMainWindow().fullScreenAction(); bool checkedState = action.isChecked(); QMetaObject::invokeMethod(&action, "setChecked", Qt::QueuedConnection, Q_ARG(bool, !checkedState)); } bool ToggleFullscreenVerb::isEnabled() const { return true; } ShutdownClientVerb::ShutdownClientVerb(Aya::VerbContainer* container, IRobloxDoc* pDoc) : Verb(container, "ShutdownClient") , m_pIDEDoc(pDoc) { } void ShutdownClientVerb::doIt(Aya::IDataState*) { RobloxMainWindow* pMainWindow = &UpdateUIManager::Instance().getMainWindow(); if (pMainWindow) { QMetaObject::invokeMethod(pMainWindow, "forceClose", Qt::QueuedConnection); } } ShutdownClientAndSaveVerb::ShutdownClientAndSaveVerb(Aya::VerbContainer* container, IRobloxDoc* pDoc) : Verb(container, "ShutdownClientAndSave") , m_pIDEDoc(pDoc) { } void ShutdownClientAndSaveVerb::doIt(Aya::IDataState*) { RobloxMainWindow* pMainWindow = &UpdateUIManager::Instance().getMainWindow(); if (pMainWindow) { QMetaObject::invokeMethod(pMainWindow, "saveAndClose", Qt::QueuedConnection); } } LeaveGameVerb::LeaveGameVerb(Aya::VerbContainer* container, IRobloxDoc* pDoc) : Verb(container, "Exit") , m_pIDEDoc(pDoc) { } void LeaveGameVerb::doIt(Aya::IDataState*) { if (RobloxDocManager::Instance().getPlayDoc()) { RobloxMainWindow& mainWindow = UpdateUIManager::Instance().getMainWindow(); QMetaObject::invokeMethod(&mainWindow, "closePlayDoc", Qt::QueuedConnection); } } ToggleAxisWidgetVerb::ToggleAxisWidgetVerb(Aya::DataModel* dataModel) : Aya::Verb(dataModel, "ToggleAxisWidget") , m_pDataModel(dataModel) { Aya::Workspace* pWorkspace = m_pDataModel->getWorkspace(); if (pWorkspace) pWorkspace->setShowAxisWidget(UpdateUIManager::Instance().get3DAxisEnabled()); } void ToggleAxisWidgetVerb::doIt(Aya::IDataState*) { Aya::Workspace* pWorkspace = m_pDataModel->getWorkspace(); if (pWorkspace) { bool newValue = !pWorkspace->getShowAxisWidget(); pWorkspace->setShowAxisWidget(newValue); UpdateUIManager::Instance().set3DAxisEnabled(newValue); } } bool ToggleAxisWidgetVerb::isChecked() const { Aya::Workspace* pWorkspace = m_pDataModel->getWorkspace(); if (pWorkspace) return pWorkspace->getShowAxisWidget(); return false; } Toggle3DGridVerb::Toggle3DGridVerb(Aya::DataModel* dataModel) : Aya::Verb(dataModel, "Toggle3DGrid") , m_pDataModel(dataModel) { Aya::Workspace* workspace = m_pDataModel->getWorkspace(); if (workspace) workspace->setShow3DGrid(UpdateUIManager::Instance().get3DGridEnabled()); } void Toggle3DGridVerb::doIt(Aya::IDataState*) { Aya::Workspace* pWorkspace = m_pDataModel->getWorkspace(); if (pWorkspace) { bool newValue = !pWorkspace->getShow3DGrid(); pWorkspace->setShow3DGrid(newValue); UpdateUIManager::Instance().set3DGridEnabled(newValue); } } bool Toggle3DGridVerb::isEnabled() const { Aya::Network::Player* pLocalPlayer = Aya::Network::Players::findLocalPlayer(m_pDataModel); return pLocalPlayer == NULL; } bool Toggle3DGridVerb::isChecked() const { if (isEnabled()) { Aya::Workspace* pWorkspace = m_pDataModel->getWorkspace(); if (pWorkspace) return pWorkspace->getShow3DGrid(); } return false; } ToggleCollisionCheckVerb::ToggleCollisionCheckVerb(Aya::DataModel* dataModel) : Aya::Verb(dataModel, "ToggleCollisionCheckVerb") { RobloxSettings settings; Aya::AdvArrowTool::advCollisionCheckMode = settings.value(sCollisionToggleModeSetting, true).toBool(); } void ToggleCollisionCheckVerb::doIt(Aya::IDataState* dataState) { Aya::AdvArrowTool::advCollisionCheckMode = !Aya::AdvArrowTool::advCollisionCheckMode; RobloxSettings settings; settings.setValue(sCollisionToggleModeSetting, Aya::AdvArrowTool::advCollisionCheckMode); } bool ToggleCollisionCheckVerb::isChecked() const { return Aya::AdvArrowTool::advCollisionCheckMode; } ToggleLocalSpaceVerb::ToggleLocalSpaceVerb(Aya::DataModel* dataModel) : Aya::Verb(dataModel, "ToggleLocalSpaceVerb") , m_pDataModel(dataModel) { RobloxSettings settings; Aya::AdvArrowTool::advLocalTranslationMode = settings.value(sLocalTranslationModeSetting, false).toBool(); Aya::AdvArrowTool::advLocalRotationMode = settings.value(sLocalRotationModeSetting, true).toBool(); } void ToggleLocalSpaceVerb::doIt(Aya::IDataState* dataState) { Aya::MouseCommand* mouseCommand = m_pDataModel->getWorkspace()->getCurrentMouseCommand(); if (!mouseCommand) return; if (DFFlag::UseRemoveTypeIDTricks) { if (Aya::AdvMoveTool::name() == mouseCommand->getName()) { Aya::AdvArrowTool::advLocalTranslationMode = !Aya::AdvArrowTool::advLocalTranslationMode; RobloxSettings settings; settings.setValue(sLocalTranslationModeSetting, Aya::AdvArrowTool::advLocalTranslationMode); } else if (Aya::AdvRotateTool::name() == mouseCommand->getName()) { Aya::AdvArrowTool::advLocalRotationMode = !Aya::AdvArrowTool::advLocalRotationMode; RobloxSettings settings; settings.setValue(sLocalRotationModeSetting, Aya::AdvArrowTool::advLocalRotationMode); } } else { if (typeid(Aya::AdvMoveTool) == typeid(*mouseCommand)) { Aya::AdvArrowTool::advLocalTranslationMode = !Aya::AdvArrowTool::advLocalTranslationMode; RobloxSettings settings; settings.setValue(sLocalTranslationModeSetting, Aya::AdvArrowTool::advLocalTranslationMode); } else if (typeid(Aya::AdvRotateTool) == typeid(*mouseCommand)) { Aya::AdvArrowTool::advLocalRotationMode = !Aya::AdvArrowTool::advLocalRotationMode; RobloxSettings settings; settings.setValue(sLocalRotationModeSetting, Aya::AdvArrowTool::advLocalRotationMode); } } } bool ToggleLocalSpaceVerb::isChecked() const { Aya::MouseCommand* mouseCommand = m_pDataModel->getWorkspace()->getCurrentMouseCommand(); if (!mouseCommand) return false; if (typeid(Aya::AdvMoveTool) == typeid(*mouseCommand)) { return Aya::AdvArrowTool::advLocalRotationMode; } else if (typeid(Aya::AdvRotateTool) == typeid(*mouseCommand)) { return Aya::AdvArrowTool::advLocalTranslationMode; } return false; } ExportSelectionVerb::ExportSelectionVerb(Aya::DataModel* pDataModel) : Aya::Verb(pDataModel, "ExportSelectionVerb") , m_pDataModel(pDataModel) { } void ExportSelectionVerb::doIt(Aya::IDataState*) { QTimer::singleShot(0, RobloxDocManager::Instance().getPlayDoc(), SLOT(exportSelection())); } ExportPlaceVerb::ExportPlaceVerb(Aya::DataModel* pDataModel) : Aya::Verb(pDataModel, "ExportPlaceVerb") , m_pDataModel(pDataModel) { } void ExportPlaceVerb::doIt(Aya::IDataState*) { QTimer::singleShot(0, RobloxDocManager::Instance().getPlayDoc(), SLOT(exportPlace())); } PublishToRobloxVerb::PublishToRobloxVerb(Aya::VerbContainer* pVerbContainer, RobloxMainWindow* pMainWindow) : Verb(pVerbContainer, "PublishToRobloxVerb") , m_pDataModel(dynamic_cast(pVerbContainer)) , m_pMainWindow(pMainWindow) , m_bIsPublishInProcess(false) { // Hook up our signal from the datamodel m_pDataModel->saveFinishedSignal.connect(boost::bind(&PublishToRobloxVerb::onEventPublishingFinished, this)); } void PublishToRobloxVerb::onEventPublishingFinished() { m_bIsPublishInProcess = false; } void PublishToRobloxVerb::doIt(Aya::IDataState* dataState) { // autosave before publish just in case RobloxDocManager::Instance().getPlayDoc()->autoSave(true); const char* failedToPublishErrorMsg = "Failed to Publish"; if (!StudioUtilities::checkNetworkAndUserAuthentication()) return; // check if mac banned if (!AuthenticationHelper::validateMachine()) throw std::runtime_error(qPrintable(failedToPublishErrorMsg)); std::string uploadUrl; if (Aya::Visit* visit = Aya::ServiceProvider::find(m_pDataModel)) uploadUrl = visit->getUploadUrl(); if (!uploadUrl.empty()) { { // Concurrency guard publishingMutex.lock(); if (m_bIsPublishInProcess) { publishingMutex.unlock(); throw std::runtime_error(qPrintable(failedToPublishErrorMsg)); } m_bIsPublishInProcess = true; publishingMutex.unlock(); } bool error; QString errorTitle; QString errorText; UpdateUIManager::Instance().waitForLongProcess( "Publishing", boost::bind(&PublishToRobloxVerb::save, this, Aya::ContentId(uploadUrl), &error, &errorTitle, &errorText)); if (error) { QMessageBox::critical(m_pMainWindow, errorTitle, errorText); } else { if (!RobloxDocManager::Instance().getPlayDoc()->isLocalDoc()) { RobloxDocManager::Instance().getPlayDoc()->resetDirty(m_pDataModel); } Aya::StandardOut::singleton()->printf( Aya::MESSAGE_INFO, "Successfully published - %s", qPrintable(RobloxDocManager::Instance().getPlayDoc()->displayName())); } } else throw Aya::runtime_error("Cannot publish without a visit script!"); } bool PublishToRobloxVerb::isEnabled() const { return (!m_bIsPublishInProcess && m_pDataModel && Aya::ServiceProvider::find(m_pDataModel) && Aya::DataModel::canSave(m_pDataModel)); } void PublishToRobloxVerb::save(Aya::ContentId contentID, bool* outError, QString* outErrorTitle, QString* outErrorText) { AYAASSERT(outError); AYAASSERT(outErrorTitle); AYAASSERT(outErrorText); *outError = true; *outErrorTitle = "Failed to Publish"; *outErrorText = "Failed to publish place!"; try { if (FFlag::StudioCSGAssets) PartOperationAsset::publishAll(m_pDataModel); // it is possible to publish from build mode, where game explorer isn't initialized RobloxGameExplorer& rge = UpdateUIManager::Instance().getViewWidget(eDW_GAME_EXPLORER); if (rge.getCurrentGameId() > 0) rge.publishNamedAssetsToCurrentSlot(); *outError = false; } catch (Aya::DataModel::SerializationException e) { m_bIsPublishInProcess = false; shared_ptr values(new Aya::Reflection::ValueTable); std::stringstream jsonStream(e.what()); bool parsed = Aya::WebParser::parseJSONTable(e.what(), values); if (parsed) { Aya::Reflection::ValueTable::const_iterator itTitle = values->find("title"); Aya::Reflection::ValueTable::const_iterator itError = values->find("error"); if (itTitle != values->end()) *outErrorTitle = itTitle->second.get().c_str(); if (itError != values->end()) *outErrorText = itError->second.get().c_str(); if (itError == values->end()) { Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "Error while publishing: %s", e.what()); } } else { *outErrorText = e.what(); } } catch (const Aya::base_exception& e) { m_bIsPublishInProcess = false; *outErrorTitle = "Error while publishing"; *outErrorText = e.what(); Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "Error while publishing: %s", e.what()); } catch (...) { m_bIsPublishInProcess = false; // NOTE: because this function is called in waitForLong process, all exceptions are swallowed // without notifying user. throw; } } InsertAdvancedObjectViewVerb::InsertAdvancedObjectViewVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "InsertAdvancedObjectDialogVerb") { // make sure we remain in sync with dockwidget's toggled state UpdateUIManager& uiManager = UpdateUIManager::Instance(); QObject::connect( uiManager.getDockAction(eDW_BASIC_OBJECTS), SIGNAL(toggled(bool)), uiManager.getAction("actionInsertAdvancedObject"), SLOT(toggle())); } InsertAdvancedObjectViewVerb::~InsertAdvancedObjectViewVerb() { UpdateUIManager& uiManager = UpdateUIManager::Instance(); QObject::disconnect( uiManager.getDockAction(eDW_BASIC_OBJECTS), SIGNAL(toggled(bool)), uiManager.getAction("actionInsertAdvancedObject"), SLOT(toggle())); } void InsertAdvancedObjectViewVerb::doIt(Aya::IDataState* dataState) { if (UpdateUIManager::Instance().getDockAction(eDW_BASIC_OBJECTS)) UpdateUIManager::Instance().getDockAction(eDW_BASIC_OBJECTS)->trigger(); } bool InsertAdvancedObjectViewVerb::isEnabled() const { return RobloxDocManager::Instance().getPlayDoc() != NULL; } bool InsertAdvancedObjectViewVerb::isChecked() const { return UpdateUIManager::Instance().getDockWidget(eDW_BASIC_OBJECTS)->isVisible(); } JointToolHelpDialogVerb::JointToolHelpDialogVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "JointToolHelpDialogVerb") { } void JointToolHelpDialogVerb::doIt(Aya::IDataState* dataState) {} StudioMaterialVerb::StudioMaterialVerb(Aya::DataModel* dataModel) : MaterialVerb(dataModel, "StudioMaterialVerb") { // initialize default value StudioMaterialVerb::sMaterialActionActAsTool = RobloxSettings().value("rbxMaterialActionActAsTool", false).toBool(); ; } void StudioMaterialVerb::doIt(Aya::IDataState* dataState) { // set material QString currentMaterial = NameValueStoreManager::singleton().getValue("actionMaterialSelector", "user_value").toString(); if (currentMaterial.isEmpty()) return; // check if we need to execute action as tool if (StudioMaterialVerb::sMaterialActionActAsTool) { // set material Aya::MaterialTool::material = Aya::MaterialVerb::parseMaterial(currentMaterial.toStdString()); // get verb associated with tool and execute Aya::Verb* materialVerb = dataModel->getVerb("MaterialTool"); if (materialVerb) materialVerb->doIt(dataState); } else { Aya::MaterialVerb::setCurrentMaterial(Aya::MaterialVerb::parseMaterial(currentMaterial.toStdString())); // execute verb Aya::MaterialVerb::doIt(dataState); } } bool StudioMaterialVerb::isChecked() const { if (StudioMaterialVerb::sMaterialActionActAsTool) { Aya::Verb* materialVerb = dataModel->getVerb("MaterialTool"); if (materialVerb) return materialVerb->isChecked(); } return false; } StudioColorVerb::StudioColorVerb(Aya::DataModel* dataModel) : ColorVerb(dataModel, "StudioColorVerb") { addColorToIcon(); // initialize default value StudioColorVerb::sColorActionActAsTool = RobloxSettings().value("rbxColorActionActAsTool", false).toBool(); } void StudioColorVerb::doIt(Aya::IDataState* dataState) { // set color Aya::BrickColor selectedBrickColor(NameValueStoreManager::singleton().getValue("actionColorSelector", "user_value").toInt()); Aya::ColorVerb::setCurrentColor(selectedBrickColor); if (StudioColorVerb::sColorActionActAsTool) { // color for fill tool Aya::FillTool::color.set(selectedBrickColor); // execute fill tool Aya::Verb* fillToolVerb = dataModel->getVerb("FillTool"); if (fillToolVerb) fillToolVerb->doIt(dataState); } else { // execute verb Aya::ColorVerb::doIt(dataState); } // updata action icon addColorToIcon(); } bool StudioColorVerb::isChecked() const { if (StudioColorVerb::sColorActionActAsTool) { Aya::Verb* fillToolVerb = dataModel->getVerb("FillTool"); if (fillToolVerb) return fillToolVerb->isChecked(); } return false; } void StudioColorVerb::addColorToIcon() { // update icon QList colorActions = UpdateUIManager::Instance().getMainWindow().findChildren("actionColorSelector"); for (int i = 0; i < colorActions.count(); i++) { QAction* pColorAction = colorActions[i]; QColor selectedQColor = QtUtilities::toQColor(Aya::ColorVerb::getCurrentColor().color3()); QPixmap pix = pColorAction->icon().pixmap(pColorAction->icon().availableSizes()[0]); QImage image = pix.toImage(); // Convert QPixmap to QImage image.fill(Qt::transparent); // Fill the image with transparency QPainter p; p.begin(&image); QRect rect = image.rect(); // Adjust the rectangle to make the circle smaller int adjustment = 2; // Adjust this value to make the circle smaller or larger QRect adjustedRect = rect.adjusted(adjustment, adjustment, -adjustment, -adjustment); p.setRenderHint(QPainter::Antialiasing); // Enable anti-aliasing p.setBrush(selectedQColor); p.drawEllipse(adjustedRect); // Draw a circle with a black outline QPen pen(QColor(91, 91, 91)); // Set the pen color to rgb(91,91,91) pen.setWidth(2); p.setPen(pen); p.setBrush(Qt::NoBrush); p.drawEllipse(adjustedRect); p.end(); pColorAction->setIcon(QIcon(QPixmap::fromImage(image))); // Convert QImage back to QPixmap } } OpenToolBoxWithOptionsVerb::OpenToolBoxWithOptionsVerb(Aya::VerbContainer* pVerbContainer) : Verb(pVerbContainer, "OpenToolBoxWithOptionsVerb") { connect(UpdateUIManager::Instance().getDockWidget(eDW_TOOLBOX), SIGNAL(visibilityChanged(bool)), this, SLOT(handleDockVisibilityChanged(bool))); } OpenToolBoxWithOptionsVerb::~OpenToolBoxWithOptionsVerb() { disconnect( UpdateUIManager::Instance().getDockWidget(eDW_TOOLBOX), SIGNAL(visibilityChanged(bool)), this, SLOT(handleDockVisibilityChanged(bool))); } void OpenToolBoxWithOptionsVerb::doIt(Aya::IDataState* dataState) { QString setUrl = NameValueStoreManager::singleton().getValue("openToolBoxWithOptions", "user_value").toString(); if (!setUrl.isEmpty()) { RobloxMainWindow& rbxMainWindow = UpdateUIManager::Instance().getMainWindow(); if (NameValueStoreManager::singleton().getValue("openToolBoxWithOptions", "requiresauthentication").toBool() && !RobloxUser::singleton().getUserId()) { QMessageBox::information(&rbxMainWindow, "Log in required", "You must log in to perform this action!", QDialogButtonBox::Ok); if (!rbxMainWindow.actionStartPage->isChecked()) rbxMainWindow.actionStartPage->setChecked(true); rbxMainWindow.openStartPage(true, "showlogin=True"); return; } RobloxToolBox& robloxToolBox = UpdateUIManager::Instance().getViewWidget(eDW_TOOLBOX); robloxToolBox.loadUrl(QString("%1/%2").arg(RobloxSettings::getBaseURL()).arg(setUrl)); UpdateUIManager::Instance().setDockVisibility(eDW_TOOLBOX, true); } else { UpdateUIManager::Instance().setDockVisibility(eDW_TOOLBOX, false); } } bool OpenToolBoxWithOptionsVerb::isEnabled() { return RobloxDocManager::Instance().getPlayDoc() != NULL; } void OpenToolBoxWithOptionsVerb::handleDockVisibilityChanged(bool isVisible) { if (!isVisible) { QActionGroup* pActionGroup = UpdateUIManager::Instance().getMainWindow().findChild("openToolBoxWithOptions"); if (pActionGroup) { QList actions = pActionGroup->actions(); for (int i = 0; i < actions.count(); i++) actions.at(i)->setChecked(false); } } } InsertBasicObjectVerb::InsertBasicObjectVerb(Aya::DataModel* dataModel) : Verb(dataModel, "InsertBasicObjectVerb") , m_pDataModel(dataModel) { } void InsertBasicObjectVerb::doIt(Aya::IDataState* dataState) { QString instanceName = NameValueStoreManager::singleton().getValue("insertBasicObject", "user_value").toString(); if (instanceName.isEmpty()) return; boost::shared_ptr pInstance = Aya::Creatable::createByName(Aya::Name::lookup(instanceName.toStdString()), Aya::EngineCreator); if (!pInstance) { // Custom logic here for data types that aren't in our instances if (instanceName == "Cylinder") { shared_ptr part = Aya::Creatable::create(); part->setLegacyPartTypeUi(Aya::BasicPartInstance::CYLINDER_LEGACY_PART); part->getPartPrimitive()->setSurfaceType(Aya::NORM_Y, Aya::NO_SURFACE); part->getPartPrimitive()->setSurfaceType(Aya::NORM_Y_NEG, Aya::NO_SURFACE); pInstance = part; } else if (instanceName == "Sphere") { shared_ptr part = Aya::Creatable::create(); part->setLegacyPartTypeUi(Aya::BasicPartInstance::BALL_LEGACY_PART); part->getPartPrimitive()->setSurfaceType(Aya::NORM_Y, Aya::NO_SURFACE); part->getPartPrimitive()->setSurfaceType(Aya::NORM_Y_NEG, Aya::NO_SURFACE); part->getPartPrimitive()->setSize(G3D::Vector3(4.0f, 4.0f, 4.0f)); pInstance = part; } else return; } // Insert it InsertObjectWidget::InsertObject(pInstance, shared_from(m_pDataModel), InsertObjectWidget::InsertMode_RibbonAction); } bool InsertBasicObjectVerb::isEnabled() { return RobloxDocManager::Instance().getPlayDoc() != NULL; } JointCreationModeVerb::JointCreationModeVerb(Aya::DataModel* dataModel) : Verb(dataModel, "JointCreationModeVerb") { RobloxSettings settings; int jointCreationMode = settings.value(sRibbonJointCreationMode, 0).toInt(); if (jointCreationMode == 2) { Aya::AdvArrowTool::advManualJointMode = false; Aya::AdvArrowTool::advCreateJointsMode = false; } else { Aya::AdvArrowTool::advManualJointMode = (bool)jointCreationMode; Aya::AdvArrowTool::advCreateJointsMode = true; } updateMenuActions(); updateMenuIcon(); } void JointCreationModeVerb::doIt(Aya::IDataState* dataState) { QString jointCreationMode = NameValueStoreManager::singleton().getValue("jointCreationMode", "user_value").toString(); if (jointCreationMode.isEmpty()) return; RobloxSettings settings; if (jointCreationMode == "Never") { Aya::AdvArrowTool::advManualJointMode = false; Aya::AdvArrowTool::advCreateJointsMode = false; settings.setValue(sRibbonJointCreationMode, 2); } else if (jointCreationMode == "Always") { Aya::AdvArrowTool::advManualJointMode = true; Aya::AdvArrowTool::advCreateJointsMode = true; settings.setValue(sRibbonJointCreationMode, 1); } else { Aya::AdvArrowTool::advManualJointMode = false; Aya::AdvArrowTool::advCreateJointsMode = true; settings.setValue(sRibbonJointCreationMode, 0); } updateMenuIcon(); } void JointCreationModeVerb::updateMenuIcon() { QList menuList = UpdateUIManager::Instance().getMainWindow().findChildren("jointCreationMode"); QActionGroup* pActionGroup = UpdateUIManager::Instance().getMainWindow().findChild("jointCreationMode"); if (!menuList.size() || !pActionGroup || !pActionGroup->checkedAction()) return; for (int ii = 0; ii < menuList.size(); ++ii) menuList[ii]->setIcon(pActionGroup->checkedAction()->icon()); } void JointCreationModeVerb::updateMenuActions() { QActionGroup* pActionGroup = UpdateUIManager::Instance().getMainWindow().findChild("jointCreationMode"); if (pActionGroup) { QList actions = pActionGroup->actions(); if (actions.count() == 3) { actions.at(0)->setChecked(Aya::AdvArrowTool::advManualJointMode && Aya::AdvArrowTool::advCreateJointsMode); actions.at(1)->setChecked(!Aya::AdvArrowTool::advManualJointMode && Aya::AdvArrowTool::advCreateJointsMode); actions.at(2)->setChecked(!Aya::AdvArrowTool::advManualJointMode && !Aya::AdvArrowTool::advCreateJointsMode); } } }