mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-07-19 11:36:06 +00:00
376 lines
13 KiB
C++
376 lines
13 KiB
C++
#include "CGeneratePropertyNamesDialog.h"
|
|
#include "ui_CGeneratePropertyNamesDialog.h"
|
|
|
|
#include "Editor/Widgets/CCheckableTreeWidgetItem.h"
|
|
#include "UICommon.h"
|
|
#include <Core/Resource/Script/NGameList.h>
|
|
#include <Core/Resource/Script/NPropertyMap.h>
|
|
#include <QtConcurrent/QtConcurrent>
|
|
#include <iterator>
|
|
|
|
CGeneratePropertyNamesDialog::CGeneratePropertyNamesDialog(QWidget* pParent)
|
|
: QDialog(pParent)
|
|
, mpUI(std::make_unique<Ui::CGeneratePropertyNamesDialog>())
|
|
, mFutureWatcher(this)
|
|
{
|
|
mpUI->setupUi(this);
|
|
mNotifier.SetProgressBar( mpUI->ProgressBar );
|
|
|
|
connect(mpUI->AddSuffixButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::AddSuffix);
|
|
connect(mpUI->RemoveSuffixButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::DeleteSuffix);
|
|
connect(mpUI->ClearIdPoolButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::ClearIdPool);
|
|
connect(mpUI->StartButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::StartGeneration);
|
|
connect(mpUI->CancelButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::CancelGeneration);
|
|
connect(mpUI->CheckAllButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::CheckAll);
|
|
connect(mpUI->UncheckAllButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::UncheckAll);
|
|
connect(mpUI->ApplyButton, &QPushButton::pressed, this, &CGeneratePropertyNamesDialog::ApplyChanges);
|
|
connect(mpUI->OutputTreeWidget, &CCheckableTreeWidget::CheckStateChanged,
|
|
this, &CGeneratePropertyNamesDialog::OnTreeItemChecked);
|
|
connect(mpUI->OutputTreeWidget, &CCheckableTreeWidget::itemDoubleClicked,
|
|
this, &CGeneratePropertyNamesDialog::OnTreeItemDoubleClicked);
|
|
|
|
// Configure default tree view split sizes
|
|
// I don't know why it needs to be multiplied by 1.5, it just does
|
|
int TreeWidth = mpUI->OutputTreeWidget->width();
|
|
mpUI->OutputTreeWidget->setColumnWidth(0, TreeWidth * 1.5);
|
|
mpUI->OutputTreeWidget->setHeaderHidden(false);
|
|
|
|
// Allow the generator to initialize in the background while the user is getting set up
|
|
QtConcurrent::run(&mGenerator, &CPropertyNameGenerator::Warmup);
|
|
}
|
|
|
|
CGeneratePropertyNamesDialog::~CGeneratePropertyNamesDialog() = default;
|
|
|
|
/** Add a property to the ID pool */
|
|
void CGeneratePropertyNamesDialog::AddToIDPool(IProperty* pProperty)
|
|
{
|
|
if (!pProperty->UsesNameMap())
|
|
{
|
|
errorf("Failed to add property %s to the generator ID pool because it doesn't use the name map", *pProperty->IDString(false));
|
|
return;
|
|
}
|
|
|
|
uint32 ID = pProperty->ID();
|
|
const char* pkTypeName = pProperty->HashableTypeName();
|
|
mIdPairs << SPropertyIdTypePair { ID, pkTypeName };
|
|
|
|
const QString ItemText = tr("%1 [%2]").arg(*TString::HexString(pProperty->ID(), 8, false)).arg(pkTypeName);
|
|
mpUI->IdPoolList->addItem( ItemText );
|
|
|
|
// We probably don't want to call UpdateUI every single time we add a property, but
|
|
// we do need to call it somewhere to make sure the ID list shows up on the UI...
|
|
if (mpUI->IdPoolGroupBox->isHidden())
|
|
{
|
|
UpdateUI();
|
|
}
|
|
}
|
|
|
|
/** Populate the ID pool with the children of the given property */
|
|
void CGeneratePropertyNamesDialog::AddChildrenToIDPool(IProperty* pProperty, bool Recursive)
|
|
{
|
|
for (size_t ChildIdx = 0; ChildIdx < pProperty->NumChildren(); ChildIdx++)
|
|
{
|
|
IProperty* pChild = pProperty->ChildByIndex(ChildIdx);
|
|
|
|
// Skip children that already have valid property names
|
|
if (!pChild->HasAccurateName() && pChild->UsesNameMap())
|
|
{
|
|
AddToIDPool(pChild);
|
|
}
|
|
|
|
if (Recursive)
|
|
{
|
|
AddChildrenToIDPool(pChild, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Show event override */
|
|
void CGeneratePropertyNamesDialog::showEvent(QShowEvent*)
|
|
{
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Close event override */
|
|
void CGeneratePropertyNamesDialog::closeEvent(QCloseEvent*)
|
|
{
|
|
if (mRunningNameGeneration)
|
|
{
|
|
CancelGeneration();
|
|
}
|
|
ClearIdPool();
|
|
}
|
|
|
|
/** Add an item to the suffix list */
|
|
void CGeneratePropertyNamesDialog::AddSuffix()
|
|
{
|
|
auto* pNewItem = new QListWidgetItem(tr("New Suffix"), mpUI->TypeSuffixesListWidget);
|
|
pNewItem->setFlags(Qt::ItemIsEditable |
|
|
Qt::ItemIsEnabled |
|
|
Qt::ItemIsSelectable);
|
|
mpUI->TypeSuffixesListWidget->setCurrentItem(pNewItem, QItemSelectionModel::ClearAndSelect);
|
|
mpUI->TypeSuffixesListWidget->editItem(pNewItem);
|
|
}
|
|
|
|
/** Deletes an item from the suffix list */
|
|
void CGeneratePropertyNamesDialog::DeleteSuffix()
|
|
{
|
|
if (mpUI->TypeSuffixesListWidget->selectedItems().size() > 0)
|
|
{
|
|
int Row = mpUI->TypeSuffixesListWidget->currentRow();
|
|
delete mpUI->TypeSuffixesListWidget->takeItem(Row);
|
|
}
|
|
}
|
|
|
|
/** Clear the ID pool */
|
|
void CGeneratePropertyNamesDialog::ClearIdPool()
|
|
{
|
|
mIdPairs.clear();
|
|
mpUI->IdPoolList->clear();
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Start name generation */
|
|
void CGeneratePropertyNamesDialog::StartGeneration()
|
|
{
|
|
ASSERT(!mRunningNameGeneration);
|
|
mRunningNameGeneration = true;
|
|
mCanceledNameGeneration = false;
|
|
mTaskOutput.clear();
|
|
mCheckedItems.clear();
|
|
mpUI->OutputTreeWidget->clear();
|
|
|
|
// Load all templates so we can match as many properties as possible
|
|
NGameList::LoadAllGameTemplates();
|
|
|
|
// Configure the generator
|
|
SPropertyNameGenerationParameters Params;
|
|
|
|
for (int RowIdx = 0; RowIdx < mpUI->TypeSuffixesListWidget->count(); RowIdx++)
|
|
{
|
|
QString ItemText = mpUI->TypeSuffixesListWidget->item(RowIdx)->text();
|
|
Params.TypeNames.push_back( TO_TSTRING(ItemText) );
|
|
}
|
|
|
|
Params.MaxWords = mpUI->NumWordsSpinBox->value();
|
|
Params.Prefix = TO_TSTRING( mpUI->PrefixLineEdit->text() );
|
|
Params.Suffix = TO_TSTRING( mpUI->SuffixLineEdit->text() );
|
|
Params.Casing = mpUI->CasingComboBox->currentEnum();
|
|
Params.ValidIdPairs = mIdPairs.toStdVector();
|
|
Params.ExcludeAccuratelyNamedProperties = mpUI->UnnamedOnlyCheckBox->isChecked();
|
|
Params.TestIntsAsChoices = mpUI->TestIntsAsChoicesCheckBox->isChecked();
|
|
Params.PrintToLog = mpUI->LogOutputCheckBox->isChecked();
|
|
|
|
// Run the task and configure ourselves so we can update correctly
|
|
connect(&mFutureWatcher, &QFutureWatcher<void>::finished, this, &CGeneratePropertyNamesDialog::GenerationComplete);
|
|
mFuture = QtConcurrent::run(&mGenerator, &CPropertyNameGenerator::Generate, Params, &mNotifier);
|
|
mFutureWatcher.setFuture(mFuture);
|
|
|
|
mUpdateTimer.start(500);
|
|
connect( &mUpdateTimer, &QTimer::timeout, this, &CGeneratePropertyNamesDialog::CheckForNewResults);
|
|
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Cancel name generation */
|
|
void CGeneratePropertyNamesDialog::CancelGeneration()
|
|
{
|
|
mNotifier.SetCanceled(true);
|
|
mCanceledNameGeneration = true;
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Called when name generation is complete */
|
|
void CGeneratePropertyNamesDialog::GenerationComplete()
|
|
{
|
|
mRunningNameGeneration = false;
|
|
mCanceledNameGeneration = false;
|
|
mNotifier.SetCanceled(false);
|
|
mUpdateTimer.stop();
|
|
|
|
mTaskOutput = QList<SGeneratedPropertyName>::fromStdList(
|
|
mGenerator.GetOutput()
|
|
);
|
|
|
|
mpUI->ProgressBar->setValue( mpUI->ProgressBar->maximum() );
|
|
|
|
disconnect( &mFutureWatcher, 0, this, 0 );
|
|
disconnect( &mUpdateTimer, 0, this, 0 );
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Called when an item in the output tree has been checked or unchecked */
|
|
void CGeneratePropertyNamesDialog::OnTreeItemChecked(QTreeWidgetItem* pItem)
|
|
{
|
|
if (pItem->checkState(0) == Qt::Checked)
|
|
mCheckedItems.append(pItem);
|
|
else
|
|
mCheckedItems.removeOne(pItem);
|
|
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Called when an item in the output tree has been double clicked */
|
|
void CGeneratePropertyNamesDialog::OnTreeItemDoubleClicked(QTreeWidgetItem* pItem)
|
|
{
|
|
// Check whether this is an XML path
|
|
if (pItem->parent() != nullptr)
|
|
{
|
|
QString Text = pItem->text(0);
|
|
|
|
if (Text.endsWith(".xml"))
|
|
{
|
|
TString TStrText = TO_TSTRING(Text);
|
|
TString DirPath = gDataDir + "templates/" + TStrText.GetFileDirectory();
|
|
TString AbsPath = FileUtil::MakeAbsolute(DirPath) + TStrText.GetFileName();
|
|
UICommon::OpenInExternalApplication( TO_QSTRING(AbsPath) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Check all items in the output tree */
|
|
void CGeneratePropertyNamesDialog::CheckAll()
|
|
{
|
|
mpUI->OutputTreeWidget->blockSignals(true);
|
|
mCheckedItems.clear();
|
|
mCheckedItems.reserve( mpUI->OutputTreeWidget->topLevelItemCount() );
|
|
|
|
for (int RowIdx = 0; RowIdx < mpUI->OutputTreeWidget->topLevelItemCount(); RowIdx++)
|
|
{
|
|
QTreeWidgetItem* pItem = mpUI->OutputTreeWidget->topLevelItem(RowIdx);
|
|
pItem->setCheckState( 0, Qt::Checked );
|
|
mCheckedItems << pItem;
|
|
}
|
|
|
|
mpUI->OutputTreeWidget->blockSignals(false);
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Uncheck all items in the output tree */
|
|
void CGeneratePropertyNamesDialog::UncheckAll()
|
|
{
|
|
mpUI->OutputGroupBox->blockSignals(true);
|
|
|
|
for (int RowIdx = 0; RowIdx < mpUI->OutputTreeWidget->topLevelItemCount(); RowIdx++)
|
|
{
|
|
QTreeWidgetItem* pItem = mpUI->OutputTreeWidget->topLevelItem(RowIdx);
|
|
pItem->setCheckState( 0, Qt::Unchecked );
|
|
}
|
|
|
|
mCheckedItems.clear();
|
|
mpUI->OutputTreeWidget->blockSignals(false);
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Apply generated names on selected items */
|
|
void CGeneratePropertyNamesDialog::ApplyChanges()
|
|
{
|
|
// make sure the user really wants to do this
|
|
const QString WarningText =
|
|
tr("Are you sure you want to rename %1 %2? This operation cannot be undone.")
|
|
.arg(mCheckedItems.size())
|
|
.arg(mCheckedItems.size() == 1 ? tr("property") : tr("properties"));
|
|
|
|
const bool ReallyRename = UICommon::YesNoQuestion(this, tr("Warning"), WarningText);
|
|
|
|
if (!ReallyRename)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Perform rename operation
|
|
for (int ItemIdx = 0; ItemIdx < mCheckedItems.size(); ItemIdx++)
|
|
{
|
|
QTreeWidgetItem* pItem = mCheckedItems[ItemIdx];
|
|
uint32 ID = TO_TSTRING( pItem->text(2) ).ToInt32(16);
|
|
TString Type = TO_TSTRING( pItem->text(1) );
|
|
TString NewName = TO_TSTRING( pItem->text(0) );
|
|
|
|
NPropertyMap::SetPropertyName( ID, *Type, *NewName );
|
|
pItem->setText( 3, TO_QSTRING(NewName) );
|
|
}
|
|
|
|
NPropertyMap::SaveMap();
|
|
}
|
|
|
|
/** Check progress on name generation task and display results on the UI */
|
|
void CGeneratePropertyNamesDialog::CheckForNewResults()
|
|
{
|
|
const std::list<SGeneratedPropertyName>& rkOutput = mGenerator.GetOutput();
|
|
|
|
QTreeWidget* pTreeWidget = mpUI->OutputTreeWidget;
|
|
int CurItemCount = pTreeWidget->topLevelItemCount();
|
|
|
|
// Add new items to the tree
|
|
if (rkOutput.size() > CurItemCount)
|
|
{
|
|
auto Iter = rkOutput.cbegin();
|
|
auto End = rkOutput.cend();
|
|
std::advance(Iter, CurItemCount);
|
|
|
|
for (; Iter != End; Iter++)
|
|
{
|
|
const SGeneratedPropertyName& rkName = *Iter;
|
|
|
|
// Add an item to the tree for this name
|
|
QStringList ColumnText;
|
|
ColumnText << TO_QSTRING(rkName.Name)
|
|
<< TO_QSTRING(rkName.Type)
|
|
<< TO_QSTRING(TString::HexString(rkName.ID))
|
|
<< TO_QSTRING(NPropertyMap::GetPropertyName(rkName.ID, *rkName.Type));
|
|
|
|
QTreeWidgetItem* pItem = new CCheckableTreeWidgetItem(pTreeWidget, ColumnText);
|
|
pItem->setFlags(Qt::ItemIsEnabled |
|
|
Qt::ItemIsSelectable |
|
|
Qt::ItemIsUserCheckable);
|
|
pItem->setCheckState(0, Qt::Unchecked);
|
|
|
|
// Add children items
|
|
for (auto Iter = rkName.XmlList.begin(); Iter != rkName.XmlList.end(); Iter++)
|
|
{
|
|
QString XmlName = TO_QSTRING( *Iter );
|
|
ColumnText.clear();
|
|
ColumnText << XmlName;
|
|
|
|
QTreeWidgetItem* pChild = new QTreeWidgetItem(pItem, ColumnText);
|
|
pChild->setFlags(Qt::ItemIsEnabled);
|
|
pChild->setFirstColumnSpanned(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateUI();
|
|
}
|
|
|
|
/** Updates the enabled status of various widgets */
|
|
void CGeneratePropertyNamesDialog::UpdateUI()
|
|
{
|
|
mpUI->SettingsGroupBox->setEnabled( !mRunningNameGeneration );
|
|
mpUI->TypeSuffixesGroupBox->setEnabled( !mRunningNameGeneration );
|
|
mpUI->TypeSuffixesGroupBox->setHidden( !mIdPairs.isEmpty() );
|
|
mpUI->IdPoolGroupBox->setEnabled( !mRunningNameGeneration );
|
|
mpUI->IdPoolGroupBox->setHidden( mIdPairs.isEmpty() );
|
|
mpUI->StartButton->setEnabled( !mRunningNameGeneration );
|
|
mpUI->CancelButton->setEnabled( mRunningNameGeneration && !mCanceledNameGeneration );
|
|
|
|
const int TotalItems = mpUI->OutputTreeWidget->topLevelItemCount();
|
|
const bool HasResults = TotalItems > 0;
|
|
const bool HasCheckedResults = HasResults && !mCheckedItems.isEmpty();
|
|
mpUI->CheckAllButton->setEnabled( HasResults );
|
|
mpUI->UncheckAllButton->setEnabled( HasResults );
|
|
mpUI->ApplyButton->setEnabled( !mRunningNameGeneration && HasCheckedResults );
|
|
|
|
// Update label
|
|
if (HasResults)
|
|
{
|
|
mpUI->NumSelectedLabel->setText(
|
|
tr("%1 names, %2 selected")
|
|
.arg(TotalItems)
|
|
.arg(mCheckedItems.size()));
|
|
}
|
|
else
|
|
{
|
|
mpUI->NumSelectedLabel->clear();
|
|
}
|
|
}
|