1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2010-06-21
7  * Description : Demo test program for FacesEngine
8  *
9  * Copyright (C) 2009-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C)      2010 by Alex Jironkin <alexjironkin at gmail dot com>
11  * Copyright (C)      2010 by Aditya Bhatt <adityabhatt1991 at gmail dot com>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 #include "demofaceitem.h"
27 
28 // Qt includes
29 
30 #include <QWidget>
31 #include <QPainter>
32 #include <QGraphicsOpacityEffect>
33 #include <QTextDocument>
34 #include <QGraphicsScene>
35 #include <QColor>
36 #include <QIcon>
37 #include <qmath.h>
38 
39 // Local includes
40 
41 #include "digikam_debug.h"
42 #include "demomarquee.h"
43 #include "demofancyrect.h"
44 
45 namespace FaceEngineDemo
46 {
47 
48 class Q_DECL_HIDDEN FaceItem::Private
49 {
50 public:
51 
Private()52     explicit Private()
53       : suggestionMode(false),
54         sceneWidth(0),
55         sceneHeight(0),
56         x1(0),
57         x2(0),
58         y1(0),
59         y2(0),
60         name(),
61         faceMarquee(nullptr),
62         faceName(nullptr),
63         nameRect(nullptr),
64         origRect(),
65         origScale(0.0),
66         scale(0.0),
67         rejectButton(nullptr),
68         acceptButton(nullptr),
69         suggestionRejectButton(nullptr),
70         suggestionAcceptButton(nullptr)
71     {
72     }
73 
74     bool               suggestionMode;
75 
76     int                sceneWidth, sceneHeight;
77     int                x1, x2, y1, y2;
78 
79     QString            name;
80     Marquee*           faceMarquee;
81 
82     QGraphicsTextItem* faceName;
83     QGraphicsRectItem* nameRect;
84 
85     QRect              origRect;
86     double             origScale;
87     double             scale;
88 
89     Button*            rejectButton;
90     Button*            acceptButton;
91 
92     Button*            suggestionRejectButton;
93     Button*            suggestionAcceptButton;
94 };
95 
FaceItem(QGraphicsItem * const parent,QGraphicsScene * const scene,const QRect & rect,double scale,const QString & name,double originalscale)96 FaceItem::FaceItem(QGraphicsItem* const parent,
97                    QGraphicsScene* const scene,
98                    const QRect& rect,
99                    double scale,
100                    const QString& name,
101                    double originalscale)
102         : QGraphicsObject(parent),
103           d(new Private)
104 {
105     setAcceptHoverEvents(true);
106 
107     d->origScale     = originalscale;
108     d->scale         = scale;
109     d->origRect      = rect;
110     d->sceneWidth    = scene->width();
111     d->sceneHeight   = scene->height();
112 
113     // Scale all coordinates to fit the initial size of the scene
114 
115     d->x1 = rect.topLeft().x()     * scale;
116     d->y1 = rect.topLeft().y()     * scale;
117     d->x2 = rect.bottomRight().x() * scale;
118     d->y2 = rect.bottomRight().y() * scale;
119 
120     // A QRect containing coordinates for the face rectangle
121 
122     QRect scaledRect;
123     scaledRect.setTopLeft(QPoint(d->x1, d->y1));
124     scaledRect.setBottomRight(QPoint(d->x2, d->y2));
125 
126     // marquee
127 
128     FancyRect* const fancy = new FancyRect(scaledRect);
129     d->faceMarquee         = new Marquee(fancy);
130     scene->addItem(d->faceMarquee);
131 
132     // Make a new QGraphicsTextItem for writing the name text, and a new QGraphicsRectItem to draw a good-looking, semi-transparent bounding box.
133 
134     d->nameRect = new QGraphicsRectItem(nullptr);
135     scene->addItem(d->nameRect);
136 
137     d->faceName = new QGraphicsTextItem(name, nullptr);
138     scene->addItem(d->faceName);
139 
140     // Make the bounding box for the name update itself to cover all the text whenever contents are changed
141 
142     QTextDocument* const doc = d->faceName->document();
143     QTextOption o;
144     o.setAlignment(Qt::AlignCenter);
145     doc->setDefaultTextOption(o);
146 
147     // Get coordinates of the name relative to the scene
148 
149     QRectF r = d->faceName->mapRectToScene(d->faceName->boundingRect());
150 
151     // Draw the bounding name rectangle with the scene coordinates
152 
153     d->nameRect->setRect(r);
154     QPen p(QColor(QLatin1String("white")));
155     p.setWidth(3);
156     d->nameRect->setPen(p);
157     d->nameRect->setBrush(QBrush(QColor(QLatin1String("black"))));
158     d->nameRect->setOpacity(0.6);
159     d->nameRect->show();
160 
161     // Draw the name input item
162 
163     d->faceName->setDefaultTextColor(QColor(QLatin1String("white")));
164     d->faceName->setFont(QFont(QLatin1String("Helvetica"), 9));
165     d->faceName->setTextInteractionFlags(Qt::TextEditorInteraction);
166     d->faceName->setOpacity(1);
167 
168     //---------------------
169 
170     QPixmap rejectPix  = QIcon::fromTheme(QLatin1String("window-close")).pixmap(16, 16);
171     d->rejectButton    = new Button( rejectPix, rejectPix);
172     scene->addItem(d->rejectButton);
173     d->rejectButton->show();
174 
175     QPixmap acceptPix  = QIcon::fromTheme(QLatin1String("dialog-ok-apply")).pixmap(16, 16);
176     d->acceptButton    = new Button( acceptPix, acceptPix);
177     scene->addItem(d->acceptButton);
178 /*
179     d->acceptButton->show();
180 */
181     d->suggestionRejectButton = new Button(rejectPix, rejectPix);
182     scene->addItem(d->suggestionRejectButton);
183     //d->suggestionAcceptButton->hide();
184 
185     d->suggestionAcceptButton = new Button(acceptPix, acceptPix);
186     scene->addItem(d->suggestionAcceptButton);
187 /*
188     d->suggestionRejectButton->hide();
189 */
190     update();
191 
192     switchToEditMode();
193 
194     d->acceptButton->hide();
195 
196     connect(d->rejectButton, &Button::clicked,
197             this, &FaceItem::reject);
198 
199     connect(d->acceptButton, &Button::clicked,
200             this, &FaceItem::accepted);
201 
202     connect(d->suggestionAcceptButton, &Button::clicked,
203             this, &FaceItem::slotSuggestionAccepted);
204 
205     connect(d->suggestionRejectButton, &Button::clicked, this,
206             &FaceItem::slotSuggestionRejected);
207 
208     connect(doc, SIGNAL(contentsChanged()),
209             this, SLOT(update()));
210 
211     connect(d->faceMarquee, &Marquee::changed,
212             this, &FaceItem::update);
213 }
214 
~FaceItem()215 FaceItem::~FaceItem()
216 {
217     delete d;
218 }
219 
boundingRect() const220 QRectF FaceItem::boundingRect() const
221 {
222     qreal adjust = 0.5;
223 
224     return QRectF(-18 - adjust,
225                   -22 - adjust,
226                    36 + adjust,
227                    60 + adjust);
228 }
229 
setText(const QString & newName)230 void FaceItem::setText(const QString& newName)
231 {
232     d->faceName->setHtml(newName);
233 }
234 
text() const235 QString FaceItem::text() const
236 {
237     return d->faceName->toPlainText().remove(QLatin1String("?"));
238 }
239 
update()240 void FaceItem::update()
241 {
242     if (text().isEmpty())
243     {
244         d->faceName->setDefaultTextColor(QColor("white"));
245         d->nameRect->setPen(QPen(QColor("white")));
246         d->acceptButton->hide();
247         d->name.clear();
248     }
249     else
250     {
251         d->nameRect->setPen(QPen(QColor("yellow")));
252         d->faceName->setDefaultTextColor(QColor("yellow"));
253 
254         if (!d->suggestionMode)
255         {
256             d->acceptButton->show();
257             d->name = text();
258         }
259     }
260 
261     QPointF bl = d->faceMarquee->mapRectToScene(d->faceMarquee->boundingRect()).bottomLeft();
262     QPointF br = d->nameRect->mapRectToScene(d->nameRect->boundingRect()).bottomRight();
263     d->faceName->setPos(bl.x() + 5, bl.y() + 5);
264 
265     d->rejectButton->setPos(bl.x() - 16, bl.y() + 9);
266     d->acceptButton->setPos(br.x() + 4,  bl.y() + 11);
267 
268     d->suggestionAcceptButton->setPos(br.x() + 4,  bl.y() + 11);
269     d->suggestionRejectButton->setPos(br.x() + 20, bl.y() + 11);
270 
271     QRectF r      = d->faceName->mapRectToScene(d->faceName->boundingRect());
272     d->nameRect->setRect(r);
273     QRect newRect = this->d->faceMarquee->mapRectToScene(d->faceMarquee->boundingRect()).toRect();
274     qCDebug(DIGIKAM_TESTS_LOG) << "Origscale is : " << d->origScale << " and scale is " << d->scale;
275 
276     QSize s(newRect.size());
277     s.scale(newRect.width() * qSqrt(d->origScale), newRect.height() * qSqrt(d->origScale), Qt::KeepAspectRatio);
278     newRect.setSize(s);
279 /*
280     newRect.setRect(x,y,w,h);
281 */
282     qCDebug(DIGIKAM_TESTS_LOG) << "Orig before" << d->origRect;
283 /*
284     d->origRect = newRect;
285 */
286     qCDebug(DIGIKAM_TESTS_LOG) << "Orig after" << d->origRect;
287 }
288 
setVisible(bool visible)289 void FaceItem::setVisible(bool visible)
290 {
291     d->faceMarquee->setVisible(visible);
292     setControlsVisible(visible);
293 }
294 
setControlsVisible(bool visible)295 void FaceItem::setControlsVisible(bool visible)
296 {
297     d->nameRect->setVisible(visible);
298     d->faceName->setVisible(visible);
299 
300     if (d->suggestionMode)
301     {
302         d->suggestionAcceptButton->setVisible(visible);
303         d->suggestionRejectButton->setVisible(visible);
304     }
305     else
306     {
307         d->rejectButton->setVisible(visible);
308         d->acceptButton->setVisible(visible);
309     }
310 }
311 
clearText()312 void FaceItem::clearText()
313 {
314     d->faceName->setPlainText(QString());
315 }
316 
clearAndHide()317 void FaceItem::clearAndHide()
318 {
319     clearText();
320     setVisible(false);
321 }
322 
accepted()323 void FaceItem::accepted()
324 {
325     d->acceptButton->hide();
326     d->faceName->setDefaultTextColor(QColor("white"));
327     d->nameRect->setPen(QPen(QColor("white")));
328     emit acceptButtonClicked(this->text(), this->originalRect());
329 }
330 
originalRect() const331 QRect FaceItem::originalRect() const
332 {
333     return d->origRect;
334 }
335 
originalScale() const336 double FaceItem::originalScale() const
337 {
338     return d->origScale;
339 }
340 
reject()341 void FaceItem::reject()
342 {
343     emit rejectButtonClicked(this->text(), this->originalRect());
344     clearAndHide();
345 }
346 
suggest(const QString & name)347 void FaceItem::suggest(const QString& name)
348 {
349     qCDebug(DIGIKAM_TESTS_LOG) << "suggested name is " << name;
350     d->name = name;
351     this->switchToSuggestionMode();
352 }
353 
switchToEditMode()354 void FaceItem::switchToEditMode()
355 {
356     d->suggestionMode = false;
357     d->faceName->setEnabled(true);
358     d->faceName->setHtml(QLatin1String("<b>") + d->name + QLatin1String("</b>"));
359     d->acceptButton->show();
360     d->suggestionAcceptButton->hide();
361     d->suggestionRejectButton->hide();
362 }
363 
switchToSuggestionMode()364 void FaceItem::switchToSuggestionMode()
365 {
366     d->suggestionMode = true;
367     d->faceName->setEnabled(false);
368     d->faceName->setHtml(QLatin1String("Is this <b>") + d->name + QLatin1String("</b> ?"));
369     d->acceptButton->hide();
370     d->suggestionAcceptButton->show();
371     d->suggestionRejectButton->show();
372 }
373 
slotSuggestionAccepted()374 void FaceItem::slotSuggestionAccepted()
375 {
376     switchToEditMode();
377     d->faceName->setHtml(QLatin1String("<b>") + d->name + QLatin1String("</b>"));
378     accepted();
379     emit suggestionAcceptButtonClicked(this->text(), this->originalRect());
380 }
381 
slotSuggestionRejected()382 void FaceItem::slotSuggestionRejected()
383 {
384     switchToEditMode();
385     d->faceName->setHtml(QLatin1String("<b>") + QString() + QLatin1String("</b>"));
386     emit suggestionRejectButtonClicked(this->text(), this->originalRect());
387 }
388 
paint(QPainter *,const QStyleOptionGraphicsItem *,QWidget *)389 void FaceItem::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
390 {
391 }
392 
393 } // namespace FaceEngineDemo
394