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:
parent
ebab154a38
commit
3d72c9e4b2
File diff suppressed because it is too large
Load Diff
|
@ -231,7 +231,8 @@ HEADERS += \
|
|||
IProgressNotifier.h \
|
||||
IUIRelay.h \
|
||||
Resource/CResTypeFilter.h \
|
||||
GameProject/COpeningBanner.h
|
||||
GameProject/COpeningBanner.h \
|
||||
Resource/Script/CPropertyNameGenerator.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
@ -340,4 +341,5 @@ SOURCES += \
|
|||
CompressionUtil.cpp \
|
||||
IUIRelay.cpp \
|
||||
GameProject\COpeningBanner.cpp \
|
||||
IProgressNotifier.cpp
|
||||
IProgressNotifier.cpp \
|
||||
Resource/Script/CPropertyNameGenerator.cpp
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
mTaskCount = Math::Max(mTaskCount, TaskIndex + 1);
|
||||
}
|
||||
|
||||
void Report(int StepIndex, int StepCount, const TString& rkStepDesc)
|
||||
void Report(int StepIndex, int StepCount, const TString& rkStepDesc = "")
|
||||
{
|
||||
ASSERT(mTaskCount >= 1);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
class IUIRelay
|
||||
{
|
||||
public:
|
||||
virtual void AsyncMessageBox(const TString& rkInfoBoxTitle, const TString& rkMessage) = 0;
|
||||
virtual bool AskYesNoQuestion(const TString& rkInfoBoxTitle, const TString& rkQuestion) = 0;
|
||||
};
|
||||
extern IUIRelay *gpUIRelay;
|
||||
|
|
|
@ -223,7 +223,11 @@ void CMasterTemplate::RenameProperty(IPropertyTemplate *pTemp, const TString& rk
|
|||
{
|
||||
u32 ID = pTemp->PropertyID();
|
||||
if (ID <= 0xFF) ID = CreatePropertyID(pTemp);
|
||||
RenameProperty(ID, rkNewName);
|
||||
}
|
||||
|
||||
void CMasterTemplate::RenameProperty(u32 ID, const TString& rkNewName)
|
||||
{
|
||||
// Master name list
|
||||
auto NameIt = smPropertyNames.find(ID);
|
||||
TString Original;
|
||||
|
@ -249,17 +253,15 @@ void CMasterTemplate::RenameProperty(IPropertyTemplate *pTemp, const TString& rk
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<TString> CMasterTemplate::XMLsUsingID(u32 ID)
|
||||
void CMasterTemplate::XMLsUsingID(u32 ID, std::vector<TString>& rOutList)
|
||||
{
|
||||
auto InfoIt = smIDMap.find(ID);
|
||||
|
||||
if (InfoIt != smIDMap.end())
|
||||
{
|
||||
const SPropIDInfo& rkInfo = InfoIt->second;
|
||||
return rkInfo.XMLList;
|
||||
rOutList = rkInfo.XMLList;
|
||||
}
|
||||
else
|
||||
return std::vector<TString>();
|
||||
}
|
||||
|
||||
const std::vector<IPropertyTemplate*>* CMasterTemplate::TemplatesWithMatchingID(IPropertyTemplate *pTemp)
|
||||
|
|
|
@ -69,7 +69,8 @@ public:
|
|||
static u32 CreatePropertyID(IPropertyTemplate *pTemp);
|
||||
static void AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName = "");
|
||||
static void RenameProperty(IPropertyTemplate *pTemp, const TString& rkNewName);
|
||||
static std::vector<TString> XMLsUsingID(u32 ID);
|
||||
static void RenameProperty(u32 ID, const TString& rkNewName);
|
||||
static void XMLsUsingID(u32 ID, std::vector<TString>& rOutList);
|
||||
static const std::vector<IPropertyTemplate*>* TemplatesWithMatchingID(IPropertyTemplate *pTemp);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
#include "CPropertyNameGenerator.h"
|
||||
#include "IUIRelay.h"
|
||||
#include "Core/Resource/Factory/CTemplateLoader.h"
|
||||
#include "Core/Resource/Script/CMasterTemplate.h"
|
||||
#include <Common/Hash/CCRC32.h>
|
||||
|
||||
/** Default constructor */
|
||||
CPropertyNameGenerator::CPropertyNameGenerator()
|
||||
: mWordListLoadStarted(false)
|
||||
, mWordListLoadFinished(false)
|
||||
, mIsRunning(false)
|
||||
, mFinishedRunning(false)
|
||||
{
|
||||
}
|
||||
|
||||
void CPropertyNameGenerator::Warmup()
|
||||
{
|
||||
// Clear output from previous runs
|
||||
ASSERT(!mWordListLoadStarted || mWordListLoadFinished);
|
||||
mWordListLoadFinished = false;
|
||||
mWordListLoadStarted = true;
|
||||
mWords.clear();
|
||||
|
||||
// Load the word list from the file
|
||||
FILE* pListFile = fopen("../resources/WordList.txt", "r");
|
||||
ASSERT(pListFile);
|
||||
|
||||
while (!feof(pListFile))
|
||||
{
|
||||
char WordBuffer[256];
|
||||
fgets(&WordBuffer[0], 256, pListFile);
|
||||
|
||||
// Capitalize first letter
|
||||
if (WordBuffer[0] >= 'a' && WordBuffer[0] <= 'z')
|
||||
{
|
||||
WordBuffer[0] -= 0x20;
|
||||
}
|
||||
|
||||
SWord Word;
|
||||
Word.Word = TString(WordBuffer).Trimmed();
|
||||
Word.Usages = 0;
|
||||
mWords.push_back(Word);
|
||||
}
|
||||
|
||||
fclose(pListFile);
|
||||
mWordListLoadFinished = true;
|
||||
}
|
||||
|
||||
void CPropertyNameGenerator::Generate(const SPropertyNameGenerationParameters& rkParams, IProgressNotifier* pProgress)
|
||||
{
|
||||
// Make sure all prerequisite data is loaded!
|
||||
ASSERT(!mIsRunning);
|
||||
ASSERT(rkParams.TypeNames.size() > 0);
|
||||
mGeneratedNames.clear();
|
||||
mIsRunning = true;
|
||||
mFinishedRunning = false;
|
||||
|
||||
// If we haven't loaded the word list yet, load it.
|
||||
// If we are still loading the word list, wait until we're finished.
|
||||
if (!mWordListLoadFinished)
|
||||
{
|
||||
if (mWordListLoadStarted)
|
||||
while (!mWordListLoadFinished) {}
|
||||
else
|
||||
Warmup();
|
||||
}
|
||||
|
||||
// Calculate the number of steps involved in this task.
|
||||
const int kNumWords = mWords.size();
|
||||
const int kMaxWords = rkParams.MaxWords;
|
||||
int TestsDone = 0;
|
||||
int TotalTests = 1;
|
||||
|
||||
for (int i = 0; i < kMaxWords; i++)
|
||||
TotalTests *= kNumWords;
|
||||
|
||||
pProgress->SetOneShotTask("Generating property names");
|
||||
pProgress->Report(TestsDone, TotalTests);
|
||||
|
||||
// Configure params needed to run the name generation!
|
||||
bool WriteToLog = rkParams.PrintToLog;
|
||||
bool SaveResults = true;
|
||||
|
||||
// The prefix only needs to be hashed this one time
|
||||
CCRC32 PrefixHash;
|
||||
PrefixHash.Hash( *rkParams.Prefix );
|
||||
|
||||
// Use a stack to keep track of the current word we are on. We can use this
|
||||
// to cache the hash of a word and then re-use it later instead of recaculating
|
||||
// the same hashes over and over. Init the stack with the first word.
|
||||
struct SWordCache
|
||||
{
|
||||
int WordIndex;
|
||||
CCRC32 Hash;
|
||||
};
|
||||
std::vector<SWordCache> WordCache;
|
||||
|
||||
SWordCache FirstWord { -1, CCRC32() };
|
||||
WordCache.push_back(FirstWord);
|
||||
|
||||
while ( true )
|
||||
{
|
||||
// Increment the current word, handle wrapping back to 0, and update cached hashes as needed.
|
||||
int RecalcIndex = WordCache.size() - 1;
|
||||
WordCache.back().WordIndex++;
|
||||
|
||||
while (WordCache[RecalcIndex].WordIndex >= kNumWords)
|
||||
{
|
||||
WordCache[RecalcIndex].WordIndex = 0;
|
||||
|
||||
if (RecalcIndex > 0)
|
||||
{
|
||||
RecalcIndex--;
|
||||
WordCache[RecalcIndex].WordIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
SWordCache NewWord { 0, CCRC32() };
|
||||
WordCache.push_back(NewWord);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've hit the word limit, break out and end the name generation system.
|
||||
if (WordCache.size() > kMaxWords)
|
||||
break;
|
||||
|
||||
// Now that all words are updated, calculate the new hashes.
|
||||
CCRC32 LastValidHash = (RecalcIndex > 0 ? WordCache[RecalcIndex-1].Hash : PrefixHash);
|
||||
|
||||
for (; RecalcIndex < WordCache.size(); RecalcIndex++)
|
||||
{
|
||||
int Index = WordCache[RecalcIndex].WordIndex;
|
||||
LastValidHash.Hash( *mWords[Index].Word );
|
||||
WordCache[RecalcIndex].Hash = LastValidHash;
|
||||
}
|
||||
|
||||
// We got our hash yay! Now hash the suffix and then we can test with each type name
|
||||
CCRC32 BaseHash = LastValidHash;
|
||||
BaseHash.Hash( *rkParams.Suffix );
|
||||
|
||||
for (int TypeIdx = 0; TypeIdx < rkParams.TypeNames.size(); TypeIdx++)
|
||||
{
|
||||
CCRC32 FullHash = BaseHash;
|
||||
FullHash.Hash( *rkParams.TypeNames[TypeIdx] );
|
||||
u32 PropertyID = FullHash.Digest();
|
||||
|
||||
// Check if this hash is a property ID - it's valid if there are any XMLs using this ID
|
||||
SGeneratedPropertyName PropertyName;
|
||||
CMasterTemplate::XMLsUsingID(PropertyID, PropertyName.XmlList);
|
||||
|
||||
if (PropertyName.XmlList.size() > 0)
|
||||
{
|
||||
// Generate a string with the complete name. (We wait to do this until now to avoid needless string allocation)
|
||||
PropertyName.Name = rkParams.Prefix;
|
||||
|
||||
for (int WordIdx = 0; WordIdx < WordCache.size(); WordIdx++)
|
||||
{
|
||||
int Index = WordCache[WordIdx].WordIndex;
|
||||
PropertyName.Name += mWords[Index].Word;
|
||||
}
|
||||
|
||||
PropertyName.Name += rkParams.Suffix;
|
||||
PropertyName.Type = rkParams.TypeNames[TypeIdx];
|
||||
PropertyName.ID = PropertyID;
|
||||
|
||||
if (SaveResults)
|
||||
{
|
||||
mGeneratedNames.push_back(PropertyName);
|
||||
|
||||
// Check if we have too many saved results. This can cause memory issues and crashing.
|
||||
// If we have too many saved results, then to avoid crashing we will force enable log output.
|
||||
if (mGeneratedNames.size() > 9999)
|
||||
{
|
||||
gpUIRelay->AsyncMessageBox("Warning", "There are over 10,000 results. To avoid memory issues, results will no longer print to the screen. Check the log for the rest of the output.");
|
||||
WriteToLog = true;
|
||||
SaveResults = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Log this out
|
||||
if ( WriteToLog )
|
||||
{
|
||||
TString DelimitedXmlList;
|
||||
|
||||
for (int XmlIdx = 0; XmlIdx < PropertyName.XmlList.size(); XmlIdx++)
|
||||
{
|
||||
DelimitedXmlList += PropertyName.XmlList[XmlIdx] + "\n";
|
||||
}
|
||||
|
||||
TString LogMsg = TString::Format("%s [%s] : 0x%08X\n", *PropertyName.Name, *PropertyName.Type, PropertyName.ID) + DelimitedXmlList;
|
||||
Log::Write(LogMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Every 250 tests, check with the progress notifier. Update the progress
|
||||
// bar and check whether the user has requested to cancel the operation.
|
||||
TestsDone++;
|
||||
|
||||
if ( (TestsDone % 250) == 0 )
|
||||
{
|
||||
if (pProgress->ShouldCancel())
|
||||
break;
|
||||
|
||||
pProgress->Report(TestsDone, TotalTests);
|
||||
}
|
||||
}
|
||||
|
||||
mIsRunning = false;
|
||||
mFinishedRunning = true;
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef CPROPERTYNAMEGENERATOR_H
|
||||
#define CPROPERTYNAMEGENERATOR_H
|
||||
|
||||
#include "Core/IProgressNotifier.h"
|
||||
#include <Common/Common.h>
|
||||
|
||||
/** Parameters for using the name generator */
|
||||
struct SPropertyNameGenerationParameters
|
||||
{
|
||||
/** Maximum number of words per name; name generation will complete when all possibilities have been checked */
|
||||
int MaxWords;
|
||||
|
||||
/** Prefix to include at the beginning of every name */
|
||||
TString Prefix;
|
||||
|
||||
/** Suffix to include at the end of every name */
|
||||
TString Suffix;
|
||||
|
||||
/** List of valid type suffixes */
|
||||
std::vector<TString> TypeNames;
|
||||
|
||||
/** Whether to print the output from the generation process to the log */
|
||||
bool PrintToLog;
|
||||
};
|
||||
|
||||
/** A generated property name */
|
||||
struct SGeneratedPropertyName
|
||||
{
|
||||
TString Name;
|
||||
TString Type;
|
||||
u32 ID;
|
||||
std::vector<TString> XmlList;
|
||||
};
|
||||
|
||||
/** Generates property names and validates them against know property IDs. */
|
||||
class CPropertyNameGenerator
|
||||
{
|
||||
/** Whether we have started loading the word list */
|
||||
bool mWordListLoadStarted;
|
||||
|
||||
/** Whether the word list has been fully loaded */
|
||||
bool mWordListLoadFinished;
|
||||
|
||||
/** Whether the generation process is running */
|
||||
bool mIsRunning;
|
||||
|
||||
/** Whether the generation process finished running */
|
||||
bool mFinishedRunning;
|
||||
|
||||
/** List of words */
|
||||
struct SWord
|
||||
{
|
||||
TString Word;
|
||||
int Usages;
|
||||
};
|
||||
std::vector<SWord> mWords;
|
||||
|
||||
/** List of output generated property names */
|
||||
std::list<SGeneratedPropertyName> mGeneratedNames;
|
||||
|
||||
/** List of word indices */
|
||||
std::vector<int> mWordIndices;
|
||||
|
||||
public:
|
||||
/** Default constructor */
|
||||
CPropertyNameGenerator();
|
||||
|
||||
/** Prepares the generator for running name generation */
|
||||
void Warmup();
|
||||
|
||||
/** Run the name generation system */
|
||||
void Generate(const SPropertyNameGenerationParameters& rkParams, IProgressNotifier* pProgressNotifier);
|
||||
|
||||
/** Accessors */
|
||||
bool IsRunning() const
|
||||
{
|
||||
return mIsRunning;
|
||||
}
|
||||
|
||||
const std::list<SGeneratedPropertyName>& GetOutput() const
|
||||
{
|
||||
return mGeneratedNames;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CPROPERTYNAMEGENERATOR_H
|
|
@ -315,10 +315,10 @@ const char* HashablePropTypeName(EPropertyType Prop)
|
|||
case eFloatProperty: return "float";
|
||||
case eStringProperty: return "string";
|
||||
case eColorProperty: return "Color";
|
||||
case eVector3Property: return "Vector3f";
|
||||
case eSoundProperty: return "SfxId";
|
||||
case eVector3Property: return "Vector";
|
||||
case eSoundProperty: return "sound";
|
||||
case eAssetProperty: return "asset";
|
||||
case eMayaSplineProperty: return "MayaSpline";
|
||||
case eMayaSplineProperty: return "spline";
|
||||
|
||||
// All other types are either invalid or need a custom reimplementation because they can return multiple strings (like struct)
|
||||
default:
|
||||
|
|
|
@ -338,7 +338,7 @@ public:
|
|||
|
||||
const char* GetTypeNameString() const
|
||||
{
|
||||
return (Game() < eCorruptionProto ? "AnimationParameters" : "CharacterAnimationSet");
|
||||
return (Game() < eCorruptionProto ? "AnimationSet" : "CharacterAnimationSet");
|
||||
}
|
||||
|
||||
IMPLEMENT_TEMPLATE_CLONE(TCharacterTemplate)
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef CGENERATEPROPERTYNAMESDIALOG_H
|
||||
#define CGENERATEPROPERTYNAMESDIALOG_H
|
||||
|
||||
#include "CProgressBarNotifier.h"
|
||||
#include <Core/Resource/Script/CPropertyNameGenerator.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QScopedPointer>
|
||||
#include <QTimer>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
namespace Ui {
|
||||
class CGeneratePropertyNamesDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog box for accessing property name generation functionality.
|
||||
*/
|
||||
class CGeneratePropertyNamesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Ui::CGeneratePropertyNamesDialog* mpUI;
|
||||
|
||||
/** The name generator */
|
||||
CPropertyNameGenerator mGenerator;
|
||||
|
||||
/** Progress notifier for updating the progress bar */
|
||||
CProgressBarNotifier mNotifier;
|
||||
|
||||
/** Future/future watcher for name generation task */
|
||||
QFuture<void> mFuture;
|
||||
QFutureWatcher<void> mFutureWatcher;
|
||||
|
||||
/** Timer for fetching updates from name generation task */
|
||||
QTimer mUpdateTimer;
|
||||
|
||||
/** Copy of the output buffer from the name generator; only set after completion */
|
||||
QList<SGeneratedPropertyName> mTaskOutput;
|
||||
|
||||
/** Checked items in the output tree widget */
|
||||
QVector<QTreeWidgetItem*> mCheckedItems;
|
||||
|
||||
/** Whether name generation is running */
|
||||
bool mRunningNameGeneration;
|
||||
|
||||
/** Whether name generation has been canceled */
|
||||
bool mCanceledNameGeneration;
|
||||
|
||||
public:
|
||||
explicit CGeneratePropertyNamesDialog(QWidget *pParent = 0);
|
||||
~CGeneratePropertyNamesDialog();
|
||||
|
||||
public slots:
|
||||
/** Close event override */
|
||||
virtual void closeEvent(QCloseEvent* pEvent);
|
||||
|
||||
/** Add an item to the suffix list */
|
||||
void AddSuffix();
|
||||
|
||||
/** Deletes an item from the suffix list */
|
||||
void DeleteSuffix();
|
||||
|
||||
/** Start name generation */
|
||||
void StartGeneration();
|
||||
|
||||
/** Cancel name generation */
|
||||
void CancelGeneration();
|
||||
|
||||
/** Called when name generation is complete */
|
||||
void GenerationComplete();
|
||||
|
||||
/** Called when an item in the output tree has been checked or unchecked */
|
||||
void OnTreeItemChecked(QTreeWidgetItem* pItem);
|
||||
|
||||
/** Called when an item in the output tree has been double clicked */
|
||||
void OnTreeItemDoubleClicked(QTreeWidgetItem* pItem);
|
||||
|
||||
/** Check all items in the output tree */
|
||||
void CheckAll();
|
||||
|
||||
/** Uncheck all items in the output tree */
|
||||
void UncheckAll();
|
||||
|
||||
/** Apply generated names on selected items */
|
||||
void ApplyChanges();
|
||||
|
||||
/** Check progress on name generation task and display results on the UI */
|
||||
void CheckForNewResults();
|
||||
|
||||
/** Updates the enabled status of various widgets */
|
||||
void UpdateUI();
|
||||
};
|
||||
|
||||
#endif // CGENERATEPROPERTYNAMESDIALOG_H
|
|
@ -0,0 +1,383 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CGeneratePropertyNamesDialog</class>
|
||||
<widget class="QDialog" name="CGeneratePropertyNamesDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>693</width>
|
||||
<height>604</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Generate Property Names</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="SettingsGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="MaxWordsLabel">
|
||||
<property name="text">
|
||||
<string>Max words:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="NumWordsSpinBox">
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="PrefixLabel">
|
||||
<property name="text">
|
||||
<string>Prefix:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="PrefixLineEdit"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="SuffixLabel">
|
||||
<property name="text">
|
||||
<string>Suffix:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="SuffixLineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="LogOutputCheckBox">
|
||||
<property name="text">
|
||||
<string>Print output to log</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="TypeSuffixesGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Types</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="TypeSuffixesListWidget">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>bool</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>int</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>float</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>asset</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>choice</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>enum</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>string</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>sound</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="AddSuffixButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="Icons.qrc">
|
||||
<normaloff>:/icons/Plus.png</normaloff>:/icons/Plus.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="RemoveSuffixButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="Icons.qrc">
|
||||
<normaloff>:/icons/Minus v2.png</normaloff>:/icons/Minus v2.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="OutputGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>3</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Output</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="CCheckableTreeWidget" name="OutputTreeWidget">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>ID</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Current</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="CheckAllButton">
|
||||
<property name="text">
|
||||
<string>Check All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="UncheckAllButton">
|
||||
<property name="text">
|
||||
<string>Uncheck All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="NumSelectedLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ApplyButton">
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="ProgressBar">
|
||||
<property name="maximum">
|
||||
<number>100000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="StartButton">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="CancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>CCheckableTreeWidget</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<header>Editor/Widgets/CCheckableTreeWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="Icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef CPROGRESSBARNOTIFIER_H
|
||||
#define CPROGRESSBARNOTIFIER_H
|
||||
|
||||
#include <Math/MathUtil.h>
|
||||
#include <Core/IProgressNotifier.h>
|
||||
#include <QProgressBar>
|
||||
|
||||
/** Progress notifier class that updates a QProgressBar. */
|
||||
class CProgressBarNotifier : public IProgressNotifier
|
||||
{
|
||||
/** The progress bar we are relaying updates to */
|
||||
QProgressBar* mpProgressBar;
|
||||
|
||||
/** Whether the user has requested to cancel */
|
||||
bool mCancel;
|
||||
|
||||
public:
|
||||
CProgressBarNotifier()
|
||||
: IProgressNotifier()
|
||||
, mpProgressBar(nullptr)
|
||||
, mCancel(false)
|
||||
{}
|
||||
|
||||
inline void SetProgressBar(QProgressBar* pProgressBar)
|
||||
{
|
||||
mpProgressBar = pProgressBar;
|
||||
}
|
||||
|
||||
inline void SetCanceled(bool ShouldCancel)
|
||||
{
|
||||
mCancel = ShouldCancel;
|
||||
}
|
||||
|
||||
/** IProgressNotifier interface */
|
||||
virtual bool ShouldCancel() const
|
||||
{
|
||||
return mCancel;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void UpdateProgress(const TString &, const TString &, float ProgressPercent)
|
||||
{
|
||||
if (mpProgressBar)
|
||||
{
|
||||
int Alpha = Math::Lerp(mpProgressBar->minimum(), mpProgressBar->maximum(), ProgressPercent);
|
||||
|
||||
// Defer setValue call so it runs on the correct thread
|
||||
QMetaObject::invokeMethod(mpProgressBar, "setValue", Qt::AutoConnection, Q_ARG(int, Alpha));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CPROGRESSBARNOTIFIER_H
|
|
@ -25,6 +25,13 @@ public:
|
|||
|
||||
// Note: All function calls should be deferred with QMetaObject::invokeMethod to ensure
|
||||
// that they run on the UI thread instead of whatever thread we happen to be on.
|
||||
virtual void AsyncMessageBox(const TString& rkInfoBoxTitle, const TString& rkMessage)
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "AsyncMessageBoxSlot", Qt::QueuedConnection,
|
||||
Q_ARG(QString, TO_QSTRING(rkInfoBoxTitle)),
|
||||
Q_ARG(QString, TO_QSTRING(rkMessage)) );
|
||||
}
|
||||
|
||||
virtual bool AskYesNoQuestion(const TString& rkInfoBoxTitle, const TString& rkQuestion)
|
||||
{
|
||||
bool RetVal;
|
||||
|
@ -36,6 +43,11 @@ public:
|
|||
}
|
||||
|
||||
public slots:
|
||||
void AsyncMessageBoxSlot(const QString& rkInfoBoxTitle, const QString& rkMessage)
|
||||
{
|
||||
UICommon::InfoMsg(gpEdApp->WorldEditor(), rkInfoBoxTitle, rkMessage);
|
||||
}
|
||||
|
||||
bool AskYesNoQuestionSlot(const QString& rkInfoBoxTitle, const QString& rkQuestion)
|
||||
{
|
||||
return UICommon::YesNoQuestion(gpEdApp->WorldEditor(), rkInfoBoxTitle, rkQuestion);
|
||||
|
|
|
@ -199,7 +199,11 @@ HEADERS += \
|
|||
ResourceBrowser/CVirtualDirectoryTreeView.h \
|
||||
CPropertyNameValidator.h \
|
||||
Widgets/CSoftValidatorLineEdit.h \
|
||||
Widgets/CValidityLabel.h
|
||||
Widgets/CValidityLabel.h \
|
||||
CGeneratePropertyNamesDialog.h \
|
||||
CProgressBarNotifier.h \
|
||||
Widgets/CCheckableTreeWidgetItem.h \
|
||||
Widgets/CCheckableTreeWidget.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
@ -274,7 +278,8 @@ SOURCES += \
|
|||
ResourceBrowser/CResourceTableView.cpp \
|
||||
ResourceBrowser/CVirtualDirectoryModel.cpp \
|
||||
ResourceBrowser/CVirtualDirectoryTreeView.cpp \
|
||||
CPropertyNameValidator.cpp
|
||||
CPropertyNameValidator.cpp \
|
||||
CGeneratePropertyNamesDialog.cpp
|
||||
|
||||
# UI Files
|
||||
FORMS += \
|
||||
|
@ -300,4 +305,5 @@ FORMS += \
|
|||
CProjectSettingsDialog.ui \
|
||||
WorldEditor/CPoiMapSidebar.ui \
|
||||
CProgressDialog.ui \
|
||||
Widgets/CSelectResourcePanel.ui
|
||||
Widgets/CSelectResourcePanel.ui \
|
||||
CGeneratePropertyNamesDialog.ui
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef CCHECKABLETREEWIDGET_H
|
||||
#define CCHECKABLETREEWIDGET_H
|
||||
|
||||
#include <QTreeWidget>
|
||||
|
||||
/**
|
||||
* QTreeWidget subclass that emits a signal when an item is checked or unchecked.
|
||||
* Items must be instantiated as CCheckableTreeWidgetItem, not QTreeWidgetItem.
|
||||
*/
|
||||
class CCheckableTreeWidget : public QTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void CheckStateChanged(QTreeWidgetItem* pItem);
|
||||
|
||||
public:
|
||||
CCheckableTreeWidget(QWidget* pParent = 0)
|
||||
: QTreeWidget(pParent) {}
|
||||
};
|
||||
|
||||
#endif // CCHECKABLETREEWIDGET_H
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef CCHECKABLETREEWIDGETITEM_H
|
||||
#define CCHECKABLETREEWIDGETITEM_H
|
||||
|
||||
#include "CCheckableTreeWidget.h"
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
/** QTreeWidgetItem subclass that emits a signal when checked/unchecked. */
|
||||
class CCheckableTreeWidgetItem : public QTreeWidgetItem
|
||||
{
|
||||
public:
|
||||
/** Constructors */
|
||||
CCheckableTreeWidgetItem(int type = Type)
|
||||
: QTreeWidgetItem(type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(const QStringList& strings, int type = Type)
|
||||
: QTreeWidgetItem(strings, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(QTreeWidget* parent, int type = Type)
|
||||
: QTreeWidgetItem(parent, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(QTreeWidget* parent, const QStringList& strings, int type = Type)
|
||||
: QTreeWidgetItem(parent, strings, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(QTreeWidget* parent, QTreeWidgetItem* preceding, int type = Type)
|
||||
: QTreeWidgetItem(parent, preceding, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(QTreeWidgetItem* parent, int type = Type)
|
||||
: QTreeWidgetItem(parent, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(QTreeWidgetItem* parent, const QStringList& strings, int type = Type)
|
||||
: QTreeWidgetItem(parent, strings, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(QTreeWidgetItem* parent, QTreeWidgetItem* preceding, int type = Type)
|
||||
: QTreeWidgetItem(parent, preceding, type) {}
|
||||
|
||||
CCheckableTreeWidgetItem(const QTreeWidgetItem& other)
|
||||
: QTreeWidgetItem(other) {}
|
||||
|
||||
/** setData override to catch check state changes */
|
||||
virtual void setData(int Column, int Role, const QVariant& rkValue)
|
||||
{
|
||||
Qt::CheckState OldState = checkState(0);
|
||||
QTreeWidgetItem::setData(Column, Role, rkValue);
|
||||
Qt::CheckState NewState = checkState(0);
|
||||
|
||||
if (OldState != NewState)
|
||||
{
|
||||
CCheckableTreeWidget* pCheckableTree =
|
||||
qobject_cast<CCheckableTreeWidget*>(treeWidget());
|
||||
|
||||
if (pCheckableTree)
|
||||
{
|
||||
pCheckableTree->CheckStateChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CCHECKABLETREEWIDGETITEM_H
|
|
@ -34,7 +34,8 @@ CTemplateEditDialog::CTemplateEditDialog(IPropertyTemplate *pTemplate, QWidget *
|
|||
else
|
||||
{
|
||||
CTemplateLoader::LoadAllGames();
|
||||
std::vector<TString> TemplateList = CMasterTemplate::XMLsUsingID(pTemplate->PropertyID());
|
||||
std::vector<TString> TemplateList;
|
||||
CMasterTemplate::XMLsUsingID(pTemplate->PropertyID(), TemplateList);
|
||||
|
||||
for (u32 iTemp = 0; iTemp < TemplateList.size(); iTemp++)
|
||||
mpUI->TemplatesListWidget->addItem(TO_QSTRING(TemplateList[iTemp]));
|
||||
|
|
|
@ -36,6 +36,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
|
|||
, mpArea(nullptr)
|
||||
, mpWorld(nullptr)
|
||||
, mpLinkDialog(new CLinkDialog(this, this))
|
||||
, mpGeneratePropertyNamesDialog(new CGeneratePropertyNamesDialog(this))
|
||||
, mIsMakingLink(false)
|
||||
, mpNewLinkSender(nullptr)
|
||||
, mpNewLinkReceiver(nullptr)
|
||||
|
@ -174,6 +175,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
|
|||
connect(ui->ActionUnlink, SIGNAL(triggered()), this, SLOT(OnUnlinkClicked()));
|
||||
|
||||
connect(ui->ActionEditLayers, SIGNAL(triggered()), this, SLOT(EditLayers()));
|
||||
connect(ui->ActionGeneratePropertyNames, SIGNAL(triggered()), this, SLOT(GeneratePropertyNames()));
|
||||
|
||||
connect(ui->ActionDrawWorld, SIGNAL(triggered()), this, SLOT(ToggleDrawWorld()));
|
||||
connect(ui->ActionDrawObjects, SIGNAL(triggered()), this, SLOT(ToggleDrawObjects()));
|
||||
|
@ -1341,3 +1343,9 @@ void CWorldEditor::EditLayers()
|
|||
Editor.SetArea(mpArea);
|
||||
Editor.exec();
|
||||
}
|
||||
|
||||
void CWorldEditor::GeneratePropertyNames()
|
||||
{
|
||||
// Launch property name generation dialog
|
||||
mpGeneratePropertyNamesDialog->show();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "CScriptEditSidebar.h"
|
||||
#include "CWorldInfoSidebar.h"
|
||||
#include "Editor/INodeEditor.h"
|
||||
#include "Editor/CGeneratePropertyNamesDialog.h"
|
||||
#include "Editor/CGizmo.h"
|
||||
#include "Editor/CSceneViewport.h"
|
||||
|
||||
|
@ -55,6 +56,7 @@ class CWorldEditor : public INodeEditor
|
|||
|
||||
CCollisionRenderSettingsDialog *mpCollisionDialog;
|
||||
CLinkDialog *mpLinkDialog;
|
||||
CGeneratePropertyNamesDialog* mpGeneratePropertyNamesDialog;
|
||||
|
||||
bool mIsMakingLink;
|
||||
CScriptObject *mpNewLinkSender;
|
||||
|
@ -177,6 +179,7 @@ private slots:
|
|||
void DecrementGizmo();
|
||||
void EditCollisionRenderSettings();
|
||||
void EditLayers();
|
||||
void GeneratePropertyNames();
|
||||
|
||||
signals:
|
||||
void MapChanged(CWorld *pNewWorld, CGameArea *pNewArea);
|
||||
|
|
|
@ -339,11 +339,12 @@
|
|||
<string>Tools</string>
|
||||
</property>
|
||||
<addaction name="ActionEditLayers"/>
|
||||
<addaction name="ActionGeneratePropertyNames"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menuTools"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuTools"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="EditModeToolBar">
|
||||
<property name="sizePolicy">
|
||||
|
@ -767,6 +768,11 @@
|
|||
<string>Project Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionGeneratePropertyNames">
|
||||
<property name="text">
|
||||
<string>Generate Property Names</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
|
@ -46,7 +46,7 @@ template<typename Type>
|
|||
Type Lerp(const Type& rkA, const Type& rkB, float t)
|
||||
{
|
||||
Type Diff = rkB - rkA;
|
||||
return rkA + (Diff * t);
|
||||
return rkA + Type(Diff * t);
|
||||
}
|
||||
|
||||
std::pair<bool,float> RayPlaneIntersection(const CRay& rkRay, const CPlane& rkPlane);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue