Support for adding/removing strings
This commit is contained in:
parent
eb9236bbea
commit
cb262504af
|
@ -90,6 +90,86 @@ void CStringTable::SetStringName(uint StringIndex, const TString& kNewName)
|
||||||
mStringNames[StringIndex] = kNewName;
|
mStringNames[StringIndex] = kNewName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Move string to another position in the table */
|
||||||
|
void CStringTable::MoveString(uint StringIndex, uint NewIndex)
|
||||||
|
{
|
||||||
|
ASSERT( NumStrings() > StringIndex );
|
||||||
|
ASSERT( NumStrings() > NewIndex );
|
||||||
|
|
||||||
|
if (NewIndex == StringIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update string data
|
||||||
|
for (uint LanguageIdx = 0; LanguageIdx < StringIndex; LanguageIdx++)
|
||||||
|
{
|
||||||
|
SLanguageData& Language = mLanguages[LanguageIdx];
|
||||||
|
TString String = Language.Strings[StringIndex];
|
||||||
|
|
||||||
|
if (NewIndex > StringIndex)
|
||||||
|
{
|
||||||
|
for (uint i=StringIndex; i<NewIndex; i++)
|
||||||
|
Language.Strings[i] = Language.Strings[i+1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint i=StringIndex; i>NewIndex; i--)
|
||||||
|
Language.Strings[i] = Language.Strings[i-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Language.Strings[NewIndex] = String;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update string name
|
||||||
|
TString Name = mStringNames[StringIndex];
|
||||||
|
|
||||||
|
if (NewIndex > StringIndex)
|
||||||
|
{
|
||||||
|
for (uint i=StringIndex; i<NewIndex; i++)
|
||||||
|
mStringNames[i] = mStringNames[i+1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint i=StringIndex; i>NewIndex; i--)
|
||||||
|
mStringNames[i] = mStringNames[i-1];
|
||||||
|
}
|
||||||
|
mStringNames[NewIndex] = Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a new string to the table */
|
||||||
|
void CStringTable::AddString(uint AtIndex)
|
||||||
|
{
|
||||||
|
if (AtIndex < NumStrings())
|
||||||
|
{
|
||||||
|
if (mStringNames.size() > AtIndex)
|
||||||
|
{
|
||||||
|
mStringNames.insert( mStringNames.begin() + AtIndex, 1, "" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
AtIndex = NumStrings();
|
||||||
|
|
||||||
|
for (uint LanguageIdx = 0; LanguageIdx < mLanguages.size(); LanguageIdx++)
|
||||||
|
{
|
||||||
|
SLanguageData& Language = mLanguages[LanguageIdx];
|
||||||
|
Language.Strings.insert( Language.Strings.begin() + AtIndex, 1, "" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove a string from the table */
|
||||||
|
void CStringTable::RemoveString(uint StringIndex)
|
||||||
|
{
|
||||||
|
ASSERT( StringIndex < NumStrings() );
|
||||||
|
|
||||||
|
if (mStringNames.size() > StringIndex)
|
||||||
|
mStringNames.erase( mStringNames.begin() + StringIndex );
|
||||||
|
|
||||||
|
for (uint LanguageIdx = 0; LanguageIdx < mLanguages.size(); LanguageIdx++)
|
||||||
|
{
|
||||||
|
SLanguageData& Language = mLanguages[LanguageIdx];
|
||||||
|
Language.Strings.erase( Language.Strings.begin() + StringIndex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Configures the string table with default languages for the game/region pairing of the resource */
|
/** Configures the string table with default languages for the game/region pairing of the resource */
|
||||||
void CStringTable::ConfigureDefaultLanguages()
|
void CStringTable::ConfigureDefaultLanguages()
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,6 +58,15 @@ public:
|
||||||
/** Updates a string name */
|
/** Updates a string name */
|
||||||
void SetStringName(uint StringIndex, const TString& kNewName);
|
void SetStringName(uint StringIndex, const TString& kNewName);
|
||||||
|
|
||||||
|
/** Move string to another position in the table */
|
||||||
|
void MoveString(uint StringIndex, uint NewIndex);
|
||||||
|
|
||||||
|
/** Add a new string to the table */
|
||||||
|
void AddString(uint AtIndex);
|
||||||
|
|
||||||
|
/** Remove a string from the table */
|
||||||
|
void RemoveString(uint StringIndex);
|
||||||
|
|
||||||
/** Configures the string table with default languages for the game/region pairing of the resource */
|
/** Configures the string table with default languages for the game/region pairing of the resource */
|
||||||
void ConfigureDefaultLanguages();
|
void ConfigureDefaultLanguages();
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,26 @@
|
||||||
#include "ui_CTweakEditor.h"
|
#include "ui_CTweakEditor.h"
|
||||||
#include "Editor/Undo/IUndoCommand.h"
|
#include "Editor/Undo/IUndoCommand.h"
|
||||||
|
|
||||||
|
/** Internal undo command for changing tabs */
|
||||||
|
class CSetTweakIndexCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
CTweakEditor* mpEditor;
|
||||||
|
int mOldIndex, mNewIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSetTweakIndexCommand(CTweakEditor* pEditor, int OldIndex, int NewIndex)
|
||||||
|
: IUndoCommand("Change Tab")
|
||||||
|
, mpEditor(pEditor)
|
||||||
|
, mOldIndex(OldIndex)
|
||||||
|
, mNewIndex(NewIndex)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void undo() override { mpEditor->SetActiveTweakIndex(mOldIndex); }
|
||||||
|
virtual void redo() override { mpEditor->SetActiveTweakIndex(mNewIndex); }
|
||||||
|
virtual bool AffectsCleanState() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** CTweakEditor functions */
|
||||||
CTweakEditor::CTweakEditor(QWidget* pParent)
|
CTweakEditor::CTweakEditor(QWidget* pParent)
|
||||||
: IEditor(pParent)
|
: IEditor(pParent)
|
||||||
, mpUI(new Ui::CTweakEditor)
|
, mpUI(new Ui::CTweakEditor)
|
||||||
|
@ -73,7 +93,8 @@ void CTweakEditor::SetActiveTweakData(CTweakData* pTweakData)
|
||||||
{
|
{
|
||||||
if (mTweakAssets[TweakIdx] == pTweakData)
|
if (mTweakAssets[TweakIdx] == pTweakData)
|
||||||
{
|
{
|
||||||
SetActiveTweakIndex(TweakIdx);
|
CSetTweakIndexCommand* pCommand = new CSetTweakIndexCommand(this, mCurrentTweakIndex, TweakIdx);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,25 +117,6 @@ void CTweakEditor::SetActiveTweakIndex(int Index)
|
||||||
|
|
||||||
void CTweakEditor::OnTweakTabClicked(int Index)
|
void CTweakEditor::OnTweakTabClicked(int Index)
|
||||||
{
|
{
|
||||||
/** Internal undo command for changing tabs */
|
|
||||||
class CSetTweakIndexCommand : public IUndoCommand
|
|
||||||
{
|
|
||||||
CTweakEditor* mpEditor;
|
|
||||||
int mOldIndex, mNewIndex;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CSetTweakIndexCommand(CTweakEditor* pEditor, int OldIndex, int NewIndex)
|
|
||||||
: IUndoCommand("Change Tab")
|
|
||||||
, mpEditor(pEditor)
|
|
||||||
, mOldIndex(OldIndex)
|
|
||||||
, mNewIndex(NewIndex)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual void undo() override { mpEditor->SetActiveTweakIndex(mOldIndex); }
|
|
||||||
virtual void redo() override { mpEditor->SetActiveTweakIndex(mNewIndex); }
|
|
||||||
virtual bool AffectsCleanState() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Index != mCurrentTweakIndex)
|
if (Index != mCurrentTweakIndex)
|
||||||
{
|
{
|
||||||
CSetTweakIndexCommand* pCommand = new CSetTweakIndexCommand(this, mCurrentTweakIndex, Index);
|
CSetTweakIndexCommand* pCommand = new CSetTweakIndexCommand(this, mCurrentTweakIndex, Index);
|
||||||
|
|
|
@ -205,7 +205,8 @@ HEADERS += \
|
||||||
StringEditor/CStringDelegate.h \
|
StringEditor/CStringDelegate.h \
|
||||||
CCustomDelegate.h \
|
CCustomDelegate.h \
|
||||||
CTweakEditor.h \
|
CTweakEditor.h \
|
||||||
Undo/CEditIntrinsicPropertyCommand.h
|
Undo/CEditIntrinsicPropertyCommand.h \
|
||||||
|
Undo/TSerializeUndoCommand.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
|
|
@ -3,12 +3,43 @@
|
||||||
|
|
||||||
#include "CStringDelegate.h"
|
#include "CStringDelegate.h"
|
||||||
#include "Editor/UICommon.h"
|
#include "Editor/UICommon.h"
|
||||||
|
#include "Editor/Undo/TSerializeUndoCommand.h"
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QShortcut>
|
||||||
|
|
||||||
/** Settings strings */
|
/** Settings strings */
|
||||||
const char* gkpLanguageSetting = "StringEditor/EditLanguage";
|
const char* gkpLanguageSetting = "StringEditor/EditLanguage";
|
||||||
|
|
||||||
|
/** Command classes */
|
||||||
|
class CSetStringIndexCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
CStringEditor* mpEditor;
|
||||||
|
int mOldIndex, mNewIndex;
|
||||||
|
public:
|
||||||
|
CSetStringIndexCommand(CStringEditor* pEditor, int OldIndex, int NewIndex)
|
||||||
|
: mpEditor(pEditor), mOldIndex(OldIndex), mNewIndex(NewIndex)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void undo() override { mpEditor->SetActiveString(mOldIndex); }
|
||||||
|
virtual void redo() override { mpEditor->SetActiveString(mNewIndex); }
|
||||||
|
virtual bool AffectsCleanState() const override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSetLanguageCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
CStringEditor* mpEditor;
|
||||||
|
ELanguage mOldLanguage, mNewLanguage;
|
||||||
|
public:
|
||||||
|
CSetLanguageCommand(CStringEditor* pEditor, ELanguage OldLanguage, ELanguage NewLanguage)
|
||||||
|
: mpEditor(pEditor), mOldLanguage(OldLanguage), mNewLanguage(NewLanguage)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void undo() override { mpEditor->SetActiveLanguage(mOldLanguage); }
|
||||||
|
virtual void redo() override { mpEditor->SetActiveLanguage(mNewLanguage); }
|
||||||
|
virtual bool AffectsCleanState() const override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
CStringEditor::CStringEditor(CStringTable* pStringTable, QWidget* pParent)
|
CStringEditor::CStringEditor(CStringTable* pStringTable, QWidget* pParent)
|
||||||
: IEditor(pParent)
|
: IEditor(pParent)
|
||||||
|
@ -16,12 +47,12 @@ CStringEditor::CStringEditor(CStringTable* pStringTable, QWidget* pParent)
|
||||||
, mpStringTable(pStringTable)
|
, mpStringTable(pStringTable)
|
||||||
, mCurrentLanguage(ELanguage::English)
|
, mCurrentLanguage(ELanguage::English)
|
||||||
, mCurrentStringIndex(0)
|
, mCurrentStringIndex(0)
|
||||||
|
, mCurrentStringCount(0)
|
||||||
|
, mIsEditingStringName(false)
|
||||||
|
, mIsEditingStringData(false)
|
||||||
{
|
{
|
||||||
mpListModel = new CStringListModel(pStringTable, this);
|
|
||||||
mpUI->setupUi(this);
|
|
||||||
|
|
||||||
InitUI();
|
InitUI();
|
||||||
LoadSettings();
|
// LoadSettings(); // Disabled for now
|
||||||
}
|
}
|
||||||
|
|
||||||
CStringEditor::~CStringEditor()
|
CStringEditor::~CStringEditor()
|
||||||
|
@ -29,10 +60,42 @@ CStringEditor::~CStringEditor()
|
||||||
delete mpUI;
|
delete mpUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CStringEditor::eventFilter(QObject* pWatched, QEvent* pEvent)
|
||||||
|
{
|
||||||
|
if (pEvent->type() == QEvent::FocusOut)
|
||||||
|
{
|
||||||
|
if (pWatched == mpUI->StringNameLineEdit && mIsEditingStringName)
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new TSerializeUndoCommand<CStringTable>("Edit String Name", mpStringTable, true);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
mIsEditingStringName = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (pWatched == mpUI->StringTextEdit && mIsEditingStringData)
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new TSerializeUndoCommand<CStringTable>("Edit String", mpStringTable, true);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
mIsEditingStringData = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CStringEditor::InitUI()
|
void CStringEditor::InitUI()
|
||||||
{
|
{
|
||||||
|
mpUI->setupUi(this);
|
||||||
|
mpListModel = new CStringListModel(mpStringTable, this);
|
||||||
mpUI->StringNameListView->setModel(mpListModel);
|
mpUI->StringNameListView->setModel(mpListModel);
|
||||||
mpUI->StringNameListView->setItemDelegate( new CStringDelegate(this) );
|
mpUI->StringNameListView->setItemDelegate( new CStringDelegate(this) );
|
||||||
|
mpUI->AddStringButton->setShortcut( QKeySequence("Alt+=") );
|
||||||
|
mpUI->RemoveStringButton->setShortcut( QKeySequence("Alt+-") );
|
||||||
|
|
||||||
|
// Register shortcuts
|
||||||
|
new QShortcut( QKeySequence("Alt+Down"), this, SLOT(IncrementStringIndex()) );
|
||||||
|
new QShortcut( QKeySequence("Alt+Up"), this, SLOT(DecrementStringIndex()) );
|
||||||
|
new QShortcut( QKeySequence("Alt+Right"), this, SLOT(IncrementLanguageIndex()) );
|
||||||
|
new QShortcut( QKeySequence("Alt+Left"), this, SLOT(DecrementLanguageIndex()) );
|
||||||
|
|
||||||
// Set up language tabs
|
// Set up language tabs
|
||||||
mpUI->EditLanguageTabBar->setExpanding(false);
|
mpUI->EditLanguageTabBar->setExpanding(false);
|
||||||
|
@ -48,7 +111,20 @@ void CStringEditor::InitUI()
|
||||||
connect( mpUI->StringNameListView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
connect( mpUI->StringNameListView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||||
this, SLOT(OnStringSelected(QModelIndex)) );
|
this, SLOT(OnStringSelected(QModelIndex)) );
|
||||||
|
|
||||||
|
connect( mpUI->StringNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(OnStringNameEdited()) );
|
||||||
|
connect( mpUI->StringTextEdit, SIGNAL(textChanged()), this, SLOT(OnStringTextEdited()) );
|
||||||
connect( mpUI->EditLanguageTabBar, SIGNAL(currentChanged(int)), this, SLOT(OnLanguageChanged(int)) );
|
connect( mpUI->EditLanguageTabBar, SIGNAL(currentChanged(int)), this, SLOT(OnLanguageChanged(int)) );
|
||||||
|
connect( mpUI->AddStringButton, SIGNAL(pressed()), this, SLOT(OnAddString()) );
|
||||||
|
connect( mpUI->RemoveStringButton, SIGNAL(pressed()), this, SLOT(OnRemoveString()) );
|
||||||
|
|
||||||
|
connect( &UndoStack(), SIGNAL(indexChanged(int)), this, SLOT(UpdateUI()) );
|
||||||
|
|
||||||
|
mpUI->ToolBar->addSeparator();
|
||||||
|
AddUndoActions(mpUI->ToolBar);
|
||||||
|
|
||||||
|
// Install event filters
|
||||||
|
mpUI->StringNameLineEdit->installEventFilter(this);
|
||||||
|
mpUI->StringTextEdit->installEventFilter(this);
|
||||||
|
|
||||||
// Update window title
|
// Update window title
|
||||||
QString WindowTitle = "%APP_FULL_NAME% - String Editor - %1[*]";
|
QString WindowTitle = "%APP_FULL_NAME% - String Editor - %1[*]";
|
||||||
|
@ -60,8 +136,8 @@ void CStringEditor::InitUI()
|
||||||
SplitterSizes << (height() * 0.95) << (height() * 0.05);
|
SplitterSizes << (height() * 0.95) << (height() * 0.05);
|
||||||
mpUI->splitter->setSizes(SplitterSizes);
|
mpUI->splitter->setSizes(SplitterSizes);
|
||||||
|
|
||||||
// Initialize status bar
|
// Initialize UI
|
||||||
UpdateStatusBar();
|
UpdateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStringEditor::UpdateStatusBar()
|
void CStringEditor::UpdateStatusBar()
|
||||||
|
@ -86,9 +162,8 @@ void CStringEditor::SetActiveLanguage(ELanguage Language)
|
||||||
if (mCurrentLanguage != Language)
|
if (mCurrentLanguage != Language)
|
||||||
{
|
{
|
||||||
mCurrentLanguage = Language;
|
mCurrentLanguage = Language;
|
||||||
|
mpListModel->SetPreviewLanguage(Language);
|
||||||
// Force UI to update with the correct string for the new language
|
UpdateUI();
|
||||||
SetActiveString( mCurrentStringIndex );
|
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,11 +171,7 @@ void CStringEditor::SetActiveLanguage(ELanguage Language)
|
||||||
void CStringEditor::SetActiveString(int StringIndex)
|
void CStringEditor::SetActiveString(int StringIndex)
|
||||||
{
|
{
|
||||||
mCurrentStringIndex = StringIndex;
|
mCurrentStringIndex = StringIndex;
|
||||||
TString StringName = mpStringTable->StringNameByIndex(mCurrentStringIndex);
|
UpdateUI();
|
||||||
TString StringData = mpStringTable->GetString(mCurrentLanguage, mCurrentStringIndex);
|
|
||||||
mpUI->StringNameLineEdit->setText( TO_QSTRING(StringName) );
|
|
||||||
mpUI->StringTextEdit->setPlainText( TO_QSTRING(StringData) );
|
|
||||||
UpdateStatusBar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStringEditor::LoadSettings()
|
void CStringEditor::LoadSettings()
|
||||||
|
@ -108,12 +179,13 @@ void CStringEditor::LoadSettings()
|
||||||
QSettings Settings;
|
QSettings Settings;
|
||||||
|
|
||||||
// Set language
|
// Set language
|
||||||
mCurrentLanguage = (ELanguage) Settings.value(gkpLanguageSetting, (int) ELanguage::English).toInt();
|
ELanguage Language = (ELanguage) Settings.value(gkpLanguageSetting, (int) ELanguage::English).toInt();
|
||||||
|
|
||||||
for (uint LanguageIdx = 0; LanguageIdx < mpStringTable->NumLanguages(); LanguageIdx++)
|
for (uint LanguageIdx = 0; LanguageIdx < mpStringTable->NumLanguages(); LanguageIdx++)
|
||||||
{
|
{
|
||||||
if (mpStringTable->LanguageByIndex(LanguageIdx) == mCurrentLanguage)
|
if (mpStringTable->LanguageByIndex(LanguageIdx) == Language)
|
||||||
{
|
{
|
||||||
|
SetActiveLanguage(Language);
|
||||||
mpUI->EditLanguageTabBar->setCurrentIndex(LanguageIdx);
|
mpUI->EditLanguageTabBar->setCurrentIndex(LanguageIdx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -127,14 +199,205 @@ void CStringEditor::SaveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Slots */
|
/** Slots */
|
||||||
|
void CStringEditor::UpdateUI()
|
||||||
|
{
|
||||||
|
// Update string list
|
||||||
|
if (mCurrentStringCount != mpStringTable->NumStrings())
|
||||||
|
{
|
||||||
|
mpUI->StringNameListView->model()->layoutChanged();
|
||||||
|
mCurrentStringCount = mpStringTable->NumStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update selection in string list
|
||||||
|
QModelIndex OldStringIndex = mpUI->StringNameListView->selectionModel()->hasSelection() ?
|
||||||
|
mpUI->StringNameListView->currentIndex() : QModelIndex();
|
||||||
|
|
||||||
|
QModelIndex NewStringIndex = mpUI->StringNameListView->model()->index(mCurrentStringIndex,0);
|
||||||
|
|
||||||
|
if (OldStringIndex != NewStringIndex)
|
||||||
|
{
|
||||||
|
QItemSelectionModel* pSelectionModel = mpUI->StringNameListView->selectionModel();
|
||||||
|
pSelectionModel->blockSignals(true);
|
||||||
|
pSelectionModel->setCurrentIndex(NewStringIndex, QItemSelectionModel::ClearAndSelect);
|
||||||
|
pSelectionModel->blockSignals(false);
|
||||||
|
mpUI->StringNameListView->update(OldStringIndex);
|
||||||
|
}
|
||||||
|
mpUI->StringNameListView->update(NewStringIndex);
|
||||||
|
|
||||||
|
// Update language tabs
|
||||||
|
uint LanguageIndex = mpUI->EditLanguageTabBar->currentIndex();
|
||||||
|
ELanguage TabLanguage = mpStringTable->LanguageByIndex(LanguageIndex);
|
||||||
|
|
||||||
|
if (TabLanguage != mCurrentLanguage)
|
||||||
|
{
|
||||||
|
for (uint LangIdx = 0; LangIdx < mpStringTable->NumLanguages(); LangIdx++)
|
||||||
|
{
|
||||||
|
if (mpStringTable->LanguageByIndex(LangIdx) == mCurrentLanguage)
|
||||||
|
{
|
||||||
|
mpUI->EditLanguageTabBar->blockSignals(true);
|
||||||
|
mpUI->EditLanguageTabBar->setCurrentIndex(LangIdx);
|
||||||
|
mpUI->EditLanguageTabBar->blockSignals(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update string name/data fields
|
||||||
|
QString StringName = TO_QSTRING( mpStringTable->StringNameByIndex(mCurrentStringIndex) );
|
||||||
|
QString StringData = TO_QSTRING( mpStringTable->GetString(mCurrentLanguage, mCurrentStringIndex) );
|
||||||
|
|
||||||
|
if (StringName != mpUI->StringNameLineEdit->text())
|
||||||
|
{
|
||||||
|
mpUI->StringNameLineEdit->blockSignals(true);
|
||||||
|
mpUI->StringNameLineEdit->setText( StringName );
|
||||||
|
mpUI->StringNameLineEdit->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringData != mpUI->StringTextEdit->toPlainText())
|
||||||
|
{
|
||||||
|
mpUI->StringTextEdit->blockSignals(true);
|
||||||
|
mpUI->StringTextEdit->setPlainText( StringData );
|
||||||
|
mpUI->StringTextEdit->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatusBar();
|
||||||
|
}
|
||||||
|
|
||||||
void CStringEditor::OnStringSelected(const QModelIndex& kIndex)
|
void CStringEditor::OnStringSelected(const QModelIndex& kIndex)
|
||||||
{
|
{
|
||||||
SetActiveString( kIndex.row() );
|
if (mCurrentStringIndex != kIndex.row())
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new CSetStringIndexCommand(this, mCurrentStringIndex, kIndex.row());
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStringEditor::OnLanguageChanged(int LanguageIndex)
|
void CStringEditor::OnLanguageChanged(int LanguageIndex)
|
||||||
{
|
{
|
||||||
ASSERT( LanguageIndex >= 0 && LanguageIndex < (int) mpStringTable->NumLanguages() );
|
ASSERT( LanguageIndex >= 0 && LanguageIndex < (int) mpStringTable->NumLanguages() );
|
||||||
ELanguage Language = mpStringTable->LanguageByIndex(LanguageIndex);
|
ELanguage Language = mpStringTable->LanguageByIndex(LanguageIndex);
|
||||||
SetActiveLanguage(Language);
|
|
||||||
|
if (Language != mCurrentLanguage)
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new CSetLanguageCommand(this, mCurrentLanguage, Language);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::OnStringNameEdited()
|
||||||
|
{
|
||||||
|
TString NewName = TO_TSTRING( mpUI->StringNameLineEdit->text() );
|
||||||
|
|
||||||
|
if (mpStringTable->StringNameByIndex(mCurrentStringIndex) != NewName)
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new TSerializeUndoCommand<CStringTable>("Edit String Name", mpStringTable, false);
|
||||||
|
mpStringTable->SetStringName( mCurrentStringIndex, NewName );
|
||||||
|
mIsEditingStringName = true;
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::OnStringTextEdited()
|
||||||
|
{
|
||||||
|
TString NewText = TO_TSTRING( mpUI->StringTextEdit->toPlainText() );
|
||||||
|
|
||||||
|
if (mpStringTable->GetString(mCurrentLanguage, mCurrentStringIndex) != NewText)
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new TSerializeUndoCommand<CStringTable>("Edit String", mpStringTable, false);
|
||||||
|
mpStringTable->SetString(mCurrentLanguage, mCurrentStringIndex, NewText);
|
||||||
|
mIsEditingStringData = true;
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::OnAddString()
|
||||||
|
{
|
||||||
|
UndoStack().beginMacro("Add String");
|
||||||
|
|
||||||
|
// Add string
|
||||||
|
IUndoCommand* pCommand = new TSerializeUndoCommand<CStringTable>("Add String", mpStringTable, true);
|
||||||
|
uint Index = mCurrentStringIndex + 1;
|
||||||
|
mpStringTable->AddString(Index);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
|
||||||
|
// Select new string
|
||||||
|
pCommand = new CSetStringIndexCommand(this, mCurrentStringIndex, Index);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
UndoStack().endMacro();
|
||||||
|
|
||||||
|
// Give focus to the text edit so the user can edit the string
|
||||||
|
mpUI->StringTextEdit->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::OnRemoveString()
|
||||||
|
{
|
||||||
|
if (mpUI->StringNameListView->selectionModel()->hasSelection())
|
||||||
|
{
|
||||||
|
UndoStack().beginMacro("Remove String");
|
||||||
|
uint Index = mCurrentStringIndex;
|
||||||
|
|
||||||
|
// Change selection to a new string.
|
||||||
|
// Do this before actually removing the string so that if the action is undone, the
|
||||||
|
// editor will not attempt to re-select the string before it gets readded to the table.
|
||||||
|
uint NewStringCount = mpStringTable->NumStrings() - 1;
|
||||||
|
uint NewIndex = (Index >= NewStringCount ? NewStringCount - 1 : Index);
|
||||||
|
IUndoCommand* pCommand = new CSetStringIndexCommand(this, Index, NewIndex);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
|
||||||
|
// Remove the string
|
||||||
|
pCommand = new TSerializeUndoCommand<CStringTable>("Remove String", mpStringTable, true);
|
||||||
|
mpStringTable->RemoveString(Index);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
UndoStack().endMacro();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::IncrementStringIndex()
|
||||||
|
{
|
||||||
|
uint NewIndex = mCurrentStringIndex + 1;
|
||||||
|
|
||||||
|
if (NewIndex < mpStringTable->NumStrings())
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new CSetStringIndexCommand(this, mCurrentStringIndex, NewIndex);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::DecrementStringIndex()
|
||||||
|
{
|
||||||
|
uint NewIndex = mCurrentStringIndex - 1;
|
||||||
|
|
||||||
|
if (NewIndex != -1)
|
||||||
|
{
|
||||||
|
IUndoCommand* pCommand = new CSetStringIndexCommand(this, mCurrentStringIndex, NewIndex);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::IncrementLanguageIndex()
|
||||||
|
{
|
||||||
|
for (uint i=0; i<mpStringTable->NumLanguages() - 1; i++)
|
||||||
|
{
|
||||||
|
if (mpStringTable->LanguageByIndex(i) == mCurrentLanguage)
|
||||||
|
{
|
||||||
|
ELanguage NewLanguage = mpStringTable->LanguageByIndex(i+1);
|
||||||
|
IUndoCommand* pCommand = new CSetLanguageCommand(this, mCurrentLanguage, NewLanguage);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStringEditor::DecrementLanguageIndex()
|
||||||
|
{
|
||||||
|
for (uint i=mpStringTable->NumLanguages() - 1; i>0; i--)
|
||||||
|
{
|
||||||
|
if (mpStringTable->LanguageByIndex(i) == mCurrentLanguage)
|
||||||
|
{
|
||||||
|
ELanguage NewLanguage = mpStringTable->LanguageByIndex(i-1);
|
||||||
|
IUndoCommand* pCommand = new CSetLanguageCommand(this, mCurrentLanguage, NewLanguage);
|
||||||
|
UndoStack().push(pCommand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,22 @@ class CStringEditor : public IEditor
|
||||||
/** Index of the string being edited */
|
/** Index of the string being edited */
|
||||||
uint mCurrentStringIndex;
|
uint mCurrentStringIndex;
|
||||||
|
|
||||||
|
/** Current string count */
|
||||||
|
uint mCurrentStringCount;
|
||||||
|
|
||||||
/** Model for the string list view */
|
/** Model for the string list view */
|
||||||
CStringListModel* mpListModel;
|
CStringListModel* mpListModel;
|
||||||
|
|
||||||
|
/** Editor state flags */
|
||||||
|
bool mIsEditingStringName;
|
||||||
|
bool mIsEditingStringData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CStringEditor(CStringTable* pStringTable, QWidget* pParent = 0);
|
explicit CStringEditor(CStringTable* pStringTable, QWidget* pParent = 0);
|
||||||
~CStringEditor();
|
~CStringEditor();
|
||||||
|
|
||||||
|
bool eventFilter(QObject* pWatched, QEvent* pEvent);
|
||||||
|
|
||||||
void InitUI();
|
void InitUI();
|
||||||
void UpdateStatusBar();
|
void UpdateStatusBar();
|
||||||
void SetActiveLanguage(ELanguage Language);
|
void SetActiveLanguage(ELanguage Language);
|
||||||
|
@ -43,9 +53,26 @@ public:
|
||||||
void LoadSettings();
|
void LoadSettings();
|
||||||
void SaveSettings();
|
void SaveSettings();
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
inline CStringTable* StringTable() const { return mpStringTable; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void UpdateUI();
|
||||||
void OnStringSelected(const QModelIndex& kIndex);
|
void OnStringSelected(const QModelIndex& kIndex);
|
||||||
void OnLanguageChanged(int LanguageIndex);
|
void OnLanguageChanged(int LanguageIndex);
|
||||||
|
void OnStringNameEdited();
|
||||||
|
void OnStringTextEdited();
|
||||||
|
void OnAddString();
|
||||||
|
void OnRemoveString();
|
||||||
|
|
||||||
|
void IncrementStringIndex();
|
||||||
|
void DecrementStringIndex();
|
||||||
|
void IncrementLanguageIndex();
|
||||||
|
void DecrementLanguageIndex();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void StringNameEdited();
|
||||||
|
void StringEdited();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CSTRINGEDITOR_H
|
#endif // CSTRINGEDITOR_H
|
||||||
|
|
|
@ -44,6 +44,15 @@
|
||||||
<pointsize>9</pointsize>
|
<pointsize>9</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="dragEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode">
|
||||||
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
|
</property>
|
||||||
|
<property name="defaultDropAction">
|
||||||
|
<enum>Qt::MoveAction</enum>
|
||||||
|
</property>
|
||||||
<property name="alternatingRowColors">
|
<property name="alternatingRowColors">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -108,7 +117,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="AddStringButton_2">
|
<widget class="QPushButton" name="RemoveStringButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
#ifndef TSERIALIZEUNDOCOMMAND_H
|
||||||
|
#define TSERIALIZEUNDOCOMMAND_H
|
||||||
|
|
||||||
|
#include "IUndoCommand.h"
|
||||||
|
#include <Common/Common.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo command that works by restoring the full state of an object
|
||||||
|
* on undo/redo. To use, create the command object, apply the change
|
||||||
|
* you want to make to the object, and then push the command.
|
||||||
|
*
|
||||||
|
* Commands with IsActionComplete=false will be merged.
|
||||||
|
* To prevent merging, push a final command with IsActionComplete=true.
|
||||||
|
*
|
||||||
|
* The current implementatation is "dumb" and just saves and restores
|
||||||
|
* the entire object, including parameters that have not changed. Due
|
||||||
|
* to this, it could be memory-intensive and not super performant.
|
||||||
|
* As a result, it's probably not a good idea to use this on very
|
||||||
|
* large objects right now.
|
||||||
|
*/
|
||||||
|
template<typename ObjectT>
|
||||||
|
class TSerializeUndoCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
ObjectT* mpObject;
|
||||||
|
std::vector<char> mOldData;
|
||||||
|
std::vector<char> mNewData;
|
||||||
|
bool mIsActionComplete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TSerializeUndoCommand(const QString& kText, ObjectT* pObject, bool IsActionComplete)
|
||||||
|
: IUndoCommand(kText)
|
||||||
|
, mpObject(pObject)
|
||||||
|
, mIsActionComplete(IsActionComplete)
|
||||||
|
{
|
||||||
|
// Save old state of object
|
||||||
|
CVectorOutStream Out(&mOldData, EEndian::SystemEndian);
|
||||||
|
CBasicBinaryWriter Writer(&Out, 0, EGame::Invalid);
|
||||||
|
mpObject->Serialize(Writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** IUndoCommand interface */
|
||||||
|
virtual int id() const override
|
||||||
|
{
|
||||||
|
return FOURCC('TSUC');
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
// Restore old state of object
|
||||||
|
CMemoryInStream In(&mOldData[0], mOldData.size(), EEndian::SystemEndian);
|
||||||
|
CBasicBinaryReader Reader(&In, CSerialVersion(0,0,EGame::Invalid));
|
||||||
|
mpObject->Serialize(Reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void redo() override
|
||||||
|
{
|
||||||
|
// First call when command is pushed - save new state of object
|
||||||
|
if (mNewData.empty())
|
||||||
|
{
|
||||||
|
CVectorOutStream Out(&mNewData, EEndian::SystemEndian);
|
||||||
|
CBasicBinaryWriter Writer(&Out, 0, EGame::Invalid);
|
||||||
|
mpObject->Serialize(Writer);
|
||||||
|
|
||||||
|
// Obsolete command if memory buffers match
|
||||||
|
if (mIsActionComplete)
|
||||||
|
{
|
||||||
|
if (mOldData.size() == mNewData.size())
|
||||||
|
{
|
||||||
|
if (memcmp(mOldData.data(), mNewData.data(), mNewData.size()) == 0)
|
||||||
|
{
|
||||||
|
setObsolete(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Subsequent calls - restore new state of object
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CMemoryInStream In(&mNewData[0], mNewData.size(), EEndian::SystemEndian);
|
||||||
|
CBasicBinaryReader Reader(&In, CSerialVersion(0,0,EGame::Invalid));
|
||||||
|
mpObject->Serialize(Reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool mergeWith(const QUndoCommand* pkOther) override
|
||||||
|
{
|
||||||
|
if (!mIsActionComplete && pkOther->id() == id())
|
||||||
|
{
|
||||||
|
const TSerializeUndoCommand* pkSerializeCommand =
|
||||||
|
static_cast<const TSerializeUndoCommand*>(pkOther);
|
||||||
|
|
||||||
|
mNewData = pkSerializeCommand->mNewData;
|
||||||
|
mIsActionComplete = pkSerializeCommand->mIsActionComplete;
|
||||||
|
|
||||||
|
// Obsolete command if memory buffers match
|
||||||
|
if (mIsActionComplete)
|
||||||
|
{
|
||||||
|
if (mOldData.size() == mNewData.size())
|
||||||
|
{
|
||||||
|
if (memcmp(mOldData.data(), mNewData.data(), mNewData.size()) == 0)
|
||||||
|
{
|
||||||
|
setObsolete(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool AffectsCleanState() const override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TSERIALIZEUNDOCOMMAND_H
|
Loading…
Reference in New Issue