diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index dfdc8496..993b49d1 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -129,7 +129,8 @@ HEADERS += \ Undo/CSelectAllCommand.h \ Undo/CInvertSelectionCommand.h \ WorldEditor/CPoiMapEditDialog.h \ - WorldEditor/CPoiMapModel.h + WorldEditor/CPoiMapModel.h \ + WorldEditor/CPoiListDialog.h # Source Files SOURCES += \ diff --git a/src/Editor/WorldEditor/CPoiListDialog.h b/src/Editor/WorldEditor/CPoiListDialog.h new file mode 100644 index 00000000..92e2a809 --- /dev/null +++ b/src/Editor/WorldEditor/CPoiListDialog.h @@ -0,0 +1,152 @@ +#ifndef CPOILISTDIALOG_H +#define CPOILISTDIALOG_H + +#include "CPoiMapModel.h" +#include "Editor/UICommon.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class CPoiListModel : public QAbstractListModel +{ + Q_OBJECT + + CScriptTemplate *mpPoiTemplate; + QList mObjList; + +public: + CPoiListModel(CScriptTemplate *pPoiTemplate, CPoiMapModel *pMapModel, CScene *pScene, QWidget *pParent = 0) + : QAbstractListModel(pParent) + , mpPoiTemplate(pPoiTemplate) + { + const std::list& rkObjList = mpPoiTemplate->ObjectList(); + + for (auto it = rkObjList.begin(); it != rkObjList.end(); it++) + { + CScriptNode *pNode = pScene->NodeForObject(*it); + + if (!pMapModel->IsPoiTracked(pNode)) + mObjList << pNode; + } + } + + int rowCount(const QModelIndex&) const + { + return mObjList.size(); + } + + QVariant data(const QModelIndex& rkIndex, int Role) const + { + if (!rkIndex.isValid()) return QVariant::Invalid; + + if (Role == Qt::DisplayRole) + return TO_QSTRING(mObjList[rkIndex.row()]->Object()->InstanceName()); + + if (Role == Qt::DecorationRole) + { + CScriptNode *pNode = mObjList[rkIndex.row()]; + CScan *pScan = static_cast(pNode->Extra())->GetScan(); + bool IsImportant = (pScan ? pScan->IsImportant() : false); + + if (IsImportant) + return QIcon(":/icons/POI Important.png"); + else + return QIcon(":/icons/POI Normal.png"); + } + + return QVariant::Invalid; + } + + CScriptNode* PoiForIndex(const QModelIndex& rkIndex) const + { + return mObjList[rkIndex.row()]; + } +}; + +class CPoiListDialog : public QDialog +{ + Q_OBJECT + + CPoiListModel mSourceModel; + QSortFilterProxyModel mModel; + QList mSelection; + + QListView *mpListView; + QDialogButtonBox *mpButtonBox; + +public: + CPoiListDialog(CScriptTemplate *pPoiTemplate, CPoiMapModel *pMapModel, CScene *pScene, QWidget *pParent = 0) + : QDialog(pParent) + , mSourceModel(pPoiTemplate, pMapModel, pScene) + { + mpListView = new QListView(this); + mpButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + + QHBoxLayout *pButtonLayout = new QHBoxLayout(); + pButtonLayout->addStretch(); + pButtonLayout->addWidget(mpButtonBox); + + QVBoxLayout *pLayout = new QVBoxLayout(); + pLayout->addWidget(mpListView); + pLayout->addLayout(pButtonLayout); + setLayout(pLayout); + + mModel.setSourceModel(&mSourceModel); + mpListView->setModel(&mModel); + mModel.sort(0); + + setWindowTitle("Add POIs"); + mpListView->setEditTriggers(QListView::NoEditTriggers); + mpListView->setSelectionMode(QListView::ExtendedSelection); + mpListView->setVerticalScrollMode(QListView::ScrollPerPixel); + + connect(mpListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickItem(QModelIndex))); + connect(mpButtonBox, SIGNAL(accepted()), this, SLOT(OnOkClicked())); + connect(mpButtonBox, SIGNAL(rejected()), this, SLOT(OnCancelClicked())); + } + + const QList& Selection() const + { + return mSelection; + } + +public slots: + void OnDoubleClickItem(QModelIndex Index) + { + QModelIndex SourceIndex = mModel.mapToSource(Index); + mSelection.clear(); + mSelection << mSourceModel.PoiForIndex(SourceIndex); + close(); + } + + void OnOkClicked() + { + QModelIndexList SelectedIndices = mpListView->selectionModel()->selectedRows(); + + foreach (const QModelIndex& rkIndex, SelectedIndices) + { + QModelIndex SourceIndex = mModel.mapToSource(rkIndex); + mSelection << mSourceModel.PoiForIndex(SourceIndex); + } + + close(); + } + + void OnCancelClicked() + { + close(); + } +}; + +#endif // CPOILISTDIALOG_H diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.cpp b/src/Editor/WorldEditor/CPoiMapEditDialog.cpp index 72a5e963..135acf50 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.cpp +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -21,7 +22,6 @@ CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent) , mSourceModel(pEditor, this) , mHighlightMode(eHighlightSelected) , mPickType(eNotPicking) - , mPickTool(eNormalTool) , mpHoverModel(nullptr) { mModel.setSourceModel(&mSourceModel); @@ -43,18 +43,18 @@ CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent) connect(ui->ListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnSelectionChanged(QItemSelection,QItemSelection))); connect(ui->ListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnItemDoubleClick(QModelIndex))); - connect(ui->AddMeshButton, SIGNAL(clicked()), this, SLOT(PickButtonClicked())); - connect(ui->RemoveMeshButton, SIGNAL(clicked()), this, SLOT(PickButtonClicked())); - connect(ui->ToolComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnToolComboBoxChanged(int))); + connect(ui->MapMeshesButton, SIGNAL(clicked()), this, SLOT(OnPickButtonClicked())); + connect(ui->UnmapMeshesButton, SIGNAL(clicked()), this, SLOT(OnPickButtonClicked())); connect(ui->UnmapAllButton, SIGNAL(clicked()), this, SLOT(OnUnmapAllPressed())); + connect(ui->AddPoiFromViewportButton, SIGNAL(clicked()), this, SLOT(OnPickButtonClicked())); + connect(ui->AddPoiFromInstanceListButton, SIGNAL(clicked()), this, SLOT(OnInstanceListButtonClicked())); + connect(ui->RemovePoiButton, SIGNAL(clicked()), this, SLOT(OnRemovePoiButtonClicked())); connect(ui->ButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close())); connect(ui->ButtonBox->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(Save())); } CPoiMapEditDialog::~CPoiMapEditDialog() { - delete ui; - // Clear model tints if (mHighlightMode != eHighlightNone) SetHighlightNone(); @@ -62,6 +62,8 @@ CPoiMapEditDialog::~CPoiMapEditDialog() // Stop picking if (mPickType != eNotPicking) StopPicking(); + + delete ui; } void CPoiMapEditDialog::closeEvent(QCloseEvent* /*pEvent*/) @@ -93,7 +95,7 @@ void CPoiMapEditDialog::UnhighlightPoiModels(const QModelIndex& rkIndex) const QList& rkModels = mSourceModel.GetPoiMeshList(SourceIndex); for (int iMdl = 0; iMdl < rkModels.size(); iMdl++) - rkModels[iMdl]->SetScanOverlayEnabled(false); + RevertModelOverlay(rkModels[iMdl]); } void CPoiMapEditDialog::HighlightModel(const QModelIndex& rkIndex, CModelNode *pNode) @@ -114,18 +116,28 @@ void CPoiMapEditDialog::RevertModelOverlay(CModelNode *pModel) { if (mHighlightMode == eHighlightAll) { - for (int iRow = 0; iRow < mSourceModel.rowCount(QModelIndex()); iRow++) + // Prioritize the selected POI over others. + QModelIndex Selected = GetSelectedRow(); + + if (mSourceModel.IsModelMapped(Selected, pModel)) + HighlightModel(Selected, pModel); + + // If it's not mapped to the selected POI, then check whether it's mapped to any others. + else { - QModelIndex Index = mSourceModel.index(iRow, 0); - - if (mSourceModel.IsModelMapped(Index, pModel)) + for (int iRow = 0; iRow < mSourceModel.rowCount(QModelIndex()); iRow++) { - HighlightModel(Index, pModel); - return; - } - } + QModelIndex Index = mSourceModel.index(iRow, 0); - UnhighlightModel(pModel); + if (mSourceModel.IsModelMapped(Index, pModel)) + { + HighlightModel(Index, pModel); + return; + } + } + + UnhighlightModel(pModel); + } } else if (mHighlightMode == eHighlightSelected) @@ -187,6 +199,8 @@ void CPoiMapEditDialog::Save() void CPoiMapEditDialog::SetHighlightSelected() { + mHighlightMode = eHighlightSelected; + const QItemSelection kSelection = ui->ListView->selectionModel()->selection(); QList SelectedIndices; QList UnselectedIndices; @@ -205,12 +219,12 @@ void CPoiMapEditDialog::SetHighlightSelected() UnhighlightPoiModels(UnselectedIndices[iIdx]); for (int iIdx = 0; iIdx < SelectedIndices.size(); iIdx++) HighlightPoiModels(SelectedIndices[iIdx]); - - mHighlightMode = eHighlightSelected; } void CPoiMapEditDialog::SetHighlightAll() { + mHighlightMode = eHighlightAll; + for (int iRow = 0; iRow < mModel.rowCount(QModelIndex()); iRow++) HighlightPoiModels(mModel.index(iRow, 0)); @@ -220,19 +234,17 @@ void CPoiMapEditDialog::SetHighlightAll() if (mpHoverModel) HighlightModel(GetSelectedRow(), mpHoverModel); - - mHighlightMode = eHighlightAll; } void CPoiMapEditDialog::SetHighlightNone() { + mHighlightMode = eHighlightNone; + for (int iRow = 0; iRow < mModel.rowCount(QModelIndex()); iRow++) UnhighlightPoiModels(mModel.index(iRow, 0)); if (mpHoverModel) UnhighlightModel(mpHoverModel); - - mHighlightMode = eHighlightNone; } void CPoiMapEditDialog::OnSelectionChanged(const QItemSelection& rkSelected, const QItemSelection& rkDeselected) @@ -260,14 +272,6 @@ void CPoiMapEditDialog::OnItemDoubleClick(QModelIndex Index) mpEditor->ClearAndSelectNode(pPOI); } -void CPoiMapEditDialog::OnToolComboBoxChanged(int NewIndex) -{ - if (NewIndex == 0) - mPickTool = eNormalTool; - else - mPickTool = eSprayCanTool; -} - void CPoiMapEditDialog::OnUnmapAllPressed() { QModelIndex Index = GetSelectedRow(); @@ -280,39 +284,55 @@ void CPoiMapEditDialog::OnUnmapAllPressed() } } -void CPoiMapEditDialog::PickButtonClicked() +void CPoiMapEditDialog::OnPickButtonClicked() { QPushButton *pButton = qobject_cast(sender()); - if (!pButton->isChecked()) - mpEditor->ExitPickMode(); + if (pButton == ui->AddPoiFromViewportButton) + { + mpEditor->EnterPickMode(eScriptNode, true, false, false); + connect(mpEditor, SIGNAL(PickModeExited()), this, SLOT(StopPicking())); + connect(mpEditor, SIGNAL(PickModeClick(SRayIntersection,QMouseEvent*)), this, SLOT(OnPoiPicked(SRayIntersection,QMouseEvent*))); + + pButton->setChecked(true); + ui->MapMeshesButton->setChecked(false); + ui->UnmapMeshesButton->setChecked(false); + + mPickType = eAddPOIs; + } else { - mpEditor->EnterPickMode(eModelNode, false, false, true); - connect(mpEditor, SIGNAL(PickModeExited()), this, SLOT(StopPicking())); - connect(mpEditor, SIGNAL(PickModeClick(SRayIntersection,QMouseEvent*)), this, SLOT(OnNodePicked(SRayIntersection,QMouseEvent*))); - connect(mpEditor, SIGNAL(PickModeHoverChanged(SRayIntersection,QMouseEvent*)), this, SLOT(OnNodeHover(SRayIntersection,QMouseEvent*))); - pButton->setChecked(true); + if (!pButton->isChecked()) + mpEditor->ExitPickMode(); - if (pButton == ui->AddMeshButton) + else { - mPickType = eAddMeshes; - ui->RemoveMeshButton->setChecked(false); - } + mpEditor->EnterPickMode(eModelNode, false, false, true); + connect(mpEditor, SIGNAL(PickModeExited()), this, SLOT(StopPicking())); + connect(mpEditor, SIGNAL(PickModeHoverChanged(SRayIntersection,QMouseEvent*)), this, SLOT(OnModelHover(SRayIntersection,QMouseEvent*))); + pButton->setChecked(true); - else if (pButton == ui->RemoveMeshButton) - { - mPickType = eRemoveMeshes; - ui->AddMeshButton->setChecked(false); + if (pButton == ui->MapMeshesButton) + { + mPickType = eAddMeshes; + ui->UnmapMeshesButton->setChecked(false); + } + + else if (pButton == ui->UnmapMeshesButton) + { + mPickType = eRemoveMeshes; + ui->MapMeshesButton->setChecked(false); + } } } } void CPoiMapEditDialog::StopPicking() { - ui->AddMeshButton->setChecked(false); - ui->RemoveMeshButton->setChecked(false); + ui->MapMeshesButton->setChecked(false); + ui->UnmapMeshesButton->setChecked(false); + ui->AddPoiFromViewportButton->setChecked(false); mPickType = eNotPicking; RevertModelOverlay(mpHoverModel); @@ -321,7 +341,50 @@ void CPoiMapEditDialog::StopPicking() disconnect(mpEditor, 0, this, 0); } -void CPoiMapEditDialog::OnNodePicked(const SRayIntersection& rkRayIntersect, QMouseEvent* pEvent) +void CPoiMapEditDialog::OnInstanceListButtonClicked() +{ + EGame Game = mpEditor->ActiveArea()->Version(); + CScriptTemplate *pPoiTemplate = CMasterTemplate::GetMasterForGame(Game)->TemplateByID("POIN"); + + CPoiListDialog Dialog(pPoiTemplate, &mSourceModel, mpEditor->Scene(), this); + Dialog.exec(); + + const QList& rkSelection = Dialog.Selection(); + + if (!rkSelection.empty()) + { + foreach (CScriptNode *pNode, rkSelection) + mSourceModel.AddPOI(pNode); + + mModel.sort(0); + } +} + +void CPoiMapEditDialog::OnRemovePoiButtonClicked() +{ + if (ui->ListView->selectionModel()->hasSelection()) + { + QModelIndex Index = ui->ListView->selectionModel()->selectedRows().front(); + UnhighlightPoiModels(Index); + Index = mModel.mapToSource(Index); + mSourceModel.RemovePOI(Index); + } +} + +void CPoiMapEditDialog::OnPoiPicked(const SRayIntersection& rkIntersect, QMouseEvent *pEvent) +{ + CScriptNode *pPOI = static_cast(rkIntersect.pNode); + if (pPOI->Object()->ObjectTypeID() != CFourCC("POIN").ToLong()) return; + + mSourceModel.AddPOI(pPOI); + mModel.sort(0); + + // Exit pick mode unless the user is holding the Ctrl key + if (!(pEvent->modifiers() & Qt::ControlModifier)) + mpEditor->ExitPickMode(); +} + +void CPoiMapEditDialog::OnModelPicked(const SRayIntersection& rkRayIntersect, QMouseEvent* pEvent) { if (!rkRayIntersect.pNode) return; @@ -363,7 +426,7 @@ void CPoiMapEditDialog::OnNodePicked(const SRayIntersection& rkRayIntersect, QMo } } -void CPoiMapEditDialog::OnNodeHover(const SRayIntersection& rkIntersect, QMouseEvent *pEvent) +void CPoiMapEditDialog::OnModelHover(const SRayIntersection& rkIntersect, QMouseEvent *pEvent) { // Restore old hover model to correct overlay color, and set new hover model if (mpHoverModel) @@ -371,9 +434,9 @@ void CPoiMapEditDialog::OnNodeHover(const SRayIntersection& rkIntersect, QMouseE mpHoverModel = static_cast(rkIntersect.pNode); - // If we're using the spray can and the mouse is pressed, treat this as a click. - if (mPickTool == eSprayCanTool && (pEvent->buttons() & Qt::LeftButton)) - OnNodePicked(rkIntersect, pEvent); + // If the left mouse button is pressed, treat this as a click. + if (pEvent->buttons() & Qt::LeftButton) + OnModelPicked(rkIntersect, pEvent); // Otherwise, process as a mouseover else diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.h b/src/Editor/WorldEditor/CPoiMapEditDialog.h index d5cf6aa8..926e1362 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.h +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.h @@ -1,9 +1,11 @@ #ifndef CPOIMAPEDITDIALOG_H #define CPOIMAPEDITDIALOG_H -#include #include "CPoiMapModel.h" +#include "CPoiListDialog.h" + #include +#include #include namespace Ui { @@ -27,6 +29,7 @@ class CPoiMapEditDialog : public QMainWindow QSortFilterProxyModel mModel; EHighlightMode mHighlightMode; + // Viewport Picking enum EPickType { eNotPicking, @@ -35,12 +38,6 @@ class CPoiMapEditDialog : public QMainWindow eAddPOIs } mPickType; - enum EPickTool - { - eNormalTool, - eSprayCanTool - } mPickTool; - CModelNode *mpHoverModel; static const CColor skNormalColor; @@ -67,13 +64,15 @@ public slots: void SetHighlightNone(); void OnSelectionChanged(const QItemSelection& rkSelected, const QItemSelection& rkDeselected); void OnItemDoubleClick(QModelIndex Index); - void OnToolComboBoxChanged(int NewIndex); void OnUnmapAllPressed(); - void PickButtonClicked(); + void OnPickButtonClicked(); void StopPicking(); - void OnNodePicked(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); - void OnNodeHover(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); + void OnInstanceListButtonClicked(); + void OnRemovePoiButtonClicked(); + void OnPoiPicked(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); + void OnModelPicked(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); + void OnModelHover(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); signals: void Closed(); diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.ui b/src/Editor/WorldEditor/CPoiMapEditDialog.ui index a17d31a5..e37706ca 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.ui +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.ui @@ -6,8 +6,8 @@ 0 0 - 274 - 308 + 263 + 306 @@ -16,11 +16,11 @@ - + - + - + Map @@ -32,9 +32,9 @@ - + - + Unmap @@ -52,26 +52,10 @@ - - - - - 85 - 0 - - - - - Normal Tool - - - - - Spray Can - - - - + + + + @@ -85,6 +69,51 @@ + + + + Add from Viewport + + + + + + + :/icons/SelectMode.png:/icons/SelectMode.png + + + true + + + + + + + Add from Instance List + + + + + + + :/icons/Instances.png:/icons/Instances.png + + + + + + + Remove + + + + + + + :/icons/Minus v2.png:/icons/Minus v2.png + + + diff --git a/src/Editor/WorldEditor/CPoiMapModel.cpp b/src/Editor/WorldEditor/CPoiMapModel.cpp index 1d9d8123..d9d555e8 100644 --- a/src/Editor/WorldEditor/CPoiMapModel.cpp +++ b/src/Editor/WorldEditor/CPoiMapModel.cpp @@ -107,9 +107,14 @@ void CPoiMapModel::AddPOI(CScriptNode *pPOI) { if (!mModelMap.contains(pPOI)) { + int NewIndex = mpPoiToWorld->NumMappedPOIs(); + beginInsertRows(QModelIndex(), NewIndex, NewIndex); + QList *pList = new QList; mModelMap[pPOI] = pList; mpPoiToWorld->AddPoi(pPOI->Object()->InstanceID()); + + endInsertRows(); } } @@ -127,6 +132,7 @@ void CPoiMapModel::AddMapping(const QModelIndex& rkIndex, CModelNode *pNode) void CPoiMapModel::RemovePOI(const QModelIndex& rkIndex) { + beginRemoveRows(QModelIndex(), rkIndex.row(), rkIndex.row()); CScriptNode *pPOI = PoiNodePointer(rkIndex); if (mModelMap.contains(pPOI)) @@ -136,6 +142,7 @@ void CPoiMapModel::RemovePOI(const QModelIndex& rkIndex) } mpPoiToWorld->RemovePoi(pPOI->Object()->InstanceID()); + endRemoveRows(); } void CPoiMapModel::RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode) @@ -152,6 +159,11 @@ void CPoiMapModel::RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode) mpPoiToWorld->RemovePoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID()); } +bool CPoiMapModel::IsPoiTracked(CScriptNode *pPOI) const +{ + return mModelMap.contains(pPOI); +} + bool CPoiMapModel::IsModelMapped(const QModelIndex& rkIndex, CModelNode *pNode) const { if (!pNode) return false; diff --git a/src/Editor/WorldEditor/CPoiMapModel.h b/src/Editor/WorldEditor/CPoiMapModel.h index 2fe3427a..45fa38dd 100644 --- a/src/Editor/WorldEditor/CPoiMapModel.h +++ b/src/Editor/WorldEditor/CPoiMapModel.h @@ -31,6 +31,7 @@ public: void AddMapping(const QModelIndex& rkIndex, CModelNode *pNode); void RemovePOI(const QModelIndex& rkIndex); void RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode); + bool IsPoiTracked(CScriptNode *pPOI) const; bool IsModelMapped(const QModelIndex& rkIndex, CModelNode *pNode) const; CScriptNode* PoiNodePointer(const QModelIndex& rkIndex) const;