mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-11 14:41:57 +00:00
Added property name generation system using dictionary attacks and added a UI dialog that allows you to search for property names and apply them to templates
This commit is contained in:
307
src/Editor/CGeneratePropertyNamesDialog.cpp
Normal file
307
src/Editor/CGeneratePropertyNamesDialog.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
#include "CGeneratePropertyNamesDialog.h"
|
||||
#include "ui_CGeneratePropertyNamesDialog.h"
|
||||
|
||||
#include "Editor/Widgets/CCheckableTreeWidgetItem.h"
|
||||
#include "UICommon.h"
|
||||
#include <Core/Resource/Cooker/CTemplateWriter.h>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <iterator>
|
||||
|
||||
CGeneratePropertyNamesDialog::CGeneratePropertyNamesDialog(QWidget* pParent)
|
||||
: QDialog(pParent)
|
||||
, mpUI( new Ui::CGeneratePropertyNamesDialog )
|
||||
, mFutureWatcher( this )
|
||||
, mRunningNameGeneration( false )
|
||||
, mCanceledNameGeneration( false )
|
||||
{
|
||||
mpUI->setupUi(this);
|
||||
mNotifier.SetProgressBar( mpUI->ProgressBar );
|
||||
|
||||
connect( mpUI->AddSuffixButton, SIGNAL(pressed()), this, SLOT(AddSuffix()) );
|
||||
connect( mpUI->RemoveSuffixButton, SIGNAL(pressed()), this, SLOT(DeleteSuffix()) );
|
||||
connect( mpUI->StartButton, SIGNAL(pressed()), this, SLOT(StartGeneration()) );
|
||||
connect( mpUI->CancelButton, SIGNAL(pressed()), this, SLOT(CancelGeneration()) );
|
||||
connect( mpUI->CheckAllButton, SIGNAL(pressed()), this, SLOT(CheckAll()) );
|
||||
connect( mpUI->UncheckAllButton, SIGNAL(pressed()), this, SLOT(UncheckAll()) );
|
||||
connect( mpUI->ApplyButton, SIGNAL(pressed()), this, SLOT(ApplyChanges()) );
|
||||
connect( mpUI->OutputTreeWidget, SIGNAL(CheckStateChanged(QTreeWidgetItem*)),
|
||||
this, SLOT(OnTreeItemChecked(QTreeWidgetItem*)) );
|
||||
connect( mpUI->OutputTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
|
||||
this, SLOT(OnTreeItemDoubleClicked(QTreeWidgetItem*)) );
|
||||
|
||||
// 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()
|
||||
{
|
||||
delete mpUI;
|
||||
}
|
||||
|
||||
/** Close event override */
|
||||
void CGeneratePropertyNamesDialog::closeEvent(QCloseEvent*)
|
||||
{
|
||||
if (mRunningNameGeneration)
|
||||
{
|
||||
CancelGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
/** Add an item to the suffix list */
|
||||
void CGeneratePropertyNamesDialog::AddSuffix()
|
||||
{
|
||||
QListWidgetItem* pNewItem = new QListWidgetItem("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);
|
||||
}
|
||||
}
|
||||
|
||||
/** Start name generation */
|
||||
void CGeneratePropertyNamesDialog::StartGeneration()
|
||||
{
|
||||
ASSERT(!mRunningNameGeneration);
|
||||
mRunningNameGeneration = true;
|
||||
mCanceledNameGeneration = false;
|
||||
mTaskOutput.clear();
|
||||
mCheckedItems.clear();
|
||||
mpUI->OutputTreeWidget->clear();
|
||||
|
||||
// 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.PrintToLog = mpUI->LogOutputCheckBox->isChecked();
|
||||
|
||||
// Run the task and configure ourselves so we can update correctly
|
||||
connect( &mFutureWatcher, SIGNAL(finished()), this, SLOT(GenerationComplete()) );
|
||||
mFuture = QtConcurrent::run(&mGenerator, &CPropertyNameGenerator::Generate, Params, &mNotifier);
|
||||
mFutureWatcher.setFuture(mFuture);
|
||||
|
||||
mUpdateTimer.start(500);
|
||||
connect( &mUpdateTimer, SIGNAL(timeout()), this, SLOT(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 = "../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
|
||||
QString WarningText =
|
||||
QString("Are you sure you want to rename %1 %2? This operation cannot be undone.")
|
||||
.arg(mCheckedItems.size())
|
||||
.arg(mCheckedItems.size() == 1 ? "property" : "properties");
|
||||
|
||||
bool ReallyRename = UICommon::YesNoQuestion(this, "Warning", WarningText);
|
||||
|
||||
if (!ReallyRename)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform rename operation
|
||||
for (int ItemIdx = 0; ItemIdx < mCheckedItems.size(); ItemIdx++)
|
||||
{
|
||||
QTreeWidgetItem* pItem = mCheckedItems[ItemIdx];
|
||||
u32 ID = TO_TSTRING( pItem->text(2) ).ToInt32();
|
||||
|
||||
QString NewName = pItem->text(0);
|
||||
CMasterTemplate::RenameProperty( ID, TO_TSTRING(NewName) );
|
||||
pItem->setText(3, NewName);
|
||||
}
|
||||
|
||||
CTemplateWriter::SavePropertyList();
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
std::list<SGeneratedPropertyName>::const_iterator Iter = rkOutput.cbegin();
|
||||
std::list<SGeneratedPropertyName>::const_iterator 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( CMasterTemplate::PropertyName(rkName.ID) );
|
||||
|
||||
QTreeWidgetItem* pItem = new CCheckableTreeWidgetItem(pTreeWidget, ColumnText);
|
||||
pItem->setFlags(Qt::ItemIsEnabled |
|
||||
Qt::ItemIsSelectable |
|
||||
Qt::ItemIsUserCheckable);
|
||||
pItem->setCheckState(0, Qt::Unchecked);
|
||||
|
||||
// Add children items
|
||||
for (int XmlIdx = 0; XmlIdx < rkName.XmlList.size(); XmlIdx++)
|
||||
{
|
||||
QString XmlName = TO_QSTRING( rkName.XmlList[XmlIdx] );
|
||||
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->TypeSuffixesGroupBox->setEnabled( !mRunningNameGeneration );
|
||||
mpUI->SettingsGroupBox->setEnabled( !mRunningNameGeneration );
|
||||
mpUI->StartButton->setEnabled( !mRunningNameGeneration );
|
||||
mpUI->CancelButton->setEnabled( mRunningNameGeneration && !mCanceledNameGeneration );
|
||||
|
||||
int TotalItems = mpUI->OutputTreeWidget->topLevelItemCount();
|
||||
bool HasResults = TotalItems > 0;
|
||||
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(
|
||||
QString("%1 names, %2 selected")
|
||||
.arg(TotalItems)
|
||||
.arg(mCheckedItems.size())
|
||||
);
|
||||
}
|
||||
else
|
||||
mpUI->NumSelectedLabel->clear();
|
||||
}
|
||||
Reference in New Issue
Block a user