translatorwidget.cpp 14.2 KB
Newer Older
1
2
/*

3
  SPDX-FileCopyrightText: 2012-2020 Laurent Montel <montel@kde.org>
4

5
  SPDX-License-Identifier: GPL-2.0-or-later
6
7
8
9
10
*/

#include "translatorwidget.h"
#include "translatorutil.h"
#include "googletranslator.h"
11
#include <PimCommon/NetworkManager>
Laurent Montel's avatar
Laurent Montel committed
12
#include <KBusyIndicatorWidget>
13
14
15
16
17
18
19
20

#include <QPushButton>
#include <KLocalizedString>
#include <KConfigGroup>
#include <KSeparator>
#include <KMessageBox>

#include <QMimeData>
Laurent Montel's avatar
Laurent Montel committed
21
#include <QIcon>
Daniel Vrátil's avatar
Daniel Vrátil committed
22
#include <QComboBox>
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QKeyEvent>
#include <QShortcut>
#include <QPainter>
#include <QSplitter>
#include <KSharedConfig>
#include <QNetworkConfigurationManager>

using namespace PimCommon;

class Q_DECL_HIDDEN TranslatorWidget::TranslatorWidgetPrivate
{
public:
    TranslatorWidgetPrivate()
    {
    }
Laurent Montel's avatar
Laurent Montel committed
42

43
44
45
46
47
48
49
50
51
52
    ~TranslatorWidgetPrivate()
    {
        delete abstractTranslator;
    }

    void initLanguage();
    void fillToCombobox(const QString &lang);

    QMap<QString, QMap<QString, QString> > listLanguage;
    QByteArray data;
Laurent Montel's avatar
Laurent Montel committed
53
54
55
    TranslatorTextEdit *inputText = nullptr;
    KPIMTextEdit::PlainTextEditorWidget *translatedText = nullptr;
    TranslatorResultTextEdit *translatorResultTextEdit = nullptr;
Daniel Vrátil's avatar
Daniel Vrátil committed
56
57
    QComboBox *from = nullptr;
    QComboBox *to = nullptr;
Laurent Montel's avatar
Laurent Montel committed
58
59
60
    QPushButton *translate = nullptr;
    QPushButton *clear = nullptr;
    PimCommon::GoogleTranslator *abstractTranslator = nullptr;
Laurent Montel's avatar
Laurent Montel committed
61
    KBusyIndicatorWidget *progressIndicator = nullptr;
Laurent Montel's avatar
Laurent Montel committed
62
63
64
    QPushButton *invert = nullptr;
    QSplitter *splitter = nullptr;
    bool languageSettingsChanged = false;
Laurent Montel's avatar
Laurent Montel committed
65
    bool standalone = true;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
};

void TranslatorWidget::TranslatorWidgetPrivate::fillToCombobox(const QString &lang)
{
    to->clear();
    const QMap<QString, QString> list = listLanguage.value(lang);
    QMap<QString, QString>::const_iterator i = list.constBegin();
    QMap<QString, QString>::const_iterator end = list.constEnd();
    while (i != end) {
        to->addItem(i.key(), i.value());
        ++i;
    }
}

void TranslatorWidget::TranslatorWidgetPrivate::initLanguage()
{
    if (!abstractTranslator) {
        return;
    }
    listLanguage = abstractTranslator->initListLanguage(from);
}

