summaryrefslogtreecommitdiffstats
path: root/tools/linguist
diff options
context:
space:
mode:
Diffstat (limited to 'tools/linguist')
-rw-r--r--tools/linguist/linguist/images/minus.pngbin0 -> 296 bytes
-rw-r--r--tools/linguist/linguist/images/plus.pngbin0 -> 383 bytes
-rw-r--r--tools/linguist/linguist/linguist.qrc2
-rw-r--r--tools/linguist/linguist/mainwindow.cpp15
-rw-r--r--tools/linguist/linguist/mainwindow.ui11
-rw-r--r--tools/linguist/linguist/messageeditor.cpp121
-rw-r--r--tools/linguist/linguist/messageeditor.h12
-rw-r--r--tools/linguist/linguist/messageeditorwidgets.cpp248
-rw-r--r--tools/linguist/linguist/messageeditorwidgets.h50
-rw-r--r--tools/linguist/shared/po.cpp10
-rw-r--r--tools/linguist/shared/qm.cpp10
-rw-r--r--tools/linguist/shared/qph.cpp7
-rw-r--r--tools/linguist/shared/translator.h6
-rw-r--r--tools/linguist/shared/ts.cpp6
-rw-r--r--tools/linguist/shared/xliff.cpp7
15 files changed, 442 insertions, 63 deletions
diff --git a/tools/linguist/linguist/images/minus.png b/tools/linguist/linguist/images/minus.png
new file mode 100644
index 0000000000..745b445722
--- /dev/null
+++ b/tools/linguist/linguist/images/minus.png
Binary files differ
diff --git a/tools/linguist/linguist/images/plus.png b/tools/linguist/linguist/images/plus.png
new file mode 100644
index 0000000000..ef43788e67
--- /dev/null
+++ b/tools/linguist/linguist/images/plus.png
Binary files differ
diff --git a/tools/linguist/linguist/linguist.qrc b/tools/linguist/linguist/linguist.qrc
index 42cf6e3ab4..a43f0ced00 100644
--- a/tools/linguist/linguist/linguist.qrc
+++ b/tools/linguist/linguist/linguist.qrc
@@ -32,6 +32,8 @@
<file>images/up.png</file>
<file>images/down.png</file>
<file>images/editdelete.png</file>
+ <file>images/minus.png</file>
+ <file>images/plus.png</file>
<file>images/win/accelerator.png</file>
<file>images/win/book.png</file>
<file>images/win/doneandnext.png</file>
diff --git a/tools/linguist/linguist/mainwindow.cpp b/tools/linguist/linguist/mainwindow.cpp
index 18edc056df..2b1df39ba0 100644
--- a/tools/linguist/linguist/mainwindow.cpp
+++ b/tools/linguist/linguist/mainwindow.cpp
@@ -480,6 +480,10 @@ MainWindow::MainWindow()
readConfig();
m_statistics = 0;
+ connect(m_ui.actionLenghtVariants, SIGNAL(toggled(bool)),
+ m_messageEditor, SLOT(setLenghtVariants(bool)));
+ m_messageEditor->setLenghtVariants(m_ui.actionLenghtVariants->isChecked());
+
m_focusWatcher = new FocusWatcher(m_messageEditor, this);
m_contextView->installEventFilter(m_focusWatcher);
m_messageView->installEventFilter(m_focusWatcher);
@@ -2358,6 +2362,13 @@ void MainWindow::updateDanger(const MultiDataIndex &index, bool verbose)
}
QStringList translations = m->translations();
+ // Truncated variants are permitted to be "denormalized"
+ for (int i = 0; i < translations.count(); ++i) {
+ int sep = translations.at(i).indexOf(QChar(Translator::BinaryVariantSeparator));
+ if (sep >= 0)
+ translations[i].truncate(sep);
+ }
+
if (m_ui.actionAccelerators->isChecked()) {
bool sk = !QKeySequence::mnemonic(source).isEmpty();
bool tk = true;
@@ -2500,6 +2511,8 @@ void MainWindow::readConfig()
config.value(settingPath("Validators/PhraseMatch"), true).toBool());
m_ui.actionPlaceMarkerMatches->setChecked(
config.value(settingPath("Validators/PlaceMarkers"), true).toBool());
+ m_ui.actionLenghtVariants->setChecked(
+ config.value(settingPath("Options/LengthVariants"), false).toBool());
recentFiles().readConfig();
@@ -2524,6 +2537,8 @@ void MainWindow::writeConfig()
m_ui.actionPhraseMatches->isChecked());
config.setValue(settingPath("Validators/PlaceMarkers"),
m_ui.actionPlaceMarkerMatches->isChecked());
+ config.setValue(settingPath("Options/LengthVariants"),
+ m_ui.actionLenghtVariants->isChecked());
config.setValue(settingPath("MainWindowState"),
saveState());
recentFiles().writeConfig();
diff --git a/tools/linguist/linguist/mainwindow.ui b/tools/linguist/linguist/mainwindow.ui
index e32bc40c2b..4f66f31d05 100644
--- a/tools/linguist/linguist/mainwindow.ui
+++ b/tools/linguist/linguist/mainwindow.ui
@@ -60,7 +60,7 @@
<x>0</x>
<y>0</y>
<width>673</width>
- <height>30</height>
+ <height>28</height>
</rect>
</property>
<widget class="QMenu" name="menuPhrases">
@@ -116,6 +116,7 @@
<addaction name="actionResetSorting"/>
<addaction name="actionDisplayGuesses"/>
<addaction name="actionStatistics"/>
+ <addaction name="actionLenghtVariants"/>
<addaction name="separator"/>
<addaction name="menuToolbars"/>
<addaction name="menuViewViews"/>
@@ -877,6 +878,14 @@
<string>Ctrl+W</string>
</property>
</action>
+ <action name="actionLenghtVariants">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Lenght Variants</string>
+ </property>
+ </action>
</widget>
<resources/>
<connections/>
diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp
index be70d2b2c6..fa1ee889ca 100644
--- a/tools/linguist/linguist/messageeditor.cpp
+++ b/tools/linguist/linguist/messageeditor.cpp
@@ -87,6 +87,7 @@ MessageEditor::MessageEditor(MultiDataModel *dataModel, QMainWindow *parent)
m_dataModel(dataModel),
m_currentModel(-1),
m_currentNumerus(-1),
+ m_lengthVariants(false),
m_undoAvail(false),
m_redoAvail(false),
m_cutAvail(false),
@@ -120,6 +121,9 @@ MessageEditor::MessageEditor(MultiDataModel *dataModel, QMainWindow *parent)
connect(m_dataModel, SIGNAL(languageChanged(int)),
SLOT(setTargetLanguage(int)));
+ m_tabOrderTimer.setSingleShot(true);
+ connect(&m_tabOrderTimer, SIGNAL(timeout()), SLOT(reallyFixTabOrder()));
+
clipboardChanged();
setWhatsThis(tr("This whole panel allows you to view and edit "
@@ -223,6 +227,7 @@ void MessageEditor::messageModelAppended()
SLOT(emitTranslatorCommentChanged(QTextEdit *)));
connect(ed.transCommentText, SIGNAL(textChanged(QTextEdit *)), SLOT(resetHoverSelection()));
connect(ed.transCommentText, SIGNAL(cursorPositionChanged()), SLOT(resetHoverSelection()));
+ fixTabOrder();
QBoxLayout *box = new QVBoxLayout(ed.container);
box->setMargin(5);
box->addWidget(ed.transCommentText);
@@ -268,18 +273,16 @@ void MessageEditor::messageModelDeleted(int model)
void MessageEditor::addPluralForm(int model, const QString &label, bool writable)
{
- FormWidget *transEditor = new FormWidget(label, true);
- QFont font;
- font.setPointSize(static_cast<int>(m_editors[model].fontSize));
- transEditor->getEditor()->setFont(font);
+ FormMultiWidget *transEditor = new FormMultiWidget(label);
+ connect(transEditor, SIGNAL(editorCreated(QTextEdit *)), SLOT(editorCreated(QTextEdit *)));
transEditor->setEditingEnabled(writable);
transEditor->setHideWhenEmpty(!writable);
if (!m_editors[model].transTexts.isEmpty())
transEditor->setVisible(false);
+ transEditor->setMultiEnabled(m_lengthVariants);
static_cast<QBoxLayout *>(m_editors[model].container->layout())->insertWidget(
m_editors[model].transTexts.count(), transEditor);
- transEditor->getEditor()->installEventFilter(this);
connect(transEditor, SIGNAL(selectionChanged(QTextEdit *)),
SLOT(selectionChanged(QTextEdit *)));
connect(transEditor, SIGNAL(textChanged(QTextEdit *)),
@@ -290,6 +293,44 @@ void MessageEditor::addPluralForm(int model, const QString &label, bool writable
m_editors[model].transTexts << transEditor;
}
+void MessageEditor::editorCreated(QTextEdit *te)
+{
+ FormMultiWidget *snd = static_cast<FormMultiWidget *>(sender());
+ for (int model = 0; ; ++model) {
+ MessageEditorData med = m_editors.at(model);
+ if (med.transTexts.contains(snd)) {
+ QFont font;
+ font.setPointSize(static_cast<int>(med.fontSize));
+ te->setFont(font);
+
+ te->installEventFilter(this);
+
+ fixTabOrder();
+ return;
+ }
+ }
+}
+
+void MessageEditor::fixTabOrder()
+{
+ m_tabOrderTimer.start(0);
+}
+
+void MessageEditor::reallyFixTabOrder()
+{
+ QWidget *prev = this;
+ foreach (const MessageEditorData &med, m_editors) {
+ foreach (FormMultiWidget *fmw, med.transTexts)
+ foreach (QTextEdit *te, fmw->getEditors()) {
+ setTabOrder(prev, te);
+ prev = te;
+ }
+ QTextEdit *te = med.transCommentText->getEditor();
+ setTabOrder(prev, te);
+ prev = te;
+ }
+}
+
/*! internal
Returns all translations for an item.
The number of translations is dependent on if we have a plural form or not.
@@ -345,11 +386,12 @@ void MessageEditor::activeModelAndNumerus(int *model, int *numerus) const
{
for (int j = 0; j < m_editors.count(); ++j) {
for (int i = 0; i < m_editors[j].transTexts.count(); ++i)
- if (m_focusWidget == m_editors[j].transTexts[i]->getEditor()) {
- *model = j;
- *numerus = i;
- return;
- }
+ foreach (QTextEdit *te, m_editors[j].transTexts[i]->getEditors())
+ if (m_focusWidget == te) {
+ *model = j;
+ *numerus = i;
+ return;
+ }
if (m_focusWidget == m_editors[j].transCommentText->getEditor()) {
*model = j;
*numerus = -1;
@@ -364,7 +406,10 @@ QTextEdit *MessageEditor::activeTranslation() const
{
if (m_currentNumerus < 0)
return 0;
- return m_editors[m_currentModel].transTexts[m_currentNumerus]->getEditor();
+ foreach (QTextEdit *te, m_editors[m_currentModel].transTexts[m_currentNumerus]->getEditors())
+ if (te->hasFocus())
+ return te;
+ return 0; // This cannot happen
}
QTextEdit *MessageEditor::activeOr1stTranslation() const
@@ -372,11 +417,11 @@ QTextEdit *MessageEditor::activeOr1stTranslation() const
if (m_currentNumerus < 0) {
for (int i = 0; i < m_editors.size(); ++i)
if (m_editors[i].container->isVisible()
- && !m_editors[i].transTexts[0]->getEditor()->isReadOnly())
- return m_editors[i].transTexts[0]->getEditor();
+ && !m_editors[i].transTexts.first()->getEditors().first()->isReadOnly())
+ return m_editors[i].transTexts.first()->getEditors().first();
return 0;
}
- return m_editors[m_currentModel].transTexts[m_currentNumerus]->getEditor();
+ return activeTranslation();
}
QTextEdit *MessageEditor::activeTransComment() const
@@ -404,25 +449,14 @@ void MessageEditor::setTargetLanguage(int model)
{
const QStringList &numerusForms = m_dataModel->model(model)->numerusForms();
const QString &langLocalized = m_dataModel->model(model)->localizedLanguage();
- bool added = false;
for (int i = 0; i < numerusForms.count(); ++i) {
const QString &label = tr("%1 translation (%2)").arg(langLocalized, numerusForms[i]);
if (!i)
m_editors[model].firstForm = label;
- if (i >= m_editors[model].transTexts.count()) {
+ if (i >= m_editors[model].transTexts.count())
addPluralForm(model, label, m_dataModel->isModelWritable(model));
- QWidget *prev;
- if (i > 0)
- prev = m_editors[model].transTexts[i - 1]->getEditor();
- else if (model)
- prev = m_editors[model - 1].transCommentText->getEditor();
- else
- prev = this;
- setTabOrder(prev, m_editors[model].transTexts[i]->getEditor());
- added = true;
- } else {
+ else
m_editors[model].transTexts[i]->setLabel(label);
- }
m_editors[model].transTexts[i]->setVisible(!i || m_editors[model].pluralEditMode);
m_editors[model].transTexts[i]->setWhatsThis(
tr("This is where you can enter or modify"
@@ -432,16 +466,15 @@ void MessageEditor::setTargetLanguage(int model)
delete m_editors[model].transTexts.takeLast();
m_editors[model].invariantForm = tr("%1 translation").arg(langLocalized);
m_editors[model].transCommentText->setLabel(tr("%1 translator comments").arg(langLocalized));
- if (added)
- setTabOrder(m_editors[model].transTexts.last()->getEditor(), m_editors[model].transCommentText->getEditor());
}
MessageEditorData *MessageEditor::modelForWidget(const QObject *o)
{
for (int j = 0; j < m_editors.count(); ++j) {
for (int i = 0; i < m_editors[j].transTexts.count(); ++i)
- if (m_editors[j].transTexts[i]->getEditor() == o)
- return &m_editors[j];
+ foreach (QTextEdit *te, m_editors[j].transTexts[i]->getEditors())
+ if (te == o)
+ return &m_editors[j];
if (m_editors[j].transCommentText->getEditor() == o)
return &m_editors[j];
}
@@ -453,7 +486,8 @@ static bool applyFont(MessageEditorData *med)
QFont font;
font.setPointSize(static_cast<int>(med->fontSize));
for (int i = 0; i < med->transTexts.count(); ++i)
- med->transTexts[i]->getEditor()->setFont(font);
+ foreach (QTextEdit *te, med->transTexts[i]->getEditors())
+ te->setFont(font);
med->transCommentText->getEditor()->setFont(font);
return true;
}
@@ -556,7 +590,7 @@ void MessageEditor::showNothing()
m_commentText->clearTranslation();
for (int j = 0; j < m_editors.count(); ++j) {
setEditingEnabled(j, false);
- foreach (FormWidget *widget, m_editors[j].transTexts)
+ foreach (FormMultiWidget *widget, m_editors[j].transTexts)
widget->clearTranslation();
m_editors[j].transCommentText->clearTranslation();
}
@@ -643,7 +677,7 @@ void MessageEditor::setTranslation(int model, const QString &translation, int nu
MessageEditorData &ed = m_editors[model];
if (numerus >= ed.transTexts.count())
numerus = 0;
- FormWidget *transForm = ed.transTexts[numerus];
+ FormMultiWidget *transForm = ed.transTexts[numerus];
transForm->setTranslation(translation, false);
updateBeginFromSource();
@@ -658,8 +692,8 @@ void MessageEditor::setTranslation(int latestModel, const QString &translation)
latestModel = m_currentModel;
numerus = m_currentNumerus;
}
- FormWidget *transForm = m_editors[latestModel].transTexts[numerus];
- transForm->getEditor()->setFocus();
+ FormMultiWidget *transForm = m_editors[latestModel].transTexts[numerus];
+ transForm->getEditors().first()->setFocus();
transForm->setTranslation(translation, true);
updateBeginFromSource();
@@ -668,13 +702,21 @@ void MessageEditor::setTranslation(int latestModel, const QString &translation)
void MessageEditor::setEditingEnabled(int model, bool enabled)
{
MessageEditorData &ed = m_editors[model];
- foreach (FormWidget *widget, ed.transTexts)
+ foreach (FormMultiWidget *widget, ed.transTexts)
widget->setEditingEnabled(enabled);
ed.transCommentText->setEditingEnabled(enabled);
updateCanPaste();
}
+void MessageEditor::setLenghtVariants(bool on)
+{
+ m_lengthVariants = on;
+ foreach (const MessageEditorData &ed, m_editors)
+ foreach (FormMultiWidget *widget, ed.transTexts)
+ widget->setMultiEnabled(on);
+}
+
void MessageEditor::undo()
{
activeEditor()->document()->undo();
@@ -813,12 +855,13 @@ void MessageEditor::setEditorFocus(int model)
resetSelection();
m_currentNumerus = -1;
m_currentModel = -1;
+ m_focusWidget = 0;
emit activeModelChanged(activeModel());
updateBeginFromSource();
updateUndoRedo();
updateCanPaste();
} else {
- m_editors[model].transTexts[0]->getEditor()->setFocus();
+ m_editors[model].transTexts.first()->getEditors().first()->setFocus();
}
}
}
@@ -829,7 +872,7 @@ bool MessageEditor::focusNextUnfinished(int start)
if (m_dataModel->isModelWritable(j))
if (MessageItem *item = m_dataModel->messageItem(m_currentIndex, j))
if (item->type() == TranslatorMessage::Unfinished) {
- m_editors[j].transTexts[0]->getEditor()->setFocus();
+ m_editors[j].transTexts.first()->getEditors().first()->setFocus();
return true;
}
return false;
diff --git a/tools/linguist/linguist/messageeditor.h b/tools/linguist/linguist/messageeditor.h
index 95bcd86989..4106036944 100644
--- a/tools/linguist/linguist/messageeditor.h
+++ b/tools/linguist/linguist/messageeditor.h
@@ -45,6 +45,7 @@
#include "messagemodel.h"
#include <QtCore/QLocale>
+#include <QtCore/QTimer>
#include <QtGui/QFrame>
#include <QtGui/QScrollArea>
@@ -58,11 +59,12 @@ class QTextEdit;
class MessageEditor;
class FormatTextEdit;
class FormWidget;
+class FormMultiWidget;
struct MessageEditorData {
QWidget *container;
FormWidget *transCommentText;
- QList<FormWidget*> transTexts;
+ QList<FormMultiWidget *> transTexts;
QString invariantForm;
QString firstForm;
float fontSize;
@@ -108,8 +110,10 @@ public slots:
void beginFromSource();
void setEditorFocus();
void setTranslation(int latestModel, const QString &translation);
+ void setLenghtVariants(bool on);
private slots:
+ void editorCreated(QTextEdit *);
void selectionChanged(QTextEdit *);
void resetHoverSelection();
void emitTranslationChanged(QTextEdit *);
@@ -120,6 +124,7 @@ private slots:
void messageModelDeleted(int model);
void allModelsDeleted();
void setTargetLanguage(int model);
+ void reallyFixTabOrder();
private:
void setupEditorPage();
@@ -141,6 +146,7 @@ private:
void updateUndoRedo();
void updateCanCutCopy();
void addPluralForm(int model, const QString &label, bool writable);
+ void fixTabOrder();
QPalette paletteForModel(int model) const;
MultiDataModel *m_dataModel;
@@ -149,6 +155,8 @@ private:
int m_currentModel;
int m_currentNumerus;
+ bool m_lengthVariants;
+
bool m_undoAvail;
bool m_redoAvail;
bool m_cutAvail;
@@ -163,6 +171,8 @@ private:
FormWidget *m_pluralSource;
FormWidget *m_commentText;
QList<MessageEditorData> m_editors;
+
+ QTimer m_tabOrderTimer;
};
QT_END_NAMESPACE
diff --git a/tools/linguist/linguist/messageeditorwidgets.cpp b/tools/linguist/linguist/messageeditorwidgets.cpp
index 2ac426d600..f8e2dc25d9 100644
--- a/tools/linguist/linguist/messageeditorwidgets.cpp
+++ b/tools/linguist/linguist/messageeditorwidgets.cpp
@@ -42,6 +42,8 @@
#include "messageeditorwidgets.h"
#include "messagehighlighter.h"
+#include <translator.h>
+
#include <QAbstractTextDocumentLayout>
#include <QAction>
#include <QApplication>
@@ -49,10 +51,12 @@
#include <QDebug>
#include <QLayout>
#include <QMenu>
+#include <QMessageBox>
#include <QPainter>
#include <QScrollArea>
#include <QTextBlock>
#include <QTextDocumentFragment>
+#include <QToolButton>
#include <QVBoxLayout>
QT_BEGIN_NAMESPACE
@@ -182,14 +186,14 @@ FormWidget::FormWidget(const QString &label, bool isEditable, QWidget *parent)
connect(m_editor, SIGNAL(cursorPositionChanged()), SIGNAL(cursorPositionChanged()));
}
-void FormWidget::slotSelectionChanged()
+void FormWidget::slotTextChanged()
{
- emit selectionChanged(m_editor);
+ emit textChanged(m_editor);
}
-void FormWidget::slotTextChanged()
+void FormWidget::slotSelectionChanged()
{
- emit textChanged(m_editor);
+ emit selectionChanged(m_editor);
}
void FormWidget::setTranslation(const QString &text, bool userAction)
@@ -207,4 +211,240 @@ void FormWidget::setEditingEnabled(bool enable)
}
+class ButtonWrapper : public QWidget
+{
+ // no Q_OBJECT: no need to, and don't want the useless moc file
+
+public:
+ ButtonWrapper(QWidget *wrapee, QWidget *relator) : m_wrapee(wrapee)
+ {
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
+ QBoxLayout *box = new QVBoxLayout;
+ box->setMargin(0);
+ setLayout(box);
+ box->addWidget(wrapee, 0, Qt::AlignBottom);
+ if (relator)
+ relator->installEventFilter(this);
+ }
+
+protected:
+ virtual bool eventFilter(QObject *object, QEvent *event)
+ {
+ if (event->type() == QEvent::Resize) {
+ QWidget *relator = static_cast<QWidget *>(object);
+ setFixedHeight((relator->height() + layout()->spacing() + m_wrapee->height()) / 2);
+ }
+ return false;
+ }
+
+private:
+ QWidget *m_wrapee;
+};
+
+FormMultiWidget::FormMultiWidget(const QString &label, QWidget *parent)
+ : QWidget(parent),
+ m_hideWhenEmpty(false),
+ m_multiEnabled(false),
+ m_plusIcon(QIcon(QLatin1String(":/images/plus.png"))), // make static
+ m_minusIcon(QIcon(QLatin1String(":/images/minus.png")))
+{
+ m_label = new QLabel(this);
+ m_label->setText(label);
+
+ m_plusButtons.append(
+ new ButtonWrapper(makeButton(m_plusIcon, SLOT(plusButtonClicked())), 0));
+}
+
+QAbstractButton *FormMultiWidget::makeButton(const QIcon &icon, const char *slot)
+{
+ QAbstractButton *btn = new QToolButton(this);
+ btn->setIcon(icon);
+ btn->setFixedSize(icon.availableSizes().first() /* + something */);
+ btn->setFocusPolicy(Qt::NoFocus);
+ connect(btn, SIGNAL(clicked()), slot);
+ return btn;
+}
+
+void FormMultiWidget::addEditor(int idx)
+{
+ FormatTextEdit *editor = new FormatTextEdit(this);
+ m_editors.insert(idx, editor);
+
+ m_minusButtons.insert(idx, makeButton(m_minusIcon, SLOT(minusButtonClicked())));
+ m_plusButtons.insert(idx + 1,
+ new ButtonWrapper(makeButton(m_plusIcon, SLOT(plusButtonClicked())), editor));
+
+ connect(editor, SIGNAL(textChanged()), SLOT(slotTextChanged()));
+ connect(editor, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
+ connect(editor, SIGNAL(cursorPositionChanged()), SIGNAL(cursorPositionChanged()));
+ editor->installEventFilter(this);
+
+ emit editorCreated(editor);
+}
+
+bool FormMultiWidget::eventFilter(QObject *watched, QEvent *event)
+{
+ int i = 0;
+ while (m_editors.at(i) != watched)
+ if (++i >= m_editors.count()) // Happens when deleting an editor
+ return false;
+ if (event->type() == QEvent::FocusOut) {
+ m_minusButtons.at(i)->setToolTip(QString());
+ m_plusButtons.at(i)->setToolTip(QString());
+ m_plusButtons.at(i + 1)->setToolTip(QString());
+ } else if (event->type() == QEvent::FocusIn) {
+ m_minusButtons.at(i)->setToolTip(/*: translate, but don't change */ tr("Alt+Delete"));
+ m_plusButtons.at(i)->setToolTip(/*: translate, but don't change */ tr("Shift+Alt+Insert"));
+ m_plusButtons.at(i + 1)->setToolTip(/*: translate, but don't change */ tr("Alt+Insert"));
+ } else if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->modifiers() & Qt::AltModifier) {
+ if (ke->key() == Qt::Key_Delete) {
+ deleteEditor(i);
+ return true;
+ } else if (ke->key() == Qt::Key_Insert) {
+ if (!(ke->modifiers() & Qt::ShiftModifier))
+ ++i;
+ insertEditor(i);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void FormMultiWidget::updateLayout()
+{
+ delete layout();
+
+ QGridLayout *layout = new QGridLayout;
+ layout->setMargin(0);
+ setLayout(layout);
+
+ bool variants = m_multiEnabled && m_label->isEnabled();
+
+ layout->addWidget(m_label, 0, 0, 1, variants ? 3 : 1);
+
+ for (int i = 0; i < m_plusButtons.count(); ++i) {
+ if (variants)
+ layout->addWidget(m_plusButtons.at(i), 1 + i * 2, 0, 2, 1, Qt::AlignTop);
+ m_plusButtons.at(i)->setVisible(variants);
+ }
+ for (int j = 0; j < m_minusButtons.count(); ++j) {
+ if (variants)
+ layout->addWidget(m_minusButtons.at(j), 2 + j * 2, 2, 2, 1, Qt::AlignVCenter);
+ m_minusButtons.at(j)->setVisible(variants);
+ }
+ for (int k = 0; k < m_editors.count(); ++k)
+ layout->addWidget(m_editors.at(k), 2 + k * 2, variants ? 1 : 0, 2, 1, Qt::AlignVCenter);
+
+ updateGeometry();
+}
+
+void FormMultiWidget::slotTextChanged()
+{
+ emit textChanged(static_cast<QTextEdit *>(sender()));
+}
+
+void FormMultiWidget::slotSelectionChanged()
+{
+ emit selectionChanged(static_cast<QTextEdit *>(sender()));
+}
+
+void FormMultiWidget::setTranslation(const QString &text, bool userAction)
+{
+ QStringList texts = text.split(QChar(Translator::BinaryVariantSeparator), QString::KeepEmptyParts);
+
+ while (m_editors.count() > texts.count()) {
+ delete m_minusButtons.takeLast();
+ delete m_plusButtons.takeLast();
+ delete m_editors.takeLast();
+ }
+ while (m_editors.count() < texts.count())
+ addEditor(m_editors.count());
+ updateLayout();
+
+ for (int i = 0; i < texts.count(); ++i)
+ // XXX this will emit n textChanged signals
+ m_editors.at(i)->setPlainText(texts.at(i), userAction);
+
+ if (m_hideWhenEmpty)
+ setHidden(text.isEmpty());
+}
+
+QString FormMultiWidget::getTranslation() const
+{
+ QString ret;
+ for (int i = 0; i < m_editors.count(); ++i) {
+ if (i)
+ ret += QChar(Translator::BinaryVariantSeparator);
+ ret += m_editors.at(i)->toPlainText();
+ }
+ return ret;
+}
+
+void FormMultiWidget::setEditingEnabled(bool enable)
+{
+ // Use read-only state so that the text can still be copied
+ for (int i = 0; i < m_editors.count(); ++i)
+ m_editors.at(i)->setReadOnly(!enable);
+ m_label->setEnabled(enable);
+ if (m_multiEnabled)
+ updateLayout();
+}
+
+void FormMultiWidget::setMultiEnabled(bool enable)
+{
+ m_multiEnabled = enable;
+ if (m_label->isEnabled())
+ updateLayout();
+}
+
+void FormMultiWidget::minusButtonClicked()
+{
+ int i = 0;
+ while (m_minusButtons.at(i) != sender())
+ ++i;
+ deleteEditor(i);
+}
+
+void FormMultiWidget::plusButtonClicked()
+{
+ QWidget *btn = static_cast<QAbstractButton *>(sender())->parentWidget();
+ int i = 0;
+ while (m_plusButtons.at(i) != btn)
+ ++i;
+ insertEditor(i);
+}
+
+void FormMultiWidget::deleteEditor(int idx)
+{
+ if (m_editors.count() == 1) {
+ // Don't just clear(), so the undo history is not lost
+ QTextCursor c = m_editors.first()->textCursor();
+ c.select(QTextCursor::Document);
+ c.removeSelectedText();
+ } else {
+ if (!m_editors.at(idx)->toPlainText().isEmpty()) {
+ if (QMessageBox::question(topLevelWidget(), tr("Confirmation - Qt Linguist"),
+ tr("Delete non-empty length variant?"),
+ QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes)
+ != QMessageBox::Yes)
+ return;
+ }
+ delete m_editors.takeAt(idx);
+ delete m_minusButtons.takeAt(idx);
+ delete m_plusButtons.takeAt(idx + 1);
+ updateLayout();
+ emit textChanged(m_editors.at((m_editors.count() == idx) ? idx - 1 : idx));
+ }
+}
+
+void FormMultiWidget::insertEditor(int idx)
+{
+ addEditor(idx);
+ updateLayout();
+ emit textChanged(m_editors.at(idx));
+}
+
QT_END_NAMESPACE
diff --git a/tools/linguist/linguist/messageeditorwidgets.h b/tools/linguist/linguist/messageeditorwidgets.h
index bf3dcb0042..c0b445c3fd 100644
--- a/tools/linguist/linguist/messageeditorwidgets.h
+++ b/tools/linguist/linguist/messageeditorwidgets.h
@@ -42,6 +42,7 @@
#ifndef MESSAGEEDITORWIDGETS_H
#define MESSAGEEDITORWIDGETS_H
+#include <QIcon>
#include <QImage>
#include <QLabel>
#include <QMap>
@@ -51,6 +52,7 @@
QT_BEGIN_NAMESPACE
+class QAbstractButton;
class QAction;
class QContextMenuEvent;
class QKeyEvent;
@@ -129,6 +131,54 @@ private:
bool m_hideWhenEmpty;
};
+/*
+ Displays text fields & associated label
+*/
+class FormMultiWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ FormMultiWidget(const QString &label, QWidget *parent = 0);
+ void setLabel(const QString &label) { m_label->setText(label); }
+ void setTranslation(const QString &text, bool userAction = false);
+ void clearTranslation() { setTranslation(QString(), false); }
+ QString getTranslation() const;
+ void setEditingEnabled(bool enable);
+ void setMultiEnabled(bool enable);
+ void setHideWhenEmpty(bool optional) { m_hideWhenEmpty = optional; }
+ const QList<FormatTextEdit *> &getEditors() const { return m_editors; }
+
+signals:
+ void editorCreated(QTextEdit *);
+ void textChanged(QTextEdit *);
+ void selectionChanged(QTextEdit *);
+ void cursorPositionChanged();
+
+protected:
+ bool eventFilter(QObject *watched, QEvent *event);
+
+private slots:
+ void slotTextChanged();
+ void slotSelectionChanged();
+ void minusButtonClicked();
+ void plusButtonClicked();
+
+private:
+ void addEditor(int idx);
+ void updateLayout();
+ QAbstractButton *makeButton(const QIcon &icon, const char *slot);
+ void insertEditor(int idx);
+ void deleteEditor(int idx);
+
+ QLabel *m_label;
+ QList<FormatTextEdit *> m_editors;
+ QList<QWidget *> m_plusButtons;
+ QList<QAbstractButton *> m_minusButtons;
+ bool m_hideWhenEmpty;
+ bool m_multiEnabled;
+ QIcon m_plusIcon, m_minusIcon;
+};
+
QT_END_NAMESPACE
#endif // MESSAGEEDITORWIDGETS_H
diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp
index cb943be83b..e22aa7d90d 100644
--- a/tools/linguist/shared/po.cpp
+++ b/tools/linguist/shared/po.cpp
@@ -395,7 +395,10 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
const QString prefix = QLatin1String(isObsolete ? "#~ " : "");
while (true) {
int idx = line.indexOf(QLatin1Char(' '), prefix.length());
- item.msgStr.append(slurpEscapedString(lines, l, idx, prefix, cd));
+ QString str = slurpEscapedString(lines, l, idx, prefix, cd);
+ str.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
+ item.msgStr.append(str);
if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1)))
break;
++l;
@@ -635,8 +638,11 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
QStringList translations = translator.normalizedTranslations(msg, cd, &ok);
for (int i = 0; i != translations.size(); ++i) {
+ QString str = translations.at(i);
+ str.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
out << poEscapedString(prefix, QString::fromLatin1("msgstr[%1]").arg(i), noWrap,
- translations.at(i));
+ str);
}
}
first = false;
diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp
index 323bd29e8f..ec61cb6e64 100644
--- a/tools/linguist/shared/qm.cpp
+++ b/tools/linguist/shared/qm.cpp
@@ -238,12 +238,8 @@ Prefix Releaser::commonPrefix(const ByteTranslatorMessage &m1, const ByteTransla
void Releaser::writeMessage(const ByteTranslatorMessage &msg, QDataStream &stream,
TranslatorSaveMode mode, Prefix prefix) const
{
- for (int i = 0; i < msg.translations().count(); ++i) {
- QString str = msg.translations().at(i);
- str.replace(QChar(Translator::DefaultVariantSeparator),
- QChar(Translator::InternalVariantSeparator));
- stream << quint8(Tag_Translation) << str;
- }
+ for (int i = 0; i < msg.translations().count(); ++i)
+ stream << quint8(Tag_Translation) << msg.translations().at(i);
if (mode == SaveEverything)
prefix = HashContextSourceTextComment;
@@ -592,8 +588,6 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
str[i] = QChar((str.at(i).unicode() >> 8) +
((str.at(i).unicode() << 8) & 0xff00));
}
- str.replace(QChar(Translator::InternalVariantSeparator),
- QChar(Translator::DefaultVariantSeparator));
translations << str;
m += len;
break;
diff --git a/tools/linguist/shared/qph.cpp b/tools/linguist/shared/qph.cpp
index 40f0386bc7..4a29e0f971 100644
--- a/tools/linguist/shared/qph.cpp
+++ b/tools/linguist/shared/qph.cpp
@@ -99,6 +99,8 @@ bool QPHReader::read(Translator &translator)
else if (m_currentField == DefinitionField)
m_currentDefinition += text();
} else if (isEndElement() && name() == QLatin1String("phrase")) {
+ m_currentTarget.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
TranslatorMessage msg;
msg.setSourceText(m_currentSource);
msg.setTranslation(m_currentTarget);
@@ -159,7 +161,10 @@ static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData
foreach (const TranslatorMessage &msg, translator.messages()) {
t << "<phrase>\n";
t << " <source>" << protect(msg.sourceText()) << "</source>\n";
- t << " <target>" << protect(msg.translations().join(QLatin1String("@")))
+ QString str = msg.translations().join(QLatin1String("@"));
+ str.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
+ t << " <target>" << protect(str)
<< "</target>\n";
if (!msg.context().isEmpty() || !msg.comment().isEmpty())
t << " <definition>" << msg.context() << msg.comment()
diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h
index 2651af02f1..ac824f3178 100644
--- a/tools/linguist/shared/translator.h
+++ b/tools/linguist/shared/translator.h
@@ -190,9 +190,9 @@ public:
static void registerFileFormat(const FileFormat &format);
static QList<FileFormat> &registeredFileFormats();
- enum VariantSeparators {
- DefaultVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
- InternalVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
+ enum {
+ TextVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
+ BinaryVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
};
private:
diff --git a/tools/linguist/shared/ts.cpp b/tools/linguist/shared/ts.cpp
index 6c95dbdaba..3efce15d25 100644
--- a/tools/linguist/shared/ts.cpp
+++ b/tools/linguist/shared/ts.cpp
@@ -197,7 +197,7 @@ QString TSReader::readTransContents()
// ignore these, just whitespace
} else if (elementStarts(strlengthvariant)) {
if (!result.isEmpty())
- result += QChar(Translator::DefaultVariantSeparator);
+ result += QChar(Translator::BinaryVariantSeparator);
result += readContents();
} else {
handleError();
@@ -514,7 +514,7 @@ static void writeExtras(QTextStream &t, const char *indent,
static void writeVariants(QTextStream &t, const char *indent, const QString &input)
{
int offset;
- if ((offset = input.indexOf(QChar(Translator::DefaultVariantSeparator))) >= 0) {
+ if ((offset = input.indexOf(QChar(Translator::BinaryVariantSeparator))) >= 0) {
t << " variants=\"yes\">";
int start = 0;
forever {
@@ -524,7 +524,7 @@ static void writeVariants(QTextStream &t, const char *indent, const QString &inp
if (offset == input.length())
break;
start = offset + 1;
- offset = input.indexOf(QChar(Translator::DefaultVariantSeparator), start);
+ offset = input.indexOf(QChar(Translator::BinaryVariantSeparator), start);
if (offset < 0)
offset = input.length();
}
diff --git a/tools/linguist/shared/xliff.cpp b/tools/linguist/shared/xliff.cpp
index 61e4b9fc30..13131726fe 100644
--- a/tools/linguist/shared/xliff.cpp
+++ b/tools/linguist/shared/xliff.cpp
@@ -303,6 +303,8 @@ static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const
QString translation;
if (transit != transend) {
translation = *transit;
+ translation.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
++transit;
puttrans = true;
}
@@ -598,8 +600,11 @@ bool XLIFFHandler::endElement(const QString &namespaceURI, const QString& localN
m_sources.append(accum);
}
} else if (localName == QLatin1String("target")) {
- if (popContext(XC_restype_translation))
+ if (popContext(XC_restype_translation)) {
+ accum.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
m_translations.append(accum);
+ }
} else if (localName == QLatin1String("context-group")) {
if (popContext(XC_context_group)) {
m_refs.append(TranslatorMessage::Reference(