1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2005-02-14
7  * Description : a tool to insert a text over an image.
8  *
9  * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2006-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option)
16  * any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * ============================================================ */
24 
25 #include "inserttexttool.h"
26 
27 // Qt includes
28 
29 #include <QBrush>
30 #include <QButtonGroup>
31 #include <QCheckBox>
32 #include <QFont>
33 #include <QFrame>
34 #include <QGridLayout>
35 #include <QGroupBox>
36 #include <QLabel>
37 #include <QPainter>
38 #include <QPen>
39 #include <QPixmap>
40 #include <QTimer>
41 #include <QToolButton>
42 #include <QVBoxLayout>
43 #include <QIcon>
44 #include <QComboBox>
45 #include <QApplication>
46 #include <QTextEdit>
47 
48 // KDE includes
49 
50 #include <klocalizedstring.h>
51 #include <ksharedconfig.h>
52 #include <kconfiggroup.h>
53 
54 // Local includes
55 
56 #include "dlayoutbox.h"
57 #include "dimg.h"
58 #include "editortoolsettings.h"
59 #include "imageiface.h"
60 #include "inserttextwidget.h"
61 #include "dfontproperties.h"
62 #include "dcolorselector.h"
63 #include "dnuminput.h"
64 
65 namespace DigikamEditorInsertTextToolPlugin
66 {
67 
68 class Q_DECL_HIDDEN InsertTextTool::Private
69 {
70 public:
71 
Private()72     explicit Private()
73       : alignTextMode       (0),
74         defaultSizeFont     (0),
75         borderText          (nullptr),
76         transparentText     (nullptr),
77         alignButtonGroup    (nullptr),
78         textRotation        (nullptr),
79         textOpacity         (nullptr),
80         fontColorButton     (nullptr),
81         fontChooserWidget   (nullptr),
82         textEdit            (nullptr),
83         previewWidget       (nullptr),
84         gboxSettings        (nullptr)
85     {
86     }
87 
88     static const QString configGroupName;
89     static const QString configTextRotationEntry;
90     static const QString configFontColorEntry;
91     static const QString configTextOpacity;
92     static const QString configTextStringEntry;
93     static const QString configFontPropertiesEntry;
94     static const QString configTextAlignmentEntry;
95     static const QString configBorderTextEntry;
96     static const QString configTransparentTextEntry;
97     static const QString configPositionHintEntry;
98 
99     int                  alignTextMode;
100     int                  defaultSizeFont;
101 
102     QCheckBox*           borderText;
103     QCheckBox*           transparentText;
104 
105     QButtonGroup*        alignButtonGroup;
106     QFont                textFont;
107 
108     QComboBox*           textRotation;
109     DIntNumInput*        textOpacity;
110     DColorSelector*      fontColorButton;
111     DFontProperties*     fontChooserWidget;
112     QTextEdit*           textEdit;
113 
114     InsertTextWidget*    previewWidget;
115     EditorToolSettings*  gboxSettings;
116 };
117 
118 const QString InsertTextTool::Private::configGroupName(QLatin1String("inserttext Tool"));
119 const QString InsertTextTool::Private::configTextRotationEntry(QLatin1String("Text Rotation"));
120 const QString InsertTextTool::Private::configFontColorEntry(QLatin1String("Font Color"));
121 const QString InsertTextTool::Private::configTextOpacity(QLatin1String("Text Opacity"));
122 const QString InsertTextTool::Private::configTextStringEntry(QLatin1String("Enter your text here."));
123 const QString InsertTextTool::Private::configFontPropertiesEntry(QLatin1String("Font Properties"));
124 const QString InsertTextTool::Private::configTextAlignmentEntry(QLatin1String("Text Alignment"));
125 const QString InsertTextTool::Private::configBorderTextEntry(QLatin1String("Border Text"));
126 const QString InsertTextTool::Private::configTransparentTextEntry(QLatin1String("Transparent Text"));
127 const QString InsertTextTool::Private::configPositionHintEntry(QLatin1String("Position Hint"));
128 
129 // --------------------------------------------------------
130 
InsertTextTool(QObject * const parent)131 InsertTextTool::InsertTextTool(QObject* const parent)
132     : EditorTool(parent),
133       d         (new Private)
134 {
135     setObjectName(QLatin1String("inserttext"));
136 
137     // -------------------------------------------------------------
138 
139     QFrame* const frame  = new QFrame(nullptr);
140     frame->setFrameStyle(QFrame::Panel|QFrame::Sunken);
141 
142     QVBoxLayout* const l = new QVBoxLayout(frame);
143     d->previewWidget     = new InsertTextWidget(480, 320, frame);
144     l->addWidget(d->previewWidget);
145     d->previewWidget->setWhatsThis(i18n("This previews the text inserted in the image. "
146                                         "You can use the mouse to move the text to the right location."));
147     setToolView(frame);
148 
149     // -------------------------------------------------------------
150 
151     d->gboxSettings = new EditorToolSettings(nullptr);
152     d->textEdit     = new QTextEdit();
153     d->textEdit->setWordWrapMode(QTextOption::NoWrap);
154     d->textEdit->setWhatsThis( i18n("Here, enter the text you want to insert in your image."));
155 
156     // -------------------------------------------------------------
157 
158     d->fontChooserWidget = new DFontProperties(nullptr, DFontProperties::NoDisplayFlags);
159     d->fontChooserWidget->setSampleBoxVisible(false);
160     d->fontChooserWidget->setWhatsThis(i18n("Here you can choose the font to be used."));
161 
162     // -------------------------------------------------------------
163 
164     QWidget* const alignBox   = new QWidget();
165     QHBoxLayout* const hlay   = new QHBoxLayout(alignBox);
166     d->alignButtonGroup       = new QButtonGroup(alignBox);
167     d->alignButtonGroup->setExclusive(true);
168 
169     QToolButton* const alignLeft = new QToolButton(alignBox);
170     d->alignButtonGroup->addButton(alignLeft, InsertTextWidget::ALIGN_LEFT);
171     alignLeft->setIcon(QIcon::fromTheme(QLatin1String("format-justify-left")));
172     alignLeft->setCheckable(true);
173     alignLeft->setToolTip(i18n("Align text to the left"));
174 
175     QToolButton* const alignRight = new QToolButton(alignBox);
176     d->alignButtonGroup->addButton(alignRight, InsertTextWidget::ALIGN_RIGHT);
177     alignRight->setIcon(QIcon::fromTheme(QLatin1String("format-justify-right")));
178     alignRight->setCheckable(true);
179     alignRight->setToolTip(i18n("Align text to the right"));
180 
181     QToolButton* const alignCenter = new QToolButton(alignBox);
182     d->alignButtonGroup->addButton(alignCenter, InsertTextWidget::ALIGN_CENTER);
183     alignCenter->setIcon(QIcon::fromTheme(QLatin1String("format-justify-center")));
184     alignCenter->setCheckable(true);
185     alignCenter->setToolTip(i18n("Align text to center"));
186 
187     QToolButton* alignBlock = new QToolButton(alignBox);
188     d->alignButtonGroup->addButton(alignBlock, InsertTextWidget::ALIGN_BLOCK);
189     alignBlock->setIcon(QIcon::fromTheme(QLatin1String("format-justify-fill")));
190     alignBlock->setCheckable(true);
191     alignBlock->setToolTip(i18n("Align text to a block"));
192 
193     hlay->setContentsMargins(QMargins());
194     hlay->setSpacing(0);
195     hlay->addStretch();
196     hlay->addWidget(alignLeft);
197     hlay->addWidget(alignRight);
198     hlay->addWidget(alignCenter);
199     hlay->addWidget(alignBlock);
200     hlay->addStretch();
201 
202     // -------------------------------------------------------------
203 
204     QLabel* const label1 = new QLabel(i18n("Rotation:"));
205     d->textRotation      = new QComboBox();
206     d->textRotation->addItem(i18nc("no rotation", "None"));
207     d->textRotation->addItem(i18n("90 Degrees"));
208     d->textRotation->addItem(i18n("180 Degrees"));
209     d->textRotation->addItem(i18n("270 Degrees"));
210     d->textRotation->setWhatsThis(i18n("Select the text rotation to use here."));
211 
212     // -------------------------------------------------------------
213 
214     QLabel* const label2 = new QLabel(i18nc("font color", "Color:"));
215     d->fontColorButton   = new DColorSelector();
216     d->fontColorButton->setColor(Qt::black);
217     d->fontColorButton->setWhatsThis(i18n("Set here the font color to use."));
218 
219     // -------------------------------------------------------------
220 
221     QLabel* const label3 = new QLabel(i18nc("text opacity", "Opacity:"));
222     d->textOpacity       = new DIntNumInput();
223     d->textOpacity->setRange(0, 100, 1);
224     d->textOpacity->setDefaultValue(100);
225     d->textOpacity->setSuffix(QLatin1String("%"));
226     d->textOpacity->setWhatsThis(i18n("Select the text opacity to use here."));
227 
228     // -------------------------------------------------------------
229 
230     d->borderText      = new QCheckBox(i18n("Add border"));
231     d->borderText->setToolTip(i18n("Add a solid border around text using current text color"));
232 
233     d->transparentText = new QCheckBox(i18n("Semi-transparent"));
234     d->transparentText->setToolTip(i18n("Use semi-transparent text background under image"));
235 
236     // -------------------------------------------------------------
237 
238     const int spacing = d->gboxSettings->spacingHint();
239 
240     QGridLayout* const mainLayout = new QGridLayout();
241     mainLayout->addWidget(d->textEdit,             0, 0, 3, -1);
242     mainLayout->addWidget(d->fontChooserWidget,    3, 0, 1, -1);
243     mainLayout->addWidget(alignBox,                4, 0, 1, -1);
244     mainLayout->addWidget(label1,                  5, 0, 1,  1);
245     mainLayout->addWidget(d->textRotation,         5, 1, 1,  1);
246     mainLayout->addWidget(label2,                  6, 0, 1,  1);
247     mainLayout->addWidget(d->fontColorButton,      6, 1, 1,  1);
248     mainLayout->addWidget(label3,                  7, 0, 1,  1);
249     mainLayout->addWidget(d->textOpacity,          7, 1, 1,  1);
250     mainLayout->addWidget(d->borderText,           8, 0, 1, -1);
251     mainLayout->addWidget(d->transparentText,      9, 0, 1, -1);
252     mainLayout->setRowStretch(10, 10);
253     mainLayout->setColumnStretch(1, 5);
254     mainLayout->setColumnStretch(2, 10);
255     mainLayout->setContentsMargins(spacing, spacing, spacing, spacing);
256     mainLayout->setSpacing(spacing);
257     d->gboxSettings->plainPage()->setLayout(mainLayout);
258 
259     // -------------------------------------------------------------
260 
261     setToolSettings(d->gboxSettings);
262 
263     // -------------------------------------------------------------
264 
265     connect(d->fontChooserWidget, SIGNAL(fontSelected(QFont)),
266             this, SLOT(slotFontPropertiesChanged(QFont)));
267 
268     connect(d->fontColorButton, SIGNAL(signalColorSelected(QColor)),
269             this, SLOT(slotUpdatePreview()));
270 
271     connect(d->textOpacity, SIGNAL(valueChanged(int)),
272             this, SLOT(slotUpdatePreview()));
273 
274     connect(d->textEdit, SIGNAL(textChanged()),
275             this, SLOT(slotUpdatePreview()));
276 
277     connect(d->alignButtonGroup, SIGNAL(buttonReleased(int)),
278             this, SLOT(slotAlignModeChanged(int)));
279 
280     connect(d->borderText, SIGNAL(toggled(bool)),
281             this, SLOT(slotUpdatePreview()));
282 
283     connect(d->transparentText, SIGNAL(toggled(bool)),
284             this, SLOT(slotUpdatePreview()));
285 
286     connect(d->textRotation, SIGNAL(activated(int)),
287             this, SLOT(slotUpdatePreview()));
288 
289     connect(this, SIGNAL(signalUpdatePreview()),
290             this, SLOT(slotUpdatePreview()));
291 
292     // -------------------------------------------------------------
293 
294     slotUpdatePreview();
295 }
296 
~InsertTextTool()297 InsertTextTool::~InsertTextTool()
298 {
299     delete d;
300 }
301 
readSettings()302 void InsertTextTool::readSettings()
303 {
304     KSharedConfig::Ptr config = KSharedConfig::openConfig();
305     KConfigGroup group        = config->group(d->configGroupName);
306     QColor black(0, 0, 0);
307     QFont  defaultFont;
308 
309     int orgW = d->previewWidget->imageIface()->originalSize().width();
310     int orgH = d->previewWidget->imageIface()->originalSize().height();
311 
312     if ( orgW > orgH )
313     {
314         d->defaultSizeFont = (int)(orgH / 8.0);
315     }
316     else
317     {
318         d->defaultSizeFont = (int)(orgW / 8.0);
319     }
320 
321     defaultFont.setPointSize(d->defaultSizeFont);
322 
323     d->textRotation->setCurrentIndex(group.readEntry(d->configTextRotationEntry,  0));
324     d->fontColorButton->setColor(group.readEntry(d->configFontColorEntry,         black));
325     d->textOpacity->setValue(group.readEntry(d->configTextOpacity,                100));
326     d->textEdit->setText(group.readEntry(d->configTextStringEntry,                i18n("Enter your text here.")));
327     d->textFont = group.readEntry(d->configFontPropertiesEntry,                   defaultFont);
328     d->fontChooserWidget->setFont(d->textFont);
329     d->alignTextMode = group.readEntry(d->configTextAlignmentEntry,               (int) InsertTextWidget::ALIGN_LEFT);
330     d->borderText->setChecked(group.readEntry(d->configBorderTextEntry,           false));
331     d->transparentText->setChecked(group.readEntry(d->configTransparentTextEntry, false));
332     d->previewWidget->setPositionHint(group.readEntry(d->configPositionHintEntry, QRect()));
333 
334     d->alignButtonGroup->button(d->alignTextMode)->setChecked(true);
335     slotAlignModeChanged(d->alignTextMode);
336 }
337 
writeSettings()338 void InsertTextTool::writeSettings()
339 {
340     KSharedConfig::Ptr config = KSharedConfig::openConfig();
341     KConfigGroup group        = config->group(d->configGroupName);
342 
343     group.writeEntry(d->configTextRotationEntry,    d->textRotation->currentIndex());
344     group.writeEntry(d->configFontColorEntry,       d->fontColorButton->color());
345     group.writeEntry(d->configTextOpacity,          d->textOpacity->value());
346     group.writeEntry(d->configTextStringEntry,      d->textEdit->toPlainText());
347     group.writeEntry(d->configFontPropertiesEntry,  d->textFont);
348     group.writeEntry(d->configTextAlignmentEntry,   d->alignTextMode);
349     group.writeEntry(d->configBorderTextEntry,      d->borderText->isChecked());
350     group.writeEntry(d->configTransparentTextEntry, d->transparentText->isChecked());
351     group.writeEntry(d->configPositionHintEntry,    d->previewWidget->getPositionHint());
352 
353     config->sync();
354 }
355 
slotResetSettings()356 void InsertTextTool::slotResetSettings()
357 {
358     d->fontColorButton->blockSignals(true);
359     d->alignButtonGroup->blockSignals(true);
360     d->fontChooserWidget->blockSignals(true);
361 
362     d->textRotation->setCurrentIndex(0); // No rotation.
363     d->fontColorButton->setColor(Qt::black);
364     d->textOpacity->slotReset();
365     QFont defaultFont;
366     d->textFont = defaultFont;           // Reset to default KDE font.
367     d->textFont.setPointSize(d->defaultSizeFont);
368     d->fontChooserWidget->setFont(d->textFont);
369     d->borderText->setChecked(false);
370     d->transparentText->setChecked(false);
371     d->previewWidget->resetEdit();
372     d->alignButtonGroup->button(InsertTextWidget::ALIGN_LEFT)->setChecked(true);
373 
374     d->fontChooserWidget->blockSignals(false);
375     d->fontColorButton->blockSignals(false);
376     d->alignButtonGroup->blockSignals(false);
377     slotAlignModeChanged(InsertTextWidget::ALIGN_LEFT);
378 }
379 
slotAlignModeChanged(int mode)380 void InsertTextTool::slotAlignModeChanged(int mode)
381 {
382     d->alignTextMode = mode;
383     d->textEdit->selectAll();
384 
385     switch (d->alignTextMode)
386     {
387         case InsertTextWidget::ALIGN_LEFT:
388             d->textEdit->setAlignment(Qt::AlignLeft);
389             break;
390 
391         case InsertTextWidget::ALIGN_RIGHT:
392             d->textEdit->setAlignment(Qt::AlignRight);
393             break;
394 
395         case InsertTextWidget::ALIGN_CENTER:
396             d->textEdit->setAlignment(Qt::AlignHCenter);
397             break;
398 
399         case InsertTextWidget::ALIGN_BLOCK:
400             d->textEdit->setAlignment(Qt::AlignJustify);
401             break;
402     }
403 
404     d->textEdit->textCursor().clearSelection();
405     emit signalUpdatePreview();
406 }
407 
slotFontPropertiesChanged(const QFont & font)408 void InsertTextTool::slotFontPropertiesChanged(const QFont& font)
409 {
410     d->textFont = font;
411     emit signalUpdatePreview();
412 }
413 
setBackgroundColor(const QColor & bg)414 void InsertTextTool::setBackgroundColor(const QColor& bg)
415 {
416     d->previewWidget->setBackgroundColor(bg);
417 }
418 
slotUpdatePreview()419 void InsertTextTool::slotUpdatePreview()
420 {
421     d->previewWidget->setText(d->textEdit->toPlainText(), d->textFont, d->fontColorButton->color(),
422                               d->textOpacity->value(), d->alignTextMode,
423                               d->borderText->isChecked(), d->transparentText->isChecked(),
424                               d->textRotation->currentIndex());
425 }
426 
finalRendering()427 void InsertTextTool::finalRendering()
428 {
429     qApp->setOverrideCursor( Qt::WaitCursor );
430 
431     ImageIface iface;
432     DImg dest = d->previewWidget->makeInsertText();
433 
434     FilterAction action(QLatin1String("digikam:insertTextTool"), 2);
435     action.setDisplayableName(i18n("Insert Text Tool"));
436 
437     action.addParameter(QLatin1String("text"),              d->textEdit->toPlainText());
438     action.addParameter(QLatin1String("textRotationIndex"), d->textRotation->currentIndex());
439     action.addParameter(QLatin1String("textFont"),          d->textFont.toString());
440     action.addParameter(QLatin1String("colorR"),            d->fontColorButton->color().red());
441     action.addParameter(QLatin1String("colorG"),            d->fontColorButton->color().green());
442     action.addParameter(QLatin1String("colorB"),            d->fontColorButton->color().blue());
443     action.addParameter(QLatin1String("colorA"),            d->fontColorButton->color().alpha());
444     action.addParameter(QLatin1String("textOpacity"),       d->textOpacity->value());
445     action.addParameter(QLatin1String("borderText"),        d->borderText->isChecked());
446     action.addParameter(QLatin1String("transparentText"),   d->transparentText->isChecked());
447 
448     iface.setOriginal(i18n("Insert Text"), action, dest);
449 
450     qApp->restoreOverrideCursor();
451 }
452 
453 } // namespace DigikamEditorInsertTextToolPlugin
454