TranslatorResultTextEdit::TranslatorResultTextEdit(QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
89
90
    : KPIMTextEdit::PlainTextEditor(parent)
    , mResultFailed(false)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
{
    setReadOnly(true);
}

void TranslatorResultTextEdit::setResultFailed(bool failed)
{
    if (mResultFailed != failed) {
        mResultFailed = failed;
        update();
    }
}

void TranslatorResultTextEdit::paintEvent(QPaintEvent *event)
{
    if (mResultFailed) {
        QPainter p(viewport());

        QFont font = p.font();
        font.setItalic(true);
        p.setFont(font);

        p.setPen(Qt::red);

        p.drawText(QRect(0, 0, width(), height()), Qt::AlignCenter, i18n("Problem when connecting to the translator web site."));
    } else {
        KPIMTextEdit::PlainTextEditor::paintEvent(event);
    }
}

TranslatorTextEdit::TranslatorTextEdit(QWidget *parent)
    : KPIMTextEdit::PlainTextEditor(parent)
{
}

void TranslatorTextEdit::dropEvent(QDropEvent *event)
{
    if (event->source() != this) {
        if (event->mimeData()->hasText()) {
            QTextCursor cursor = textCursor();
            cursor.beginEditBlock();
            cursor.insertText(event->mimeData()->text());
            cursor.endEditBlock();
            event->setDropAction(Qt::CopyAction);
            event->accept();
            Q_EMIT translateText();
            return;
        }
    }
    KPIMTextEdit::PlainTextEditor::dropEvent(event);
}

TranslatorWidget::TranslatorWidget(QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
143
144
    : QWidget(parent)
    , d(new TranslatorWidgetPrivate)
145
146
147
148
149
{
    init();
}

TranslatorWidget::TranslatorWidget(const QString &text, QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
150
151
    : QWidget(parent)
    , d(new TranslatorWidgetPrivate)
152
153
154
155
156
157
158
{
    init();
    d->inputText->setPlainText(text);
}

TranslatorWidget::~TranslatorWidget()
{
Laurent Montel's avatar
Laurent Montel committed
159
160
    disconnect(d->inputText, &TranslatorTextEdit::textChanged, this, &TranslatorWidget::slotTextChanged);
    disconnect(d->inputText, &TranslatorTextEdit::translateText, this, &TranslatorWidget::slotTranslate);
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
    writeConfig();
    delete d;
}

void TranslatorWidget::writeConfig()
{
    KConfigGroup myGroup(KSharedConfig::openConfig(), "TranslatorWidget");
    if (d->languageSettingsChanged) {
        myGroup.writeEntry(QStringLiteral("FromLanguage"), d->from->itemData(d->from->currentIndex()).toString());
        myGroup.writeEntry("ToLanguage", d->to->itemData(d->to->currentIndex()).toString());
    }
    myGroup.writeEntry("mainSplitter", d->splitter->sizes());
    myGroup.sync();
}

void TranslatorWidget::readConfig()
{
    KConfigGroup myGroup(KSharedConfig::openConfig(), "TranslatorWidget");
    const QString from = myGroup.readEntry(QStringLiteral("FromLanguage"));
    const QString to = myGroup.readEntry(QStringLiteral("ToLanguage"));
    if (from.isEmpty()) {
        return;
    }
    const int indexFrom = d->from->findData(from);
    if (indexFrom != -1) {
        d->from->setCurrentIndex(indexFrom);
    }
    const int indexTo = d->to->findData(to);
    if (indexTo != -1) {
        d->to->setCurrentIndex(indexTo);
    }
Laurent Montel's avatar
Laurent Montel committed
192
    const QList<int> size = {100, 400};
193
    d->splitter->setSizes(myGroup.readEntry("mainSplitter", size));
194
    d->invert->setEnabled(from != QLatin1String("auto"));
195
196
197
198
199
}

void TranslatorWidget::init()
{
    d->abstractTranslator = new GoogleTranslator();
Laurent Montel's avatar
Laurent Montel committed
200
    d->abstractTranslator->setParentWidget(this);
201
202
203
204
    connect(d->abstractTranslator, &PimCommon::GoogleTranslator::translateDone, this, &TranslatorWidget::slotTranslateDone);
    connect(d->abstractTranslator, &PimCommon::GoogleTranslator::translateFailed, this, &TranslatorWidget::slotTranslateFailed);

    QVBoxLayout *layout = new QVBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
205
    layout->setContentsMargins(0, 0, 0, 0);
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    QHBoxLayout *hboxLayout = new QHBoxLayout;
    QToolButton *closeBtn = new QToolButton(this);
    closeBtn->setObjectName(QStringLiteral("close-button"));
    closeBtn->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
    closeBtn->setIconSize(QSize(16, 16));
    closeBtn->setToolTip(i18n("Close"));

#ifndef QT_NO_ACCESSIBILITY
    closeBtn->setAccessibleName(i18n("Close"));
#endif
    closeBtn->setAutoRaise(true);
    hboxLayout->addWidget(closeBtn);
    connect(closeBtn, &QToolButton::clicked, this, &TranslatorWidget::slotCloseWidget);

    QLabel *label = new QLabel(i18nc("Translate from language", "From:"));
    hboxLayout->addWidget(label);
Daniel Vrátil's avatar
Daniel Vrátil committed
222
223
    d->from = new QComboBox;
    d->from->setMinimumWidth(50);
224
225
226
227
228
    d->from->setObjectName(QStringLiteral("from"));
    hboxLayout->addWidget(d->from);

    label = new QLabel(i18nc("Translate to language", "To:"));
    hboxLayout->addWidget(label);
Daniel Vrátil's avatar
Daniel Vrátil committed
229
230
    d->to = new QComboBox;
    d->to->setMinimumWidth(50);
231
    d->to->setObjectName(QStringLiteral("to"));
Laurent Montel's avatar
Laurent Montel committed
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
    hboxLayout->addWidget(d->to);

    KSeparator *separator = new KSeparator;
    separator->setOrientation(Qt::Vertical);
    hboxLayout->addWidget(separator);

    d->invert = new QPushButton(
        i18nc("Invert language choices so that from becomes to and to becomes from", "Invert"), this);
    d->invert->setObjectName(QStringLiteral("invert-button"));
    connect(d->invert, &QPushButton::clicked, this, &TranslatorWidget::slotInvertLanguage);
    hboxLayout->addWidget(d->invert);

    d->clear = new QPushButton(i18n("Clear"), this);
    d->clear->setObjectName(QStringLiteral("clear-button"));
#ifndef QT_NO_ACCESSIBILITY
    d->clear->setAccessibleName(i18n("Clear"));
#endif
    connect(d->clear, &QPushButton::clicked, this, &TranslatorWidget::slotClear);
    hboxLayout->addWidget(d->clear);

    d->translate = new QPushButton(i18n("Translate"));
    d->translate->setObjectName(QStringLiteral("translate-button"));
#ifndef QT_NO_ACCESSIBILITY
    d->translate->setAccessibleName(i18n("Translate"));
#endif

    hboxLayout->addWidget(d->translate);
    connect(d->translate, &QPushButton::clicked, this, &TranslatorWidget::slotTranslate);

Laurent Montel's avatar
Laurent Montel committed
262
    if (!qEnvironmentVariableIsEmpty("KDEPIM_DEBUGGING")) {
263
264
265
266
267
        QPushButton *debugButton = new QPushButton(i18n("Debug"));
        hboxLayout->addWidget(debugButton);
        connect(debugButton, &QPushButton::clicked, this, &TranslatorWidget::slotDebug);
    }

Laurent Montel's avatar
Laurent Montel committed
268
269
    d->progressIndicator = new KBusyIndicatorWidget(this);
    hboxLayout->addWidget(d->progressIndicator);
270
271
272
273
274
275
276
277
278
279

    hboxLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum));

    layout->addLayout(hboxLayout);

    d->splitter = new QSplitter;
    d->splitter->setChildrenCollapsible(false);
    d->inputText = new TranslatorTextEdit(this);
    KPIMTextEdit::PlainTextEditorWidget *editorWidget = new KPIMTextEdit::PlainTextEditorWidget(d->inputText);
    d->inputText->setObjectName(QStringLiteral("inputtext"));
280
    d->inputText->setPlaceholderText(i18n("Drag text that you want to translate. (Be careful text will be send to Google Server)."));
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    connect(d->inputText, &TranslatorTextEdit::textChanged, this, &TranslatorWidget::slotTextChanged);
    connect(d->inputText, &TranslatorTextEdit::translateText, this, &TranslatorWidget::slotTranslate);

    d->splitter->addWidget(editorWidget);
    d->translatorResultTextEdit = new TranslatorResultTextEdit;
    d->translatedText = new KPIMTextEdit::PlainTextEditorWidget(d->translatorResultTextEdit, this);
    d->translatedText->setObjectName(QStringLiteral("translatedtext"));
    d->translatedText->setReadOnly(true);
    d->splitter->addWidget(d->translatedText);

    layout->addWidget(d->splitter);

    d->initLanguage();
    d->from->setCurrentIndex(0);   //Fill "to" combobox
    slotFromLanguageChanged(0, true);
    slotTextChanged();
    readConfig();
Daniel Vrátil's avatar
Daniel Vrátil committed
298
    connect(d->from, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int val) {
Laurent Montel's avatar
Laurent Montel committed
299
        slotFromLanguageChanged(val, false);
300
        slotConfigChanged();
Laurent Montel's avatar
Laurent Montel committed
301
    });
302
303
304
305
    connect(d->to, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this]() {
        slotConfigChanged();
        slotTranslate();
    });
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347

    hide();
    setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
    d->languageSettingsChanged = false;
}

