/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "PrefabPanel.h" #include #include "Prefabs/PrefabManager.h" #include "Objects/PrefabObject.h" #include "Prefabs/PrefabItem.h" #include #include #include class CUndoGroupObjectOpenClose : public IUndoObject { public: CUndoGroupObjectOpenClose(CPrefabObject* prefabObj) { m_prefabObject = prefabObj; m_bOpenForUndo = m_prefabObject->IsOpen(); } protected: virtual int GetSize() { return sizeof(*this); }; // Return size of xml state. virtual QString GetDescription() { return "Prefab's Open/Close"; }; virtual void Undo(bool bUndo) { m_bRedo = bUndo; Apply(m_bOpenForUndo); } virtual void Redo() { if (m_bRedo) { Apply(!m_bOpenForUndo); } } void Apply(bool bOpen) { if (m_prefabObject) { if (bOpen) { m_prefabObject->Open(); } else { m_prefabObject->Close(); } } } private: CPrefabObjectPtr m_prefabObject; bool m_bRedo; bool m_bOpenForUndo; }; #define COLUMN_NAME 0 #define COLUMN_TYPE 1 CPrefabPanel::CPrefabPanel(QWidget* pParent /*=nullptr*/) : QWidget(pParent) , m_type(0) , m_changePivotMode(false) , ui(new Ui::CPrefabPanel) , m_updatingObjectList(false) { ui->setupUi(this); m_pPrefabObject = 0; GetIEditor()->GetObjectManager()->AddObjectEventListener(functor(*this, &CPrefabPanel::OnObjectEvent)); connect(ui->PREFAB, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedGotoPrefab); connect(ui->REMOVE, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedDelete); connect(ui->CLONE_SELECTED, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedCloneSelected); connect(ui->CLONE_ALL, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedCloneAll); connect(ui->EXTRACT_SELECTED, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedExtractSelected); connect(ui->EXTRACT_ALL, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedExtractAll); connect(ui->OPEN, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedOpen); connect(ui->CHANGE_PIVOT, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedChangePivot); connect(ui->CLOSE, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedClose); connect(ui->CLOSEALL, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedCloseAll); connect(ui->OPENALL, &QPushButton::clicked, this, &CPrefabPanel::OnBnClickedOpenAll); connect(ui->OBJECTS, &QWidget::customContextMenuRequested, this, &CPrefabPanel::OnContextMenu); connect(ui->OBJECTS, &QTableWidget::itemSelectionChanged, this, &CPrefabPanel::OnItemchangedObjects); connect(ui->OBJECTS, &QTableWidget::itemDoubleClicked, this, &CPrefabPanel::OnDblclkObjects); m_prefabButtons.push_back(ui->PICK_NEW); m_prefabButtons.push_back(ui->REMOVE); m_prefabButtons.push_back(ui->CLONE_SELECTED); m_prefabButtons.push_back(ui->CLONE_ALL); m_prefabButtons.push_back(ui->EXTRACT_SELECTED); m_prefabButtons.push_back(ui->EXTRACT_ALL); m_prefabButtons.push_back(ui->OPEN); m_prefabButtons.push_back(ui->CLOSE); m_prefabButtons.push_back(ui->CHANGE_PIVOT); ui->PICK_NEW->SetPickCallback(this, tr("Pick Object To Attach")); ui->REMOVE->setEnabled(false); ui->EXTRACT_SELECTED->setEnabled(false); ui->CLONE_SELECTED->setEnabled(false); } CPrefabPanel::~CPrefabPanel() { m_prefabButtons.clear(); SetPrefabPivotModeOnCurrentEditTool(false); GetIEditor()->GetObjectManager()->RemoveObjectEventListener(functor(*this, &CPrefabPanel::OnObjectEvent)); } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnContextMenu() { std::vector selectedObjects; int selectedCount = GetSelectedObjects(selectedObjects); if (selectedCount < 1) { return; } QMenu menu; enum { eCmd_Delete = 1, eCmd_Clone, eCmd_Extract, eCmd_Pivot, eCmd_Last }; menu.addAction(tr("Delete Object(s)"))->setData(eCmd_Delete); menu.addAction(tr("Clone Object(s)"))->setData(eCmd_Clone); menu.addAction(tr("Extract Object(s)"))->setData(eCmd_Extract); if (selectedCount == 1) { menu.addSeparator(); menu.addAction(tr("Use Pivot for Prefab"))->setData(eCmd_Pivot); } QAction* action = menu.exec(QCursor::pos()); int nResult = action ? action->data().toInt() : -1; switch (nResult) { case eCmd_Delete: OnBnClickedDelete(); break; case eCmd_Clone: OnBnClickedCloneSelected(); break; case eCmd_Extract: OnBnClickedExtractSelected(); break; case eCmd_Pivot: if (QMessageBox::question(this, tr("Apply new pivot"), tr("This will affect all instances of this prefab in your level.\n\nDo you want to continue?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { m_pPrefabObject->SetPivot(selectedObjects[0]->GetWorldPos()); } break; } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::SetObject(CPrefabObject* object) { assert(object); m_pPrefabObject = object; ReloadObjects(); } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnObjectEvent(CBaseObject* pObject, int nEvent) { if (nEvent == CBaseObject::ON_PREFAB_CHANGED) { if (pObject->GetPrefab() == m_pPrefabObject || pObject == m_pPrefabObject) { ReloadObjects(); } } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::RecursivelyGetAllPrefabChilds(CBaseObject* obj, std::vector& childs, int level) { if (!obj) { return; } for (int i = 0; i < obj->GetChildCount(); i++) { CBaseObject* pChild = obj->GetChild(i); if (pChild->CheckFlags(OBJFLAG_PREFAB) && pChild->GetGroup() == pChild->GetPrefab()) { ChildRecord rec; rec.pObject = pChild; rec.level = level; rec.bSelected = false; childs.push_back(rec); if (!qobject_cast(pChild)) { RecursivelyGetAllPrefabChilds(pChild, childs, level + 1); } } } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::ReloadListCtrl() { m_updatingObjectList = true; ui->OBJECTS->clear(); ui->OBJECTS->setColumnCount(2); ui->OBJECTS->setRowCount(m_childObjects.size()); ui->OBJECTS->setHorizontalHeaderLabels({tr("Name"), tr("Type")}); for (int i = 0; i < m_childObjects.size(); i++) { const ChildRecord& record = m_childObjects[i]; // KDAB_PORT original code sets an "indent = record.level" on this item // see https://msdn.microsoft.com/en-us/library/windows/desktop/bb774760(v=vs.85).aspx // use variable width pixmap, size arbitrary since original is unknown QString text = record.pObject->GetName(); QTableWidgetItem* item = new QTableWidgetItem(text); if (record.level) { QPixmap pm(12 * record.level, 12); pm.fill(Qt::transparent); item->setIcon(QIcon(pm)); } ui->OBJECTS->setItem(i, 0, item); item = new QTableWidgetItem(record.pObject->GetTypeDescription()); ui->OBJECTS->setItem(i, 1, item); } m_updatingObjectList = false; } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::ReloadObjects() { m_childObjects.clear(); RecursivelyGetAllPrefabChilds(m_pPrefabObject, m_childObjects, 0); ReloadListCtrl(); if (m_pPrefabObject) { if (m_pPrefabObject->IsOpen()) { ui->OPEN->setEnabled(false); ui->CLOSE->setEnabled(true); } else { ui->OPEN->setEnabled(true); ui->CLOSE->setEnabled(false); } BOOL bEnableButtons = m_childObjects.empty() ? FALSE : TRUE; ui->CLONE_ALL->setEnabled(bEnableButtons); ui->EXTRACT_ALL->setEnabled(bEnableButtons); CPrefabItem* pItem = m_pPrefabObject->GetPrefab(); if (pItem) { int numObjects = m_childObjects.size(); ui->PREFAB_NAME->setText(tr("%1 (%2 Object%3)").arg(pItem->GetFullName()).arg(numObjects).arg(numObjects > 1 ? "s" : "")); int numInstances = GetIEditor()->GetPrefabManager()->GetPrefabInstanceCount(pItem); ui->STATIC1->setTitle(tr("Local (%1 Instance%2)").arg(numInstances).arg(numInstances > 1 ? "s" : "")); } } // Multiple prefab selection else { ui->OBJECTS->setEnabled(false); ui->PREFAB_NAME->setText(tr("Prefab Selection")); ui->PREFAB_NAME->setEnabled(false); ui->PICK_NEW->setEnabled(false); ui->EXTRACT_SELECTED->setEnabled(false); ui->REMOVE->setEnabled(false); ui->PREFAB->setEnabled(false); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnItemchangedObjects() { if (m_updatingObjectList) { return; } QModelIndexList selection = ui->OBJECTS->selectionModel()->selectedRows(); BOOL bEnableButtons = selection.size(); ui->REMOVE->setEnabled(bEnableButtons); ui->EXTRACT_SELECTED->setEnabled(bEnableButtons); ui->CLONE_SELECTED->setEnabled(bEnableButtons); for (int i = 0; i < ui->OBJECTS->rowCount(); ++i) { m_childObjects[i].bSelected = false; } for (int i = 0; i < selection.size(); ++i) { m_childObjects[selection[i].row()].bSelected = true; } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnDblclkObjects(QTableWidgetItem* item) { ChildRecord& record = m_childObjects[item->row()]; CBaseObject* pObject = record.pObject; if (pObject) { GetIEditor()->ClearSelection(); GetIEditor()->SelectObject(pObject); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedCloneSelected() { if (!m_pPrefabObject) { return; } std::vector objectsToClone; if (GetSelectedObjects(objectsToClone) > 0) { GetIEditor()->GetPrefabManager()->CloneObjectsFromPrefabs(objectsToClone); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedCloneAll() { std::vectorpPrefabs; if (m_pPrefabObject) { pPrefabs.push_back(m_pPrefabObject); } else if (CSelectionGroup* pSelection = GetIEditor()->GetSelection()) { for (int i = 0, count = pSelection->GetCount(); i < count; i++) { if (qobject_cast(pSelection->GetObject(i))) { CPrefabObject* pPrefab = static_cast(pSelection->GetObject(i)); pPrefabs.push_back(pPrefab); } } } if (!pPrefabs.empty()) { GetIEditor()->GetPrefabManager()->CloneAllFromPrefabs(pPrefabs); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedExtractSelected() { if (!m_pPrefabObject) { return; } std::vector objectsToExtract; if (GetSelectedObjects(objectsToExtract) > 0) { GetIEditor()->GetPrefabManager()->ExtractObjectsFromPrefabs(objectsToExtract); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedExtractAll() { if (CryMessageBox("Are you sure you want to extract all objects from this prefab?", "Extract objects", MB_YESNO | MB_ICONWARNING) == IDNO) { return; } std::vectorpPrefabs; if (m_pPrefabObject) { pPrefabs.push_back(m_pPrefabObject); } else if (CSelectionGroup* pSelection = GetIEditor()->GetSelection()) { for (int i = 0, count = pSelection->GetCount(); i < count; i++) { if (qobject_cast(pSelection->GetObject(i))) { CPrefabObject* pPrefab = static_cast(pSelection->GetObject(i)); pPrefabs.push_back(pPrefab); } } } if (!pPrefabs.empty()) { GetIEditor()->GetPrefabManager()->ExtractAllFromPrefabs(pPrefabs); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedGotoPrefab() { if (m_pPrefabObject) { GetIEditor()->OpenDataBaseLibrary(EDB_TYPE_PREFAB, m_pPrefabObject->GetPrefab()); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedOpen() { if (m_pPrefabObject && !m_pPrefabObject->IsOpen()) { std::vector selectedPrefabs(1, m_pPrefabObject); if (GetIEditor()->GetPrefabManager()->OpenPrefabs(selectedPrefabs, "Open prefab object")) { ReloadObjects(); } } else if (CSelectionGroup* pSelection = GetIEditor()->GetSelection()) { const int count = pSelection->GetCount(); std::vector selectedPrefabs(count); for (int i = 0; i < count; ++i) { selectedPrefabs[i] = static_cast(pSelection->GetObject(i)); } if (GetIEditor()->GetPrefabManager()->OpenPrefabs(selectedPrefabs, "Open selected prefab object")) { ReloadObjects(); } } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedChangePivot() { if (!m_changePivotMode) { m_changePivotMode = true; ui->CHANGE_PIVOT->setText(tr("Pivot Mode is ON")); } else { m_changePivotMode = false; ui->CHANGE_PIVOT->setText(tr("Pivot Mode is OFF")); } SetPrefabPivotModeOnCurrentEditTool(m_changePivotMode); } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedClose() { if (m_pPrefabObject && m_pPrefabObject->IsOpen()) { std::vector selectedPrefabs(1, m_pPrefabObject); if (GetIEditor()->GetPrefabManager()->ClosePrefabs(selectedPrefabs, "Close prefab object")) { ReloadObjects(); } } else if (CSelectionGroup* pSelection = GetIEditor()->GetSelection()) { const int count = pSelection->GetCount(); std::vector selectedPrefabs(count); for (int i = 0; i < count; ++i) { selectedPrefabs[i] = static_cast(pSelection->GetObject(i)); } if (GetIEditor()->GetPrefabManager()->ClosePrefabs(selectedPrefabs, "Close selected prefab objects")) { ReloadObjects(); } } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedDelete() { if (!m_pPrefabObject) { return; } CUndo undo("Delete Object(s)"); std::vector selectedObjects; int objectCount = GetSelectedObjects(selectedObjects); for (int i = 0; i < objectCount; ++i) { GetIEditor()->GetObjectManager()->DeleteObject(selectedObjects[i]); } if (objectCount > 0) { ReloadObjects(); GetIEditor()->GetObjectManager()->InvalidateVisibleList(); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnPick(CBaseObject* picked) { if (m_pPrefabObject) { if (!m_pPrefabObject->CanObjectBeAddedAsMember(picked)) { Warning("Object %s is already part of a prefab (%s)", picked->GetName().toUtf8().constData(), picked->GetPrefab()->GetName().toUtf8().constData()); return; } // TO DO notification for GD change TBaseObjects objects; CUndo undo("Add Object To Prefab"); if (CUndo::IsRecording()) { // If this is not a group add all the attached children to the prefab, // otherwise the group children adding is handled by the AddMember in CPrefabObject if (!qobject_cast(picked)) { objects.reserve(picked->GetChildCount() + 1); objects.push_back(picked); picked->GetAllChildren(objects); } else { objects.push_back(picked); } CUndo::Record(new CUndoAddObjectsToPrefab(m_pPrefabObject, objects)); } for (int i = 0; i < objects.size(); i++) { m_pPrefabObject->AddMember(objects[i]); } ReloadObjects(); } } ////////////////////////////////////////////////////////////////////////// bool CPrefabPanel::OnPickFilter(CBaseObject* picked) { if (picked->CheckFlags(OBJFLAG_PREFAB) || picked == m_pPrefabObject) { return false; } return true; } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnCancelPick() { } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedCloseAll() { std::vector prefabObjects; GetAllPrefabObjects(prefabObjects); if (GetIEditor()->GetPrefabManager()->ClosePrefabs(prefabObjects, "Close all prefab objects")) { ReloadObjects(); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::OnBnClickedOpenAll() { std::vector prefabObjects; GetAllPrefabObjects(prefabObjects); if (GetIEditor()->GetPrefabManager()->OpenPrefabs(prefabObjects, "Open all prefab objects")) { ReloadObjects(); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::GetAllPrefabObjects(std::vector& outPrefabObjects) { std::vector prefabObjects; GetIEditor()->GetObjectManager()->FindObjectsOfType(&CPrefabObject::staticMetaObject, prefabObjects); if (prefabObjects.empty()) { return; } bool bOpenMoreThanOne = false; for (int i = 0, iCount(prefabObjects.size()); i < iCount; ++i) { CPrefabObject* pPrefabObj = (CPrefabObject*)prefabObjects[i]; if (pPrefabObj == NULL) { continue; } if (m_pPrefabObject && pPrefabObj->GetPrefab() != m_pPrefabObject->GetPrefab()) { continue; } outPrefabObjects.push_back(pPrefabObj); } } ////////////////////////////////////////////////////////////////////////// void CPrefabPanel::SetPrefabPivotModeOnCurrentEditTool(bool pivotModeOn) { if (m_pPrefabObject) { m_pPrefabObject->SetChangePivotMode(pivotModeOn); } } ////////////////////////////////////////////////////////////////////////// int CPrefabPanel::GetSelectedObjects(std::vector& selectedObjects) { selectedObjects.clear(); for (int i = 0; i < m_childObjects.size(); ++i) { if (m_childObjects[i].bSelected) { selectedObjects.push_back(m_childObjects[i].pObject); } } return selectedObjects.size(); }