forked from Mirrors/openclonk
Editor: Add localized string support
parent
52caf696e6
commit
4fac960cf4
|
@ -758,6 +758,9 @@ if(WITH_QT_EDITOR)
|
|||
src/editor/C4ConsoleQtNewScenario.cpp
|
||||
src/editor/C4ConsoleQtNewScenario.h
|
||||
src/editor/C4ConsoleQtNewScenario.ui
|
||||
src/editor/C4ConsoleQtLocalizeString.cpp
|
||||
src/editor/C4ConsoleQtLocalizeString.h
|
||||
src/editor/C4ConsoleQtLocalizeString.ui
|
||||
src/editor/C4ConsoleQtMainWindow.ui
|
||||
src/editor/resource.qrc
|
||||
${qt_editor_resources}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE funcs
|
||||
SYSTEM '../../../clonk.dtd'>
|
||||
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
|
||||
<funcs>
|
||||
<func>
|
||||
<title>GetTranslatedString</title>
|
||||
<category>Script</category>
|
||||
<subcat>Strings</subcat>
|
||||
<version>8.0 OC</version>
|
||||
<syntax>
|
||||
<rtype>string</rtype>
|
||||
<params>
|
||||
<param>
|
||||
<type>any</type>
|
||||
<name>string_data</name>
|
||||
<desc>Either a string or a proplist containing multiple translations of a string. If a string or <code>nil</code> is passed, the parameter is returned directly. If a proplist is passed, the value corresponding to the selected language (or a fallback) is returned.</desc>
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Returns a string corresponding to the user's selected language. For <code>string_data</code>, the expected format is <code>{ Function="Translate", DE="Hallo, Welt", US="Hello, World"}</code>. If no matching entry is found or it is <code>nil</code>, then another language string is returned as a fallback.</desc>
|
||||
<examples>
|
||||
<example>
|
||||
<code>Log(GetTranslatedString({ Function="Translate", DE="Dies ist ein Test.", US="This is a test."}));</code>
|
||||
<text>Logs either "Dies ist ein Test." or "This is a test." depending on the player's language setting.</text>
|
||||
</example>
|
||||
<example>
|
||||
<code>local inscription = "";
|
||||
|
||||
// Players can read the sign via the interaction bar.
|
||||
public func IsInteractable() { return true; }
|
||||
|
||||
// Called on player interaction.
|
||||
public func Interact(object clonk)
|
||||
{
|
||||
if (!clonk) return false;
|
||||
Dialogue->MessageBox(GetTranslatedString(inscription), clonk, this, clonk->GetController(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public func SetInscription(to_text)
|
||||
{
|
||||
inscription = to_text ?? "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public func Definition(def)
|
||||
{
|
||||
// Inscription props
|
||||
if (!def.EditorProps) def.EditorProps = {};
|
||||
def.EditorProps.inscription = { Name="Inscription", Type="string", Set="SetInscription", Save="Inscription", Translatable=true };
|
||||
}</code>
|
||||
<text>Code for a signpost. The string editor property with setting <code>Translatable=true</code> provides a translation proplist in the correct format automatically.</text>
|
||||
</example>
|
||||
</examples>
|
||||
<related><funclink>Translate</funclink></related>
|
||||
</func>
|
||||
<author>Sven2</author><date>2017-05</date>
|
||||
</funcs>
|
|
@ -34,6 +34,7 @@ MsgOnFire3=Oops, I dropped my lighter!</code>
|
|||
<text>When the clonk catches fire, the engine calls Incineration() in the clonk and in this example, one of the four above messages is displayed at random.</text>
|
||||
</example>
|
||||
</examples>
|
||||
<related><funclink>GetTranslatedString</funclink></related>
|
||||
</func>
|
||||
<author>Isilkor</author><date>2009-11</date>
|
||||
<author>Newton</author><date>2011-06</date>
|
||||
|
|
|
@ -32,6 +32,8 @@ IDS_BTN_YES=Ja
|
|||
IDS_CHAT_NOTCONNECTED=nicht verbunden
|
||||
IDS_CHAT_SERVER=Server
|
||||
IDS_CNS_ACTION=Aktivität:
|
||||
IDS_CNS_ADDLANGUAGE=Sprache hinzufügen
|
||||
IDS_CNS_ADDLANGUAGEID=Sprach-ID (z.B. DE, US, FR)
|
||||
IDS_CNS_ALLOBJECTS=Alle Objekte
|
||||
IDS_CNS_ARRAYADD=Element hinzufügen
|
||||
IDS_CNS_ARRAYEDIT=Array
|
||||
|
@ -100,6 +102,7 @@ IDS_CNS_SHOWHELP=Hilfetexte anzeigen
|
|||
IDS_CNS_SHOWHELPTIP=Aktiviert oder deaktiviert Objektbeschreibungen und Tooltip-Marker (?) im Objekteigenschaftsdialog.
|
||||
IDS_CNS_TEMPLATE=Vorlage
|
||||
IDS_CNS_TITLE=Titel
|
||||
IDS_CNS_TRANSLATE=Übersetzung
|
||||
IDS_CNS_TRUE=Ja
|
||||
IDS_CNS_TYPE=Typ: %s (%s)
|
||||
IDS_CNS_VALUE=Wert
|
||||
|
@ -398,6 +401,7 @@ IDS_ERR_HELPCMD=Grundlegende Befehle im IRC-Chat:|/join [Chatraum] - Neuen Chatr
|
|||
IDS_ERR_INITFONTS=Fehler bei der Schriftinitialisierung
|
||||
IDS_ERR_INSUFFICIENTPARAMETERS=/%s: fehlende Parameter
|
||||
IDS_ERR_INVALIDCHANNELNAME=Kein gültiger Chat-Kanal.
|
||||
IDS_ERR_INVALIDLANGUAGEID=Ungültige Sprach-ID.
|
||||
IDS_ERR_INVALIDNICKNAME=Unzulässiger Kurzname.
|
||||
IDS_ERR_INVALIDNICKNAME2=/%s: unzulässiger Kurzname
|
||||
IDS_ERR_INVALIDPASSWORDMAX31CHARA=Nicht zulässiges Passwort: maximal 31 Zeichen, keine Leerzeichen.
|
||||
|
|
|
@ -32,6 +32,8 @@ IDS_BTN_YES=Yes
|
|||
IDS_CHAT_NOTCONNECTED=not connected
|
||||
IDS_CHAT_SERVER=Server
|
||||
IDS_CNS_ACTION=Action:
|
||||
IDS_CNS_ADDLANGUAGE=Add language
|
||||
IDS_CNS_ADDLANGUAGEID=Language ID (e.g. DE, US, FR)
|
||||
IDS_CNS_ALLOBJECTS=All objects
|
||||
IDS_CNS_ARRAYADD=Add item
|
||||
IDS_CNS_ARRAYEDIT=Array
|
||||
|
@ -100,6 +102,7 @@ IDS_CNS_SHOWHELP=Show help texts
|
|||
IDS_CNS_SHOWHELPTIP=Activates or deactivates object descriptions and tooltip markers (?) in the object property dialogue.
|
||||
IDS_CNS_TEMPLATE=Template
|
||||
IDS_CNS_TITLE=Title
|
||||
IDS_CNS_TRANSLATE=Translation
|
||||
IDS_CNS_TRUE=Yes
|
||||
IDS_CNS_TYPE=Type: %s (%s)
|
||||
IDS_CNS_VALUE=Value
|
||||
|
@ -398,6 +401,7 @@ IDS_ERR_HELPCMD=Basic commands in the IRC-chat:|/join [channel] - Enter a new ch
|
|||
IDS_ERR_INITFONTS=Error initializing fonts
|
||||
IDS_ERR_INSUFFICIENTPARAMETERS=/%s: insufficient parameters
|
||||
IDS_ERR_INVALIDCHANNELNAME=Invalid channel name.
|
||||
IDS_ERR_INVALIDLANGUAGEID=Invalid language ID.
|
||||
IDS_ERR_INVALIDNICKNAME=Invalid nickname.
|
||||
IDS_ERR_INVALIDNICKNAME2=/%s: invalid nick name
|
||||
IDS_ERR_INVALIDPASSWORDMAX31CHARA=Invalid password. Maximum 31 characters. No spaces allowed.
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2013, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
||||
* See accompanying file "TRADEMARK" for details.
|
||||
*
|
||||
* To redistribute this file separately, substitute the full license texts
|
||||
* for the above references.
|
||||
*/
|
||||
|
||||
/* String localization editors */
|
||||
|
||||
#include "C4Include.h"
|
||||
#include "script/C4Value.h"
|
||||
#include "config/C4Config.h"
|
||||
#include "editor/C4ConsoleQtLocalizeString.h"
|
||||
#include "c4group/C4Language.h"
|
||||
|
||||
|
||||
/* Single string editor */
|
||||
|
||||
C4ConsoleQtLocalizeStringDlg::C4ConsoleQtLocalizeStringDlg(class QMainWindow *parent_window, const C4Value &translations)
|
||||
: QDialog(parent_window, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint)
|
||||
, translations(translations)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
// Add language editors
|
||||
int32_t lang_index = 0;
|
||||
C4LanguageInfo *lang_info;
|
||||
while (lang_info = ::Languages.GetInfo(lang_index++))
|
||||
{
|
||||
AddEditor(lang_info->Code, lang_info->Name);
|
||||
}
|
||||
// Fill in values
|
||||
C4PropList *translations_proplist = translations.getPropList();
|
||||
assert(translations_proplist);
|
||||
for (C4String *lang_str : translations_proplist->GetSortedLocalProperties(false))
|
||||
{
|
||||
if (lang_str->GetData().getLength() == 2)
|
||||
{
|
||||
C4Value text_val;
|
||||
if (translations_proplist->GetPropertyByS(lang_str, &text_val))
|
||||
{
|
||||
C4String *text = text_val.getStr();
|
||||
if (text)
|
||||
{
|
||||
QLineEdit *editor = GetEditorByLanguage(lang_str->GetCStr());
|
||||
if (!editor)
|
||||
{
|
||||
// Unknown language. Just add an editor without language name.
|
||||
editor = AddEditor(lang_str->GetCStr(), nullptr);
|
||||
}
|
||||
editor->setText(QString(text->GetCStr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Size
|
||||
adjustSize();
|
||||
setMinimumSize(size());
|
||||
// Focus on first empty editor
|
||||
if (edited_languages.size())
|
||||
{
|
||||
edited_languages.front().value_editor->setFocus(); // fallback to first editor
|
||||
for (const auto & langs : edited_languages)
|
||||
{
|
||||
if (!langs.value_editor->text().length())
|
||||
{
|
||||
langs.value_editor->setFocus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void C4ConsoleQtLocalizeStringDlg::DoError(const char *msg)
|
||||
{
|
||||
QMessageBox::critical(this, ::LoadResStr("IDS_ERR_TITLE"), QString(msg));
|
||||
}
|
||||
|
||||
QLineEdit *C4ConsoleQtLocalizeStringDlg::AddEditor(const char *language, const char *language_name)
|
||||
{
|
||||
assert(!GetEditorByLanguage(const char *language));
|
||||
// Add editor widgets
|
||||
int32_t row = edited_languages.size();
|
||||
QString language_label_text(language);
|
||||
if (language_name) language_label_text.append(FormatString(" (%s)", language_name).getData());
|
||||
QLabel *language_label = new QLabel(language_label_text, this);
|
||||
ui.mainGrid->addWidget(language_label, row, 0);
|
||||
QLineEdit *value_editor = new QLineEdit(this);
|
||||
ui.mainGrid->addWidget(value_editor, row, 1);
|
||||
// Add to list
|
||||
EditedLanguage new_editor;
|
||||
SCopy(language, new_editor.language, 2);
|
||||
new_editor.value_editor = value_editor;
|
||||
edited_languages.push_back(new_editor);
|
||||
return value_editor;
|
||||
}
|
||||
|
||||
QLineEdit *C4ConsoleQtLocalizeStringDlg::GetEditorByLanguage(const char *language)
|
||||
{
|
||||
// Search text editor by language ID
|
||||
for (const auto & langs : edited_languages)
|
||||
{
|
||||
if (!strcmp(langs.language, language))
|
||||
{
|
||||
return langs.value_editor;
|
||||
}
|
||||
}
|
||||
// Not found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void C4ConsoleQtLocalizeStringDlg::done(int r)
|
||||
{
|
||||
if (QDialog::Accepted == r) // ok was pressed
|
||||
{
|
||||
C4PropList *translations_proplist = translations.getPropList();
|
||||
assert(translations_proplist);
|
||||
// Set all translations
|
||||
for (const auto & langs : edited_languages)
|
||||
{
|
||||
// Empty strings are set to nil, because that allows the user to set it to fallback
|
||||
QString text = langs.value_editor->text();
|
||||
if (text.length())
|
||||
{
|
||||
C4Value text_val = C4VString(text.toUtf8());
|
||||
translations_proplist->SetPropertyByS(::Strings.RegString(langs.language), text_val);
|
||||
}
|
||||
else
|
||||
{
|
||||
translations_proplist->ResetProperty(::Strings.RegString(langs.language));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Close
|
||||
QDialog::done(r);
|
||||
}
|
||||
|
||||
void C4ConsoleQtLocalizeStringDlg::AddLanguagePressed()
|
||||
{
|
||||
bool lang_ok = false;
|
||||
QRegExpValidator validator(QRegExp("^[a-zA-Z][a-zA-Z]$"), this);
|
||||
QString lang_id;
|
||||
while (!lang_ok)
|
||||
{
|
||||
bool ok; int q = 0;
|
||||
lang_id = QInputDialog::getText(this, LoadResStr("IDS_CNS_ADDLANGUAGE"), LoadResStr("IDS_CNS_ADDLANGUAGEID"), QLineEdit::Normal, QString(), &ok);
|
||||
if (!ok) return;
|
||||
lang_ok = (validator.validate(lang_id, q) == QValidator::Acceptable);
|
||||
if (!lang_ok)
|
||||
{
|
||||
DoError(LoadResStr("IDS_ERR_INVALIDLANGUAGEID"));
|
||||
}
|
||||
}
|
||||
// Either add or just focus existing editor
|
||||
QLineEdit *editor = GetEditorByLanguage(lang_id.toUtf8());
|
||||
if (!editor)
|
||||
{
|
||||
editor = AddEditor(lang_id.toUtf8(), nullptr);
|
||||
adjustSize();
|
||||
setMinimumSize(size());
|
||||
}
|
||||
editor->setFocus();
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2013, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
||||
* See accompanying file "TRADEMARK" for details.
|
||||
*
|
||||
* To redistribute this file separately, substitute the full license texts
|
||||
* for the above references.
|
||||
*/
|
||||
|
||||
/* String localization editors */
|
||||
|
||||
#ifndef INC_C4ConsoleQtLocalizeString
|
||||
#define INC_C4ConsoleQtLocalizeString
|
||||
#ifdef WITH_QT_EDITOR
|
||||
|
||||
#include "C4Include.h" // needed for automoc
|
||||
#include "editor/C4ConsoleGUI.h" // for glew.h
|
||||
#include "editor/C4ConsoleQt.h"
|
||||
#include "ui_C4ConsoleQtLocalizeString.h"
|
||||
|
||||
class C4ConsoleQtLocalizeStringDlg : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Ui::LocalizeStringDialog ui;
|
||||
C4Value translations;
|
||||
|
||||
struct EditedLanguage
|
||||
{
|
||||
char language[3];
|
||||
QLineEdit *value_editor;
|
||||
};
|
||||
std::list<EditedLanguage> edited_languages;
|
||||
|
||||
public:
|
||||
C4ConsoleQtLocalizeStringDlg(class QMainWindow *parent_window, const C4Value &translations);
|
||||
C4PropList *GetTranslations() const { return translations.getPropList(); }
|
||||
|
||||
private:
|
||||
void DoError(const char *msg);
|
||||
QLineEdit *AddEditor(const char *language, const char *language_name);
|
||||
QLineEdit *GetEditorByLanguage(const char *language);
|
||||
void done(int r) override;
|
||||
|
||||
protected slots:
|
||||
void AddLanguagePressed();
|
||||
};
|
||||
|
||||
#endif // WITH_QT_EDITOR
|
||||
#endif // INC_C4ConsoleQtLocalizeString
|
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LocalizeStringDialog</class>
|
||||
<widget class="QDialog" name="LocalizeStringDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>863</width>
|
||||
<height>89</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string comment="res">IDS_CNS_TRANSLATE</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="mainGrid">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addLanguageButton">
|
||||
<property name="text">
|
||||
<string comment="res">IDS_CNS_ADDLANGUAGE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>LocalizeStringDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>400</x>
|
||||
<y>282</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>LocalizeStringDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>addLanguageButton</sender>
|
||||
<signal>pressed()</signal>
|
||||
<receiver>LocalizeStringDialog</receiver>
|
||||
<slot>AddLanguagePressed()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>78</x>
|
||||
<y>270</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>242</x>
|
||||
<y>146</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>AddLanguagePressed()</slot>
|
||||
</slots>
|
||||
</ui>
|
|
@ -19,6 +19,7 @@
|
|||
#include "editor/C4ConsoleQtPropListViewer.h"
|
||||
#include "editor/C4ConsoleQtDefinitionListViewer.h"
|
||||
#include "editor/C4ConsoleQtState.h"
|
||||
#include "editor/C4ConsoleQtLocalizeString.h"
|
||||
#include "editor/C4Console.h"
|
||||
#include "object/C4Object.h"
|
||||
#include "object/C4GameObjects.h"
|
||||
|
@ -312,44 +313,191 @@ bool C4PropertyDelegateInt::IsPasteValid(const C4Value &val) const
|
|||
|
||||
/* String delegate */
|
||||
|
||||
C4PropertyDelegateStringEditor::C4PropertyDelegateStringEditor(QWidget *parent, bool has_localization_button)
|
||||
: QWidget(parent), edit(nullptr), localization_button(nullptr), commit_pending(false), text_edited(false)
|
||||
{
|
||||
auto layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
edit = new QLineEdit(this);
|
||||
layout->addWidget(edit);
|
||||
if (has_localization_button)
|
||||
{
|
||||
localization_button = new QPushButton(QString(LoadResStr("IDS_CNS_MORE")), this);
|
||||
layout->addWidget(localization_button);
|
||||
connect(localization_button, &QPushButton::pressed, this, [this]() {
|
||||
// Show dialogue
|
||||
OpenLocalizationDialogue();
|
||||
});
|
||||
}
|
||||
connect(edit, &QLineEdit::returnPressed, this, [this]() {
|
||||
text_edited = true;
|
||||
commit_pending = true;
|
||||
emit EditingDoneSignal();
|
||||
});
|
||||
connect(edit, &QLineEdit::textEdited, this, [this]() {
|
||||
text_edited = true;
|
||||
commit_pending = true;
|
||||
});
|
||||
}
|
||||
|
||||
void C4PropertyDelegateStringEditor::OpenLocalizationDialogue()
|
||||
{
|
||||
if (!localization_dialogue)
|
||||
{
|
||||
// Make sure we have an updated value
|
||||
StoreEditedText();
|
||||
// Make sure we're using a localized string
|
||||
if (value.GetType() != C4V_PropList)
|
||||
{
|
||||
C4PropList *value_proplist = ::Game.AllocateTranslatedString();
|
||||
if (value.GetType() == C4V_String)
|
||||
{
|
||||
C4String *lang = ::Strings.RegString(lang_code);
|
||||
value_proplist->SetPropertyByS(lang, value);
|
||||
}
|
||||
value = C4VPropList(value_proplist);
|
||||
}
|
||||
// Open dialogue on value
|
||||
localization_dialogue.reset(new C4ConsoleQtLocalizeStringDlg(::Console.GetState()->window.get(), value));
|
||||
connect(localization_dialogue.get(), &C4ConsoleQtLocalizeStringDlg::accepted, this, [this]() {
|
||||
// Usually, the proplist owned by localization_dialogue is the same as this->value
|
||||
// However, it may have changed if there was an update call that modified the value while the dialogue was open
|
||||
// In this case, take the value from the dialogue
|
||||
SetValue(C4VPropList(localization_dialogue->GetTranslations()));
|
||||
// Finish editing on the value
|
||||
CloseLocalizationDialogue();
|
||||
commit_pending = true;
|
||||
emit EditingDoneSignal();
|
||||
});
|
||||
connect(localization_dialogue.get(), &C4ConsoleQtLocalizeStringDlg::rejected, this, [this]() {
|
||||
CloseLocalizationDialogue();
|
||||
});
|
||||
localization_dialogue->show();
|
||||
}
|
||||
}
|
||||
|
||||
void C4PropertyDelegateStringEditor::CloseLocalizationDialogue()
|
||||
{
|
||||
if (localization_dialogue)
|
||||
{
|
||||
localization_dialogue->close();
|
||||
localization_dialogue.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void C4PropertyDelegateStringEditor::StoreEditedText()
|
||||
{
|
||||
if (text_edited)
|
||||
{
|
||||
// TODO: Would be better to handle escaping in the C4Value-to-string code
|
||||
QString new_value = edit->text();
|
||||
new_value = new_value.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
C4Value text_value = C4VString(new_value.toUtf8());
|
||||
// If translatable, always store as translation proplist
|
||||
// This makes it easier to collect strings to be localized in the localization overview
|
||||
if (localization_button)
|
||||
{
|
||||
C4PropList *value_proplist = this->value.getPropList();
|
||||
if (!value_proplist)
|
||||
{
|
||||
value_proplist = ::Game.AllocateTranslatedString();
|
||||
}
|
||||
C4String *lang = ::Strings.RegString(lang_code);
|
||||
value_proplist->SetPropertyByS(lang, text_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->value = text_value;
|
||||
}
|
||||
text_edited = false;
|
||||
}
|
||||
}
|
||||
|
||||
void C4PropertyDelegateStringEditor::SetValue(const C4Value &val)
|
||||
{
|
||||
// Set editor text to value
|
||||
// Resolve text string and default language for localized strings
|
||||
C4String *s;
|
||||
C4Value language;
|
||||
if (localization_button)
|
||||
{
|
||||
s = ::Game.GetTranslatedString(val, &language, true);
|
||||
C4String *language_string = language.getStr();
|
||||
SCopy(language_string ? language_string->GetCStr() : Config.General.LanguageEx, lang_code, 2);
|
||||
localization_button->setText(QString(lang_code));
|
||||
}
|
||||
else
|
||||
{
|
||||
s = val.getStr();
|
||||
}
|
||||
edit->setText(QString(s ? s->GetCStr() : ""));
|
||||
// Remember full value with all localizations
|
||||
if (val.GetType() == C4V_PropList)
|
||||
{
|
||||
if (val != this->value)
|
||||
{
|
||||
// Localization proplist: Create a copy (C4Value::Copy() would be nice)
|
||||
C4PropList *new_value_proplist = new C4PropListScript();
|
||||
this->value = C4VPropList(new_value_proplist);
|
||||
C4PropList *val_proplist = val.getPropList();
|
||||
for (C4String *lang : val_proplist->GetSortedLocalProperties())
|
||||
{
|
||||
C4Value lang_string;
|
||||
val_proplist->GetPropertyByS(lang, &lang_string);
|
||||
new_value_proplist->SetPropertyByS(lang, lang_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->value = val;
|
||||
}
|
||||
}
|
||||
|
||||
C4Value C4PropertyDelegateStringEditor::GetValue()
|
||||
{
|
||||
// Flush edits from the text field into value
|
||||
StoreEditedText();
|
||||
// Return current value
|
||||
return this->value;
|
||||
}
|
||||
|
||||
C4PropertyDelegateString::C4PropertyDelegateString(const C4PropertyDelegateFactory *factory, C4PropList *props)
|
||||
: C4PropertyDelegate(factory, props)
|
||||
{
|
||||
}
|
||||
|
||||
void C4PropertyDelegateString::SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const
|
||||
{
|
||||
Editor *line_edit = static_cast<Editor*>(editor);
|
||||
C4String *s = val.getStr();
|
||||
line_edit->setText(QString(s ? s->GetCStr() : ""));
|
||||
}
|
||||
|
||||
void C4PropertyDelegateString::SetModelData(QObject *editor, const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape) const
|
||||
{
|
||||
Editor *line_edit = static_cast<Editor*>(editor);
|
||||
// Only set model data when pressing Enter explicitely; not just when leaving
|
||||
if (line_edit->commit_pending)
|
||||
if (props)
|
||||
{
|
||||
QString new_value = line_edit->text();
|
||||
// TODO: Would be better to handle escaping in the C4Value-to-string code
|
||||
new_value = new_value.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
property_path.SetProperty(C4VString(new_value.toUtf8()));
|
||||
translatable = props->GetPropertyBool(P_Translatable);
|
||||
}
|
||||
}
|
||||
|
||||
void C4PropertyDelegateString::SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const
|
||||
{
|
||||
Editor *editor = static_cast<Editor*>(aeditor);
|
||||
editor->SetValue(val);
|
||||
}
|
||||
|
||||
void C4PropertyDelegateString::SetModelData(QObject *aeditor, const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape) const
|
||||
{
|
||||
Editor *editor = static_cast<Editor*>(aeditor);
|
||||
// Only set model data when pressing Enter explicitely; not just when leaving
|
||||
if (editor->IsCommitPending())
|
||||
{
|
||||
property_path.SetProperty(editor->GetValue());
|
||||
factory->GetPropertyModel()->DoOnUpdateCall(property_path, this);
|
||||
line_edit->commit_pending = false;
|
||||
editor->SetCommitPending(false);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *C4PropertyDelegateString::CreateEditor(const C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const
|
||||
{
|
||||
Editor *editor = new Editor(parent);
|
||||
Editor *editor = new Editor(parent, translatable);
|
||||
// EditingDone on return or when leaving edit field after a change has been made
|
||||
connect(editor, &QLineEdit::returnPressed, editor, [this, editor]() {
|
||||
editor->commit_pending = true;
|
||||
connect(editor, &Editor::EditingDoneSignal, editor, [this, editor]() {
|
||||
emit EditingDoneSignal(editor);
|
||||
});
|
||||
connect(editor, &QLineEdit::textEdited, this, [editor, this]() {
|
||||
editor->commit_pending = true;
|
||||
});
|
||||
// Selection in child enum: Direct focus
|
||||
if (by_selection && is_child) editor->setFocus();
|
||||
return editor;
|
||||
|
@ -358,15 +506,23 @@ QWidget *C4PropertyDelegateString::CreateEditor(const C4PropertyDelegateFactory
|
|||
QString C4PropertyDelegateString::GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const
|
||||
{
|
||||
// Raw string without ""
|
||||
C4String *s = v.getStr();
|
||||
C4String *s = translatable ? ::Game.GetTranslatedString(v, nullptr, true) : v.getStr();
|
||||
return QString(s ? s->GetCStr() : "");
|
||||
}
|
||||
|
||||
bool C4PropertyDelegateString::IsPasteValid(const C4Value &val) const
|
||||
{
|
||||
// Check string type
|
||||
if (val.GetType() != C4V_String) return false;
|
||||
return true;
|
||||
// Check string type or translatable proplist
|
||||
if (val.GetType() == C4V_String) return true;
|
||||
if (translatable)
|
||||
{
|
||||
C4PropList *val_p = val.getPropList();
|
||||
if (val_p)
|
||||
{
|
||||
return val_p->GetPropertyStr(P_Function) == &::Strings.P[P_Translate];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -125,15 +125,35 @@ public:
|
|||
bool IsPasteValid(const C4Value &val) const override;
|
||||
};
|
||||
|
||||
class C4PropertyDelegateStringEditor : public QLineEdit
|
||||
class C4PropertyDelegateStringEditor : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QLineEdit *edit;
|
||||
QPushButton *localization_button;
|
||||
bool text_edited, commit_pending;
|
||||
C4Value value;
|
||||
char lang_code[3];
|
||||
C4Value base_proplist;
|
||||
std::unique_ptr<class C4ConsoleQtLocalizeStringDlg> localization_dialogue;
|
||||
|
||||
void OpenLocalizationDialogue();
|
||||
void CloseLocalizationDialogue();
|
||||
void StoreEditedText();
|
||||
public:
|
||||
C4PropertyDelegateStringEditor(QWidget *parent) : QLineEdit(parent), commit_pending(false) {}
|
||||
bool commit_pending;
|
||||
C4PropertyDelegateStringEditor(QWidget *parent, bool has_localization_button);
|
||||
void SetValue(const C4Value &val);
|
||||
C4Value GetValue();
|
||||
bool IsCommitPending() const { return commit_pending; }
|
||||
void SetCommitPending(bool to_val) { commit_pending = to_val; }
|
||||
signals:
|
||||
void EditingDoneSignal() const;
|
||||
};
|
||||
|
||||
class C4PropertyDelegateString : public C4PropertyDelegate
|
||||
{
|
||||
private:
|
||||
bool translatable;
|
||||
public:
|
||||
typedef C4PropertyDelegateStringEditor Editor;
|
||||
|
||||
|
|
|
@ -3890,3 +3890,83 @@ void C4Game::SetGlobalSoundModifier(C4PropList *new_modifier)
|
|||
}
|
||||
::Application.SoundSystem.Modifiers.SetGlobalModifier(mod, NO_OWNER);
|
||||
}
|
||||
|
||||
C4String *C4Game::GetTranslatedString(const C4Value &input_string, C4Value *selected_language, bool fail_silently) const
|
||||
{
|
||||
// Resolve a localized string
|
||||
// If a string is passed, just return it
|
||||
// If a proplist like { DE="Hallo, Welt!", US="Hello, world!" } is passed, return the string matching the selected language
|
||||
// Nothing?
|
||||
if (input_string.GetType() == C4V_Nil)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
// Non-localized string?
|
||||
if (input_string.GetType() == C4V_String)
|
||||
{
|
||||
return input_string._getStr();
|
||||
}
|
||||
// Invalid type for this function?
|
||||
C4PropList *p = input_string._getPropList();
|
||||
if (!p || p->GetPropertyStr(P_Function) != &::Strings.P[P_Translate])
|
||||
{
|
||||
if (fail_silently)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw C4AulExecError(FormatString("Invalid value for translation: %s", input_string.GetDataString().getData()).getData());
|
||||
}
|
||||
}
|
||||
// This is a proplist. Resolve the language as the key.
|
||||
char lang_code[3] = "";
|
||||
for (int32_t lang_index = 0; SCopySegment(Config.General.LanguageEx, lang_index, lang_code, ',', 2); ++lang_index)
|
||||
{
|
||||
C4String *lang_string = ::Strings.FindString(lang_code);
|
||||
if (lang_string) // If the string is not found, it cannot be the key in a prop list
|
||||
{
|
||||
C4Value localized_string_val;
|
||||
if (p->GetPropertyByS(lang_string, &localized_string_val))
|
||||
{
|
||||
C4String *localized_string = localized_string_val.getStr();
|
||||
if (localized_string)
|
||||
{
|
||||
// Found it!
|
||||
if (selected_language)
|
||||
{
|
||||
selected_language->SetString(lang_string);
|
||||
}
|
||||
return localized_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No language matched. Just use any property and assume it's a language key.
|
||||
for (C4String *lang_string : p->GetSortedLocalProperties(false))
|
||||
{
|
||||
C4Value localized_string_val;
|
||||
if (p->GetPropertyByS(lang_string, &localized_string_val))
|
||||
{
|
||||
C4String *localized_string = localized_string_val.getStr();
|
||||
if (localized_string)
|
||||
{
|
||||
// Found it!
|
||||
if (selected_language)
|
||||
{
|
||||
selected_language->SetString(lang_string);
|
||||
}
|
||||
return localized_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No string properties. There's no localized information to be found.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
C4PropList *C4Game::AllocateTranslatedString()
|
||||
{
|
||||
C4PropListScript *value_proplist = new C4PropListScript();
|
||||
value_proplist->SetProperty(P_Function, C4VString(&::Strings.P[P_Translate]));
|
||||
return value_proplist;
|
||||
}
|
||||
|
|
|
@ -290,6 +290,10 @@ public:
|
|||
bool ToggleChart(); // chart dlg on/off
|
||||
void SetGlobalSoundModifier(C4PropList *modifier_props);
|
||||
|
||||
// Localized strings in editor props
|
||||
C4String *GetTranslatedString(const class C4Value &input_string, C4Value *selected_language, bool fail_silently) const;
|
||||
C4PropList *AllocateTranslatedString();
|
||||
|
||||
static constexpr const char * DirectJoinFilePrefix = "file:";
|
||||
};
|
||||
|
||||
|
|
|
@ -2742,6 +2742,12 @@ static long FnGetPXSCount(C4PropList * _this, Nillable<long> iMaterial, Nillable
|
|||
}
|
||||
}
|
||||
|
||||
static C4String *FnGetTranslatedString(C4PropList * _this, const C4Value & string_data)
|
||||
{
|
||||
// Resolve proplists containing localized strings to the current localization
|
||||
return ::Game.GetTranslatedString(string_data, nullptr, false);
|
||||
}
|
||||
|
||||
extern C4ScriptConstDef C4ScriptGameConstMap[];
|
||||
extern C4ScriptFnDef C4ScriptGameFnMap[];
|
||||
|
||||
|
@ -2953,6 +2959,7 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine)
|
|||
F(IncinerateLandscape);
|
||||
F(GetGravity);
|
||||
F(SetGravity);
|
||||
F(GetTranslatedString);
|
||||
#undef F
|
||||
}
|
||||
|
||||
|
|
|
@ -311,6 +311,9 @@ C4StringTable::C4StringTable()
|
|||
P[P_ForceSerialization] = "ForceSerialization";
|
||||
P[P_DrawArrows] = "DrawArrows";
|
||||
P[P_SCENPAR] = "SCENPAR";
|
||||
P[P_Translatable] = "Translatable";
|
||||
P[P_Function] = "Function";
|
||||
P[P_Translate] = "Translate";
|
||||
P[DFA_WALK] = "WALK";
|
||||
P[DFA_FLIGHT] = "FLIGHT";
|
||||
P[DFA_KNEEL] = "KNEEL";
|
||||
|
|
|
@ -535,6 +535,9 @@ enum C4PropertyName
|
|||
P_ForceSerialization,
|
||||
P_DrawArrows,
|
||||
P_SCENPAR,
|
||||
P_Translatable,
|
||||
P_Function,
|
||||
P_Translate,
|
||||
// Default Action Procedures
|
||||
DFA_WALK,
|
||||
DFA_FLIGHT,
|
||||
|
|
Loading…
Reference in New Issue