void TranslatorWidget::slotConfigChanged()
{
    d->languageSettingsChanged = true;
}

void TranslatorWidget::slotTextChanged()
{
    d->translate->setEnabled(!d->inputText->document()->isEmpty());
    d->clear->setEnabled(!d->inputText->document()->isEmpty());
}

void TranslatorWidget::slotFromLanguageChanged(int index, bool initialize)
{
    const QString lang = d->from->itemData(index).toString();
    d->invert->setEnabled(lang != QLatin1String("auto"));
    const QString to = d->to->itemData(d->to->currentIndex()).toString();
    d->to->blockSignals(true);
    d->fillToCombobox(lang);
    d->to->blockSignals(false);
    const int indexTo = d->to->findData(to);
    if (indexTo != -1) {
        d->to->setCurrentIndex(indexTo);
    }
    if (!initialize) {
        slotTranslate();
    }
}

void TranslatorWidget::setTextToTranslate(const QString &text)
{
    d->inputText->setPlainText(text);
    slotTranslate();
}

void TranslatorWidget::slotTranslate()
{
348
    if (!PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline()) {
349
350
351
352
353
354
355
356
357
358
359
360
361
        KMessageBox::information(this, i18n("No network connection detected, we cannot translate text."), i18n("No network"));
        return;
    }
    const QString textToTranslate = d->inputText->toPlainText();
    if (textToTranslate.trimmed().isEmpty()) {
        return;
    }

    d->translatorResultTextEdit->clear();

    const QString from = d->from->itemData(d->from->currentIndex()).toString();
    const QString to = d->to->itemData(d->to->currentIndex()).toString();
    d->translate->setEnabled(false);
Laurent Montel's avatar
Laurent Montel committed
362
    d->progressIndicator->show();
363
364
365
366
367
368
369
370
371
372

    d->abstractTranslator->setFrom(from);
    d->abstractTranslator->setTo(to);
    d->abstractTranslator->setInputText(d->inputText->toPlainText());
    d->abstractTranslator->translate();
}

