From f0a66c60d1eba14be2b3fdc9307fdd2e4a421a5c Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 29 Jun 2009 18:54:19 +0200 Subject: support editing of length variants this is not complete, e.g. full text search does not discern which variant contains the hit, the integration with phrases&guesses is non-existing, etc. the form preview will do funny things as long as the widgets don't support the multi-variant strings. --- tools/linguist/linguist/images/minus.png | Bin 0 -> 296 bytes tools/linguist/linguist/images/plus.png | Bin 0 -> 383 bytes tools/linguist/linguist/linguist.qrc | 2 + tools/linguist/linguist/mainwindow.cpp | 15 ++ tools/linguist/linguist/mainwindow.ui | 11 +- tools/linguist/linguist/messageeditor.cpp | 121 +++++++---- tools/linguist/linguist/messageeditor.h | 12 +- tools/linguist/linguist/messageeditorwidgets.cpp | 248 ++++++++++++++++++++++- tools/linguist/linguist/messageeditorwidgets.h | 50 +++++ tools/linguist/shared/po.cpp | 10 +- tools/linguist/shared/qm.cpp | 10 +- tools/linguist/shared/qph.cpp | 7 +- tools/linguist/shared/translator.h | 6 +- tools/linguist/shared/ts.cpp | 6 +- tools/linguist/shared/xliff.cpp | 7 +- 15 files changed, 442 insertions(+), 63 deletions(-) create mode 100644 tools/linguist/linguist/images/minus.png create mode 100644 tools/linguist/linguist/images/plus.png diff --git a/tools/linguist/linguist/images/minus.png b/tools/linguist/linguist/images/minus.png new file mode 100644 index 0000000..745b445 Binary files /dev/null and b/tools/linguist/linguist/images/minus.png differ diff --git a/tools/linguist/linguist/images/plus.png b/tools/linguist/linguist/images/plus.png new file mode 100644 index 0000000..ef43788 Binary files /dev/null and b/tools/linguist/linguist/images/plus.png differ diff --git a/tools/linguist/linguist/linguist.qrc b/tools/linguist/linguist/linguist.qrc index 42cf6e3..a43f0ce 100644 --- a/tools/linguist/linguist/linguist.qrc +++ b/tools/linguist/linguist/linguist.qrc @@ -32,6 +32,8 @@ images/up.png images/down.png images/editdelete.png + images/minus.png + images/plus.png images/win/accelerator.png images/win/book.png images/win/doneandnext.png diff --git a/tools/linguist/linguist/mainwindow.cpp b/tools/linguist/linguist/mainwindow.cpp index 18edc05..2b1df39 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 e32bc40..4f66f31 100644 --- a/tools/linguist/linguist/mainwindow.ui +++ b/tools/linguist/linguist/mainwindow.ui @@ -60,7 +60,7 @@ 0 0 673 - 30 + 28 @@ -116,6 +116,7 @@ + @@ -877,6 +878,14 @@ Ctrl+W + + + true + + + Lenght Variants + + diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp index be70d2b..fa1ee88 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(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(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(sender()); + for (int model = 0; ; ++model) { + MessageEditorData med = m_editors.at(model); + if (med.transTexts.contains(snd)) { + QFont font; + font.setPointSize(static_cast(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(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 95bcd86..4106036 100644 --- a/tools/linguist/linguist/messageeditor.h +++ b/tools/linguist/linguist/messageeditor.h @@ -45,6 +45,7 @@ #include "messagemodel.h" #include +#include #include #include @@ -58,11 +59,12 @@ class QTextEdit; class MessageEditor; class FormatTextEdit; class FormWidget; +class FormMultiWidget; struct MessageEditorData { QWidget *container; FormWidget *transCommentText; - QList transTexts; + QList 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 m_editors; + + QTimer m_tabOrderTimer; }; QT_END_NAMESPACE diff --git a/tools/linguist/linguist/messageeditorwidgets.cpp b/tools/linguist/linguist/messageeditorwidgets.cpp index 2ac426d..f8e2dc2 100644 --- a/tools/linguist/linguist/messageeditorwidgets.cpp +++ b/tools/linguist/linguist/messageeditorwidgets.cpp @@ -42,6 +42,8 @@ #include "messageeditorwidgets.h" #include "messagehighlighter.h" +#include + #include #include #include @@ -49,10 +51,12 @@ #include #include #include +#include #include #include #include #include +#include #include 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(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(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(sender())); +} + +void FormMultiWidget::slotSelectionChanged() +{ + emit selectionChanged(static_cast(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(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 bf3dcb0..c0b445c 100644 --- a/tools/linguist/linguist/messageeditorwidgets.h +++ b/tools/linguist/linguist/messageeditorwidgets.h @@ -42,6 +42,7 @@ #ifndef MESSAGEEDITORWIDGETS_H #define MESSAGEEDITORWIDGETS_H +#include #include #include #include @@ -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 &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 m_editors; + QList m_plusButtons; + QList 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 cb943be..e22aa7d 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 323bd29..ec61cb6 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 40f0386..4a29e0f 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 << "\n"; t << " " << protect(msg.sourceText()) << "\n"; - t << " " << protect(msg.translations().join(QLatin1String("@"))) + QString str = msg.translations().join(QLatin1String("@")); + str.replace(QChar(Translator::BinaryVariantSeparator), + QChar(Translator::TextVariantSeparator)); + t << " " << protect(str) << "\n"; if (!msg.context().isEmpty() || !msg.comment().isEmpty()) t << " " << msg.context() << msg.comment() diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h index 2651af0..ac824f3 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 ®isteredFileFormats(); - 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 6c95dbd..3efce15 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 61e4b9f..1313172 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( -- cgit v0.12