#include "CGeneratePropertyNamesDialog.h" #include "ui_CGeneratePropertyNamesDialog.h" #include "Editor/Widgets/CCheckableTreeWidgetItem.h" #include "UICommon.h" #include #include #include #include CGeneratePropertyNamesDialog::CGeneratePropertyNamesDialog(QWidget* pParent) : QDialog(pParent) , mpUI(std::make_unique()) , 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::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::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& 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(); } }