void TranslatorWidget::slotTranslateDone()
{
    d->translate->setEnabled(true);
Laurent Montel's avatar
Laurent Montel committed
373
    d->progressIndicator->hide();
374
375
376
377
378
379
380
    d->translatorResultTextEdit->setResultFailed(false);
    d->translatorResultTextEdit->setPlainText(d->abstractTranslator->resultTranslate());
}

void TranslatorWidget::slotTranslateFailed(bool signalFailed, const QString &message)
{
    d->translate->setEnabled(true);
Laurent Montel's avatar
Laurent Montel committed
381
    d->progressIndicator->hide();
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
    d->translatorResultTextEdit->setResultFailed(signalFailed);
    d->translatorResultTextEdit->clear();
    if (!message.isEmpty()) {
        KMessageBox::error(this, message, i18n("Translate error"));
    }
}

void TranslatorWidget::slotInvertLanguage()
{
    const QString fromLanguage = d->from->itemData(d->from->currentIndex()).toString();
    // don't invert when fromLanguage == auto
    if (fromLanguage == QLatin1String("auto")) {
        return;
    }

    const QString toLanguage = d->to->itemData(d->to->currentIndex()).toString();
    const int indexFrom = d->from->findData(toLanguage);
    if (indexFrom != -1) {
        d->from->setCurrentIndex(indexFrom);
    }
    const int indexTo = d->to->findData(fromLanguage);
    if (indexTo != -1) {
        d->to->setCurrentIndex(indexTo);
    }
    slotTranslate();
}

void TranslatorWidget::setStandalone(bool b)
{
    d->standalone = b;
}

void TranslatorWidget::slotCloseWidget()
{
    if (isHidden()) {
        return;
    }
    d->inputText->clear();
    d->translatorResultTextEdit->clear();
Laurent Montel's avatar
Laurent Montel committed
421
    d->progressIndicator->hide();
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
    if (d->standalone) {
        hide();
    }
    Q_EMIT toolsWasClosed();
}

bool TranslatorWidget::event(QEvent *e)
{
    // Close the bar when pressing Escape.
    // Not using a QShortcut for this because it could conflict with
    // window-global actions (e.g. Emil Sedgh binds Esc to "close tab").
    // With a shortcut override we can catch this before it gets to kactions.
    if (e->type() == QEvent::ShortcutOverride || e->type() == QEvent::KeyPress) {
        QKeyEvent *kev = static_cast<QKeyEvent * >(e);
        if (kev->key() == Qt::Key_Escape) {
            e->accept();
            slotCloseWidget();
            return true;
        }
    }
    return QWidget::event(e);
}

void TranslatorWidget::slotClear()
{
    d->inputText->clear();
    d->translatorResultTextEdit->clear();
    d->translate->setEnabled(false);
    d->abstractTranslator->clear();
}

void TranslatorWidget::slotDebug()
{
    d->abstractTranslator->debug();
}