File indexing completed on 2025-01-05 03:51:10
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-10-14 0007 * Description : overlay for assigning names to faces 0008 * 0009 * SPDX-FileCopyrightText: 2010 by Aditya Bhatt <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2009-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0011 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * SPDX-FileCopyrightText: 2008 by Peter Penz <peter dot penz at gmx dot at> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "assignnameoverlay.h" 0019 0020 // Qt includes 0021 0022 #include <QApplication> 0023 #include <QPushButton> 0024 0025 // Local includes 0026 0027 #include "dlayoutbox.h" 0028 #include "digikam_debug.h" 0029 #include "addtagslineedit.h" 0030 #include "albummodel.h" 0031 #include "albumfiltermodel.h" 0032 #include "assignnamewidget.h" 0033 #include "facetagsiface.h" 0034 #include "facepipeline.h" 0035 #include "facetags.h" 0036 #include "itemdelegate.h" 0037 #include "itemmodel.h" 0038 #include "itemcategorizedview.h" 0039 #include "taggingaction.h" 0040 #include "tagscache.h" 0041 #include "searchutilities.h" 0042 #include "applicationsettings.h" 0043 0044 namespace Digikam 0045 { 0046 0047 class Q_DECL_HIDDEN AssignNameOverlay::Private 0048 { 0049 public: 0050 0051 explicit Private() 0052 : tagModel (AbstractAlbumModel::IgnoreRootAlbum), 0053 assignNameWidget(nullptr) 0054 { 0055 } 0056 0057 bool isChildWidget(QWidget* widget, const QWidget* const parent) const 0058 { 0059 if (!parent) 0060 { 0061 return false; 0062 } 0063 0064 // isAncestorOf may not work if widgets are located in different windows 0065 0066 while (widget) 0067 { 0068 if (widget == parent) 0069 { 0070 return true; 0071 } 0072 0073 widget = widget->parentWidget(); 0074 } 0075 0076 return false; 0077 } 0078 0079 public: 0080 0081 TagModel tagModel; 0082 CheckableAlbumFilterModel filterModel; 0083 TagPropertiesFilterModel filteredModel; 0084 0085 AssignNameWidget* assignNameWidget; 0086 }; 0087 0088 AssignNameOverlay::AssignNameOverlay(QObject* const parent) 0089 : PersistentWidgetDelegateOverlay(parent), 0090 d (new Private) 0091 { 0092 d->filteredModel.setSourceAlbumModel(&d->tagModel); 0093 d->filterModel.setSourceFilterModel(&d->filteredModel); 0094 0095 // Restrict the tag properties filter model to people if configured. 0096 0097 ApplicationSettings* const settings = ApplicationSettings::instance(); 0098 0099 if (settings) 0100 { 0101 if (settings->showOnlyPersonTagsInPeopleSidebar()) 0102 { 0103 d->filteredModel.listOnlyTagsWithProperty(TagPropertyName::person()); 0104 } 0105 } 0106 } 0107 0108 AssignNameOverlay::~AssignNameOverlay() 0109 { 0110 delete d; 0111 } 0112 0113 AssignNameWidget* AssignNameOverlay::assignNameWidget() const 0114 { 0115 return d->assignNameWidget; 0116 } 0117 0118 QWidget* AssignNameOverlay::createWidget() 0119 { 0120 DVBox* const vbox = new DVBox(parentWidget()); 0121 d->assignNameWidget = new AssignNameWidget(vbox); 0122 d->assignNameWidget->setMode(AssignNameWidget::UnconfirmedEditMode); 0123 d->assignNameWidget->setVisualStyle(AssignNameWidget::TranslucentThemedFrameless); 0124 d->assignNameWidget->setTagEntryWidgetMode(AssignNameWidget::AddTagsLineEditMode); 0125 d->assignNameWidget->setLayoutMode(AssignNameWidget::Compact); 0126 d->assignNameWidget->setAlbumModels(&d->tagModel, &d->filteredModel, &d->filterModel); 0127 d->assignNameWidget->lineEdit()->installEventFilter(this); 0128 /* 0129 new StyleSheetDebugger(d->assignNameWidget); 0130 */ 0131 return vbox; 0132 } 0133 0134 void AssignNameOverlay::setActive(bool active) 0135 { 0136 PersistentWidgetDelegateOverlay::setActive(active); 0137 0138 if (active) 0139 { 0140 connect(assignNameWidget(), SIGNAL(assigned(TaggingAction,ItemInfo,QVariant)), 0141 this, SLOT(slotAssigned(TaggingAction,ItemInfo,QVariant))); 0142 0143 connect(assignNameWidget(), SIGNAL(rejected(ItemInfo,QVariant)), 0144 this, SLOT(slotRejected(ItemInfo,QVariant))); 0145 0146 connect(assignNameWidget(), SIGNAL(ignored(ItemInfo,QVariant)), 0147 this, SLOT(slotIgnored(ItemInfo,QVariant))); 0148 0149 connect(assignNameWidget(), SIGNAL(ignoredClicked(ItemInfo,QVariant)), 0150 this, SLOT(slotUnknown(ItemInfo,QVariant))); 0151 0152 connect(assignNameWidget(), SIGNAL(selected(TaggingAction,ItemInfo,QVariant)), 0153 this, SLOT(enterPersistentMode())); 0154 0155 connect(assignNameWidget(), SIGNAL(assigned(TaggingAction,ItemInfo,QVariant)), 0156 this, SLOT(leavePersistentMode())); 0157 0158 connect(assignNameWidget(), SIGNAL(rejected(ItemInfo,QVariant)), 0159 this, SLOT(leavePersistentMode())); 0160 0161 connect(assignNameWidget(), SIGNAL(ignored(ItemInfo,QVariant)), 0162 this, SLOT(leavePersistentMode())); 0163 0164 connect(assignNameWidget(), SIGNAL(ignoredClicked(ItemInfo,QVariant)), 0165 this, SLOT(leavePersistentMode())); 0166 0167 connect(assignNameWidget(), SIGNAL(assigned(TaggingAction,ItemInfo,QVariant)), 0168 this, SLOT(storeFocus())); 0169 0170 connect(assignNameWidget(), SIGNAL(rejected(ItemInfo,QVariant)), 0171 this, SLOT(storeFocus())); 0172 0173 connect(assignNameWidget(), SIGNAL(ignoredClicked(ItemInfo,QVariant)), 0174 this, SLOT(storeFocus())); 0175 0176 connect(assignNameWidget(), SIGNAL(ignored(ItemInfo,QVariant)), 0177 this, SLOT(storeFocus())); 0178 0179 /* 0180 if (view()->model()) 0181 { 0182 connect(view()->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0183 this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); 0184 } 0185 */ 0186 } 0187 else 0188 { 0189 // widget is deleted 0190 0191 /* 0192 if (view() && view()->model()) 0193 { 0194 disconnect(view()->model(), 0, this, 0); 0195 } 0196 */ 0197 } 0198 } 0199 0200 void AssignNameOverlay::visualChange() 0201 { 0202 if (m_widget && m_widget->isVisible()) 0203 { 0204 updatePosition(); 0205 } 0206 } 0207 0208 void AssignNameOverlay::hide() 0209 { 0210 PersistentWidgetDelegateOverlay::hide(); 0211 } 0212 0213 void AssignNameOverlay::updatePosition() 0214 { 0215 if (!index().isValid()) 0216 { 0217 return; 0218 } 0219 0220 // See bug #365667. 0221 // Use information view below pixmap. 0222 // Depending of icon-view item options enabled in setup, the free space to use can be different. 0223 // We can continue to show the widget behind bottom of thumbnail view. 0224 0225 QRect rect = delegate()->imageInformationRect(); 0226 rect.setTop(delegate()->pixmapRect().top()); 0227 0228 if (rect.width() < m_widget->minimumSizeHint().width()) 0229 { 0230 int offset = (m_widget->minimumSizeHint().width() - rect.width()) / 2; 0231 rect.adjust(-offset, 0, offset, 0); 0232 } 0233 0234 int yoffset = rect.height() - m_widget->minimumSizeHint().height(); 0235 rect.adjust(0, yoffset, 0, 0); 0236 0237 QRect visualRect = m_view->visualRect(index()); 0238 rect.translate(visualRect.topLeft()); 0239 0240 m_widget->setFixedSize(rect.width(), m_widget->minimumSizeHint().height()); 0241 m_widget->move(rect.topLeft()); 0242 } 0243 0244 void AssignNameOverlay::updateFace() 0245 { 0246 if (!index().isValid() || !assignNameWidget()) 0247 { 0248 return; 0249 } 0250 0251 QVariant extraData = index().data(ItemModel::ExtraDataRole); 0252 0253 /** 0254 * The order to plug these functions is important, since 0255 * setUserData() controls how the Overlay appears on 0256 * a particular face. 0257 */ 0258 assignNameWidget()->setUserData(ItemModel::retrieveItemInfo(index()), extraData); 0259 assignNameWidget()->setCurrentFace(FaceTagsIface::fromVariant(extraData)); 0260 } 0261 0262 /* 0263 void AssignNameOverlay::slotDataChanged(const QModelIndex& / *topLeft* /, const QModelIndex& / *bottomRight* /) 0264 { 0265 if (m_widget && m_widget->isVisible() && QItemSelectionRange(topLeft, bottomRight).contains(index())) 0266 updateTag(); 0267 } 0268 */ 0269 0270 bool AssignNameOverlay::checkIndex(const QModelIndex& index) const 0271 { 0272 if (!index.isValid()) 0273 { 0274 return false; 0275 } 0276 0277 QVariant extraData = index.data(ItemModel::ExtraDataRole); 0278 0279 if (extraData.isNull()) 0280 { 0281 return false; 0282 } 0283 0284 return (FaceTagsIface::fromVariant(extraData).isIgnoredName() || 0285 FaceTagsIface::fromVariant(extraData).isUnconfirmedType()); 0286 } 0287 0288 void AssignNameOverlay::showOnIndex(const QModelIndex& index) 0289 { 0290 PersistentWidgetDelegateOverlay::showOnIndex(index); 0291 0292 /* 0293 // TODO: add again when fading in 0294 // see bug 228810, this is a small workaround 0295 0296 if (m_widget && m_widget->isVisible() && index().isValid() && (index == index())) 0297 { 0298 addTagsLineEdit()->setVisibleImmediately; 0299 } 0300 */ 0301 0302 updatePosition(); 0303 updateFace(); 0304 } 0305 0306 void AssignNameOverlay::viewportLeaveEvent(QObject* o, QEvent* e) 0307 { 0308 if (isPersistent() && m_widget->isVisible()) 0309 { 0310 return; 0311 } 0312 0313 // Do not hide when hovering the pop-up of the line edit. 0314 0315 if (d->isChildWidget(qApp->widgetAt(QCursor::pos()), assignNameWidget())) 0316 { 0317 return; 0318 } 0319 0320 PersistentWidgetDelegateOverlay::viewportLeaveEvent(o, e); 0321 } 0322 0323 void AssignNameOverlay::slotAssigned(const TaggingAction& action, const ItemInfo& info, const QVariant& faceIdentifier) 0324 { 0325 Q_UNUSED(info); 0326 FaceTagsIface face = FaceTagsIface::fromVariant(faceIdentifier); 0327 0328 //qCDebug(DIGIKAM_GENERAL_LOG) << "Confirming" << face << action.shallAssignTag() << action.tagId(); 0329 0330 if (face.isConfirmedName() || !action.isValid()) 0331 { 0332 return; 0333 } 0334 0335 int tagId = 0; 0336 0337 if (action.shallAssignTag()) 0338 { 0339 tagId = action.tagId(); 0340 } 0341 else if (action.shallCreateNewTag()) 0342 { 0343 QStringList faceNames = action.newTagName().split(QLatin1Char('/'), 0344 QT_SKIP_EMPTY_PARTS); 0345 if (!faceNames.isEmpty()) 0346 { 0347 tagId = -1; 0348 0349 Q_FOREACH (const QString& name, faceNames) 0350 { 0351 tagId = FaceTags::getOrCreateTagForPerson(name.trimmed(), tagId); 0352 } 0353 } 0354 } 0355 0356 if (tagId) 0357 { 0358 Q_EMIT confirmFaces(affectedIndexes(index()), tagId); 0359 } 0360 0361 hide(); 0362 } 0363 0364 void AssignNameOverlay::slotRejected(const ItemInfo& info, const QVariant& faceIdentifier) 0365 { 0366 Q_UNUSED(info); 0367 Q_UNUSED(faceIdentifier); 0368 0369 Q_EMIT removeFaces(affectedIndexes(index())); 0370 hide(); 0371 } 0372 0373 void AssignNameOverlay::slotIgnored(const ItemInfo& info, const QVariant& faceIdentifier) 0374 { 0375 Q_UNUSED(info); 0376 Q_UNUSED(faceIdentifier); 0377 0378 Q_EMIT ignoreFaces(affectedIndexes(index())); 0379 hide(); 0380 } 0381 0382 void AssignNameOverlay::slotUnknown(const ItemInfo& info, const QVariant& faceIdentifier) 0383 { 0384 Q_UNUSED(info); 0385 Q_UNUSED(faceIdentifier); 0386 0387 Q_EMIT unknownFaces(affectedIndexes(index())); 0388 hide(); 0389 } 0390 0391 void AssignNameOverlay::widgetEnterEvent() 0392 { 0393 widgetEnterNotifyMultiple(index()); 0394 } 0395 0396 void AssignNameOverlay::widgetLeaveEvent() 0397 { 0398 widgetLeaveNotifyMultiple(); 0399 } 0400 0401 void AssignNameOverlay::setFocusOnWidget() 0402 { 0403 if (assignNameWidget()->lineEdit()) 0404 { 0405 assignNameWidget()->lineEdit()->selectAll(); 0406 assignNameWidget()->lineEdit()->setFocus(); 0407 } 0408 } 0409 0410 bool AssignNameOverlay::eventFilter(QObject* o, QEvent* e) 0411 { 0412 switch (e->type()) 0413 { 0414 case QEvent::MouseButtonPress: 0415 { 0416 enterPersistentMode(); 0417 break; 0418 } 0419 0420 case QEvent::FocusOut: 0421 { 0422 if (!d->isChildWidget(QApplication::focusWidget(), assignNameWidget())) 0423 { 0424 leavePersistentMode(); 0425 } 0426 break; 0427 } 0428 0429 default: 0430 break; 0431 } 0432 0433 return PersistentWidgetDelegateOverlay::eventFilter(o, e); 0434 } 0435 0436 } // namespace Digikam 0437 0438 #include "moc_assignnameoverlay.cpp"