File indexing completed on 2025-01-19 03:57:57

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-09-12
0007  * Description : Widget for assignment and confirmation of names for faces
0008  *
0009  * SPDX-FileCopyrightText: 2010      by Aditya Bhatt <adityabhatt1991 at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "assignnamewidget_p.h"
0018 
0019 namespace Digikam
0020 {
0021 
0022 AssignNameWidget::Private::Private(AssignNameWidget* const q)
0023     : mode            (InvalidMode),
0024       layoutMode      (InvalidLayout),
0025       visualStyle     (InvalidVisualStyle),
0026       widgetMode      (InvalidTagEntryWidgetMode),
0027       comboBox        (nullptr),
0028       lineEdit        (nullptr),
0029       confirmButton   (nullptr),
0030       rejectButton    (nullptr),
0031       ignoreButton    (nullptr),
0032       clickLabel      (nullptr),
0033       modelsGiven     (false),
0034       tagModel        (nullptr),
0035       tagFilterModel  (nullptr),
0036       tagFilteredModel(nullptr),
0037       layout          (nullptr),
0038       q               (q)
0039 {
0040 }
0041 
0042 QWidget* AssignNameWidget::Private::addTagsWidget() const
0043 {
0044     if (comboBox)
0045     {
0046         return comboBox;
0047     }
0048     else
0049     {
0050         return lineEdit;
0051     }
0052 }
0053 
0054 bool AssignNameWidget::Private::isValid() const
0055 {
0056     return (
0057             (mode        != InvalidMode)                  &&
0058             (layoutMode  != InvalidLayout)                &&
0059             (visualStyle != InvalidVisualStyle)           &&
0060             (widgetMode  != InvalidTagEntryWidgetMode)
0061            );
0062 }
0063 
0064 void AssignNameWidget::Private::clearWidgets()
0065 {
0066     delete comboBox;
0067     comboBox      = nullptr;
0068 
0069     delete lineEdit;
0070     lineEdit      = nullptr;
0071 
0072     delete confirmButton;
0073     confirmButton = nullptr;
0074 
0075     delete rejectButton;
0076     rejectButton  = nullptr;
0077 
0078     delete ignoreButton;
0079     ignoreButton  = nullptr;
0080 
0081     delete clickLabel;
0082     clickLabel    = nullptr;
0083 }
0084 
0085 QToolButton* AssignNameWidget::Private::createToolButton(const QIcon& icon,
0086                                                          const QString& text,
0087                                                          const QString& tip) const
0088 {
0089     QToolButton* const b = new QToolButton;
0090     b->setIcon(icon);
0091     b->setText(text);
0092     b->setToolTip(tip);
0093     b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0094 
0095     return b;
0096 }
0097 
0098 void AssignNameWidget::Private::updateModes()
0099 {
0100     if (isValid())
0101     {
0102         clearWidgets();
0103         checkWidgets();
0104         updateIgnoreButton();
0105         updateLayout();
0106         updateVisualStyle();
0107     }
0108 }
0109 
0110 template <class T>
0111 void AssignNameWidget::Private::setupAddTagsWidget(T* const widget)
0112 {
0113     if (modelsGiven)
0114     {
0115         widget->setAlbumModels(tagModel, tagFilteredModel, tagFilterModel);
0116     }
0117 
0118     if (parentTag)
0119     {
0120         widget->setParentTag(parentTag);
0121     }
0122 
0123     q->connect(widget, SIGNAL(taggingActionActivated(TaggingAction)),
0124                q, SLOT(slotActionActivated(TaggingAction)));
0125 
0126     q->connect(widget, SIGNAL(taggingActionSelected(TaggingAction)),
0127                q, SLOT(slotActionSelected(TaggingAction)));
0128 }
0129 
0130 void AssignNameWidget::Private::checkWidgets()
0131 {
0132     if (!isValid())
0133     {
0134         return;
0135     }
0136 
0137     switch (mode)
0138     {
0139         case InvalidMode:
0140         {
0141             break;
0142         }
0143 
0144         case UnconfirmedEditMode:
0145         case ConfirmedEditMode:
0146         {
0147             switch (widgetMode)
0148             {
0149                 case InvalidTagEntryWidgetMode:
0150                 {
0151                     break;
0152                 }
0153 
0154                 case AddTagsComboBoxMode:
0155                 {
0156                     if (!comboBox)
0157                     {
0158                         comboBox = new AddTagsComboBox(q);
0159                         setupAddTagsWidget(comboBox);
0160                     }
0161 
0162                     break;
0163                 }
0164 
0165                 case AddTagsLineEditMode:
0166                 {
0167                     if (!lineEdit)
0168                     {
0169                         lineEdit = new AddTagsLineEdit(q);
0170                         setupAddTagsWidget(lineEdit);
0171                     }
0172 
0173                     break;
0174                 }
0175             }
0176 
0177             if (!confirmButton)
0178             {
0179                 confirmButton = createToolButton(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), i18nc("@action", "OK"));
0180                 confirmButton->setToolTip(i18nc("@info:tooltip", "Confirm that the selected person is shown here"));
0181 
0182                 if (mode == UnconfirmedEditMode)
0183                 {
0184                     confirmButton->setText(i18nc("@action", "Confirm"));
0185                 }
0186 
0187                 q->connect(confirmButton, SIGNAL(clicked()),
0188                            q, SLOT(slotConfirm()));
0189             }
0190 
0191             if (!rejectButton)
0192             {
0193                 rejectButton = createToolButton(QIcon::fromTheme(QLatin1String("list-remove")), i18nc("@action", "Remove"));
0194                 rejectButton->setToolTip(i18nc("@info:tooltip", "Remove this face region"));
0195 
0196                 q->connect(rejectButton, SIGNAL(clicked()),
0197                            q, SLOT(slotReject()));
0198             }
0199 
0200             break;
0201         }
0202 
0203         case IgnoredMode:
0204         {
0205             if (layoutMode == Compact)
0206             {
0207                 if (!confirmButton)
0208                 {
0209                     confirmButton = createToolButton(QIcon::fromTheme(QLatin1String("edit-undo")), i18nc("@action", "OK"));
0210                     confirmButton->setToolTip(i18nc("@info:tooltip", "Unmark this face as Ignored"));
0211 
0212                     q->connect(confirmButton, SIGNAL(clicked()),
0213                                q, SLOT(slotIgnoredClicked()));
0214                 }
0215 
0216                 if (!rejectButton)
0217                 {
0218                     rejectButton = createToolButton(QIcon::fromTheme(QLatin1String("list-remove")), i18nc("@action", "Remove"));
0219                     rejectButton->setToolTip(i18nc("@info:tooltip", "Remove this face region"));
0220 
0221                     q->connect(rejectButton, SIGNAL(clicked()),
0222                                q, SLOT(slotReject()));
0223                 }
0224             }
0225             else
0226             {
0227                 clickLabel = new DClickLabel;
0228                 clickLabel->setAlignment(Qt::AlignCenter);
0229 
0230                 connect(clickLabel, SIGNAL(activated()),
0231                         q, SLOT(slotIgnoredClicked()));
0232             }
0233 
0234             break;
0235         }
0236 
0237         case ConfirmedMode:
0238         {
0239             clickLabel = new DClickLabel;
0240             clickLabel->setAlignment(Qt::AlignCenter);
0241 
0242             connect(clickLabel, SIGNAL(activated()),
0243                     q, SLOT(slotLabelClicked()));
0244 
0245             break;
0246         }
0247     }
0248 }
0249 
0250 void AssignNameWidget::Private::layoutAddTagsWidget(bool exceedBounds, int minimumContentsLength)
0251 {
0252     if      (comboBox)
0253     {
0254         comboBox->setMinimumContentsLength(minimumContentsLength);
0255         comboBox->lineEdit()->setAllowExceedBound(exceedBounds);
0256     }
0257     else if (lineEdit)
0258     {
0259         lineEdit->setAllowExceedBound(exceedBounds);
0260     }
0261 }
0262 
0263 void AssignNameWidget::Private::setSizePolicies(QSizePolicy::Policy h, QSizePolicy::Policy v)
0264 {
0265     confirmButton->setSizePolicy(h, v);
0266     rejectButton->setSizePolicy(h, v);
0267 
0268     if (ignoreButton)
0269     {
0270         ignoreButton->setSizePolicy(h, v);
0271     }
0272 
0273     addTagsWidget()->setSizePolicy(h, v);
0274 }
0275 
0276 void AssignNameWidget::Private::setToolButtonStyles(Qt::ToolButtonStyle style)
0277 {
0278     confirmButton->setToolButtonStyle(style);
0279     rejectButton->setToolButtonStyle(style);
0280 
0281     if (ignoreButton)
0282     {
0283         ignoreButton->setToolButtonStyle(style);
0284     }
0285 }
0286 
0287 void AssignNameWidget::Private::updateLayout()
0288 {
0289     if (!isValid())
0290     {
0291         return;
0292     }
0293 
0294     delete layout;
0295     layout = new QGridLayout;
0296 
0297     switch (mode)
0298     {
0299         case InvalidMode:
0300         {
0301             break;
0302         }
0303 
0304         case UnconfirmedEditMode:
0305         case ConfirmedEditMode:
0306         {
0307             switch (layoutMode)
0308             {
0309                 case InvalidLayout:
0310                 {
0311                     break;
0312                 }
0313 
0314                 case FullLine:
0315                 {
0316                     layout->addWidget(addTagsWidget(), 0, 0);
0317                     layout->addWidget(confirmButton,   0, 1);
0318                     layout->addWidget(rejectButton,    0, 2);
0319                     layout->addWidget(ignoreButton,    0, 3);
0320                     layout->setColumnStretch(0, 1);
0321 
0322                     setSizePolicies(QSizePolicy::Expanding, QSizePolicy::Preferred);
0323                     setToolButtonStyles(Qt::ToolButtonTextBesideIcon);
0324                     layoutAddTagsWidget(false, 15);
0325 
0326                     break;
0327                 }
0328 
0329                 case TwoLines:
0330                 case Compact:
0331                 {
0332                     layout->addWidget(addTagsWidget(), 0, 0, 1, 3);
0333                     layout->addWidget(confirmButton,   1, 0);
0334                     layout->addWidget(rejectButton,    1, 1);
0335                     layout->addWidget(ignoreButton,    1, 2);
0336 
0337                     setSizePolicies(QSizePolicy::Expanding, QSizePolicy::Minimum);
0338 
0339                     if (layoutMode == AssignNameWidget::TwoLines)
0340                     {
0341                         setToolButtonStyles(Qt::ToolButtonTextBesideIcon);
0342                         layoutAddTagsWidget(true, 10);
0343                     }
0344                     else
0345                     {
0346                         setToolButtonStyles(Qt::ToolButtonIconOnly);
0347                         layoutAddTagsWidget(true, 0);
0348                     }
0349 
0350                     break;
0351                 }
0352             }
0353 
0354             break;
0355         }
0356 
0357         case IgnoredMode:
0358         {
0359             if (layoutMode == Compact)
0360             {
0361                 layout->addWidget(confirmButton, 0, 0);
0362                 layout->addWidget(rejectButton,  0, 1);
0363 
0364                 confirmButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
0365                 rejectButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
0366                 setToolButtonStyles(Qt::ToolButtonIconOnly);
0367                 layoutAddTagsWidget(true, 0);
0368             }
0369             else
0370             {
0371                 layout->addWidget(clickLabel, 0, 0);
0372             }
0373 
0374             break;
0375         }
0376 
0377         case ConfirmedMode:
0378         {
0379             layout->addWidget(clickLabel, 0, 0);
0380 
0381             break;
0382         }
0383     }
0384 
0385     layout->setContentsMargins(1, 1, 1, 1);
0386     layout->setSpacing(1);
0387     q->setLayout(layout);
0388 }
0389 
0390 QString AssignNameWidget::Private::styleSheetFontDescriptor(const QFont& font) const
0391 {
0392     QString s;
0393 
0394     s += (font.pointSize() == -1) ? QString::fromUtf8("font-size: %1px; ").arg(font.pixelSize())
0395                                   : QString::fromUtf8("font-size: %1pt; ").arg(font.pointSize());
0396     s += QString::fromUtf8("font-family: \"%1\"; ").arg(font.family());
0397 
0398     return s;
0399 }
0400 
0401 void AssignNameWidget::Private::updateVisualStyle()
0402 {
0403     if (!isValid())
0404     {
0405         return;
0406     }
0407 
0408     QFont appFont = ApplicationSettings::instance()->getApplicationFont();
0409 
0410     if (appFont.pointSize() == -1)
0411     {
0412         appFont.setPixelSize(appFont.pixelSize() - 1);
0413     }
0414     else
0415     {
0416         appFont.setPointSize(appFont.pointSize() - 1);
0417     }
0418 
0419     switch (visualStyle)
0420     {
0421         case InvalidVisualStyle:
0422         {
0423             break;
0424         }
0425 
0426         case TranslucentDarkRound:
0427         {
0428             q->setStyleSheet(
0429                 QString::fromUtf8(
0430                     "QWidget { "
0431                     " %1 "
0432                     "} "
0433 
0434                     "QFrame {"
0435                     "  background-color: rgba(0, 0, 0, 66%); "
0436                     "  border: 1px solid rgba(100, 100, 100, 66%); "
0437                     "  border-radius: %2px; "
0438                     "} "
0439 
0440                     "QToolButton { "
0441                     "  color: rgba(255, 255, 255, 220); "
0442                     "  padding: 1px; "
0443                     "  background-color: "
0444                     "    qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 100), "
0445                     "                    stop:1 rgba(255, 255, 255, 0)); "
0446                     "  border: 1px solid rgba(255, 255, 255, 127); "
0447                     "  border-radius: 4px; "
0448                     "} "
0449 
0450                     "QToolButton:hover { "
0451                     "  border-color: white; "
0452                     "} "
0453 
0454                     "QToolButton:pressed { "
0455                     "  background-color: "
0456                     "    qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 0), "
0457                     "                    stop:1 rgba(255, 255, 255, 100)); "
0458                     "  border-color: white; "
0459                     "} "
0460 
0461                     "QToolButton:disabled { "
0462                     "  color: rgba(255, 255, 255, 120); "
0463                     "  background-color: "
0464                     "    qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 50), "
0465                     "                    stop:1 rgba(255, 255, 255, 0)); "
0466                     "} "
0467 
0468                     "QComboBox { "
0469                     "  color: white; "
0470                     "  background-color: transparent; "
0471                     "} "
0472 
0473                     "QComboBox QAbstractItemView, QListView::item:!selected { "
0474                     "  color: white; "
0475                     "  background-color: rgba(0, 0, 0, 80%); "
0476                     "} "
0477 
0478                     "QLabel { "
0479                     "  color: white; background-color: transparent; border: none; "
0480                     "}"
0481                 ).arg(styleSheetFontDescriptor(appFont))
0482                  .arg(((mode == ConfirmedMode) || (mode == IgnoredMode)) ? QLatin1String("8")
0483                                                                          : QLatin1String("4"))
0484             );
0485 
0486             break;
0487         }
0488 
0489         case TranslucentThemedFrameless:
0490         {
0491             QColor bg = qApp->palette().color(QPalette::Base);
0492             q->setStyleSheet(
0493                 QString::fromUtf8(
0494                     "QWidget { "
0495                     " %1 "
0496                     "} "
0497 
0498                     "QFrame#assignNameWidget {"
0499                     "  background-color: "
0500                     "    qradialgradient(cx:0.5 cy:0.5, fx:0, fy:0, radius: 1, stop:0 rgba(%2, %3, %4, 255), "
0501                     "                    stop:0.8 rgba(%2, %3, %4, 200), stop:1 rgba(%2, %3, %4, 0));"
0502                     "  border: none; "
0503                     "  border-radius: 8px; "
0504                     "}"
0505                 ).arg(styleSheetFontDescriptor(appFont))
0506                  .arg(bg.red())
0507                  .arg(bg.green())
0508                  .arg(bg.blue())
0509             );
0510 
0511             break;
0512         }
0513 
0514         case StyledFrame:
0515         {
0516             q->setStyleSheet(QString());
0517             q->setFrameStyle(Raised | StyledPanel);
0518 
0519             break;
0520         }
0521     }
0522 }
0523 
0524 template <class T>
0525 void AssignNameWidget::Private::setAddTagsWidgetContents(T* const widget)
0526 {
0527     if (widget)
0528     {
0529         widget->setCurrentTag(currentTag);
0530         widget->setPlaceholderText((mode == UnconfirmedEditMode) ? i18nc("@label", "Who is this?")
0531                                                                  : QString());
0532 
0533         if (confirmButton)
0534         {
0535             confirmButton->setEnabled(widget->currentTaggingAction().isValid());
0536         }
0537     }
0538 }
0539 
0540 void AssignNameWidget::Private::updateContents()
0541 {
0542     if (!isValid())
0543     {
0544         return;
0545     }
0546 
0547     if      (comboBox)
0548     {
0549         setAddTagsWidgetContents(comboBox);
0550     }
0551     else if (lineEdit)
0552     {
0553         setAddTagsWidgetContents(lineEdit);
0554     }
0555 
0556     if (clickLabel)
0557     {
0558         if (mode != IgnoredMode)
0559         {
0560             clickLabel->setText(currentTag ? currentTag->title()
0561                                            : QString());
0562         }
0563         else
0564         {
0565             int tagId = FaceTags::ignoredPersonTagId();
0566             clickLabel->setText(FaceTags::faceNameForTag(tagId));
0567         }
0568     }
0569 }
0570 
0571 void AssignNameWidget::Private::updateIgnoreButton()
0572 {
0573     if (!ignoreButton)
0574     {
0575         ignoreButton = createToolButton(QIcon::fromTheme(QLatin1String("dialog-cancel")),
0576                                         i18nc("@action", "Ignore"));
0577         ignoreButton->setToolTip(i18nc("@info:tooltip", "Ignore this face"));
0578 
0579         q->connect(ignoreButton, SIGNAL(clicked()),
0580                     q, SLOT(slotIgnore()));
0581     }
0582     else
0583     {
0584         FaceTagsIface face = FaceTagsIface::fromVariant(faceIdentifier);
0585 
0586         if ((face.type() == FaceTagsIface::IgnoredName) ||
0587             (face.type() == FaceTagsIface::UnknownName))
0588         {
0589             ignoreButton->hide();
0590         }
0591         else
0592         {
0593             ignoreButton->show();
0594         }
0595     }
0596 }
0597 
0598 void AssignNameWidget::Private::updateRejectButton()
0599 {
0600     if (!rejectButton)
0601     {
0602         return;
0603     }
0604 
0605     FaceTagsIface face = FaceTagsIface::fromVariant(faceIdentifier);
0606 
0607     if      (face.type() == FaceTagsIface::UnknownName)
0608     {
0609         rejectButton->setToolTip(i18nc("@info:tooltip", "Mark this face as Ignored"));
0610         rejectButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-cancel")));
0611     }
0612     else if (face.type() == FaceTagsIface::UnconfirmedName)
0613     {
0614         rejectButton->setToolTip(i18nc("@info:tooltip", "Reject this suggestion"));
0615         rejectButton->setIcon(QIcon::fromTheme(QLatin1String("list-remove")));
0616     }
0617 
0618     updateIgnoreButton();
0619 }
0620 
0621 } // namespace Digikam