File indexing completed on 2025-04-27 03:58:33
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-07-16 0007 * Description : metadata selector. 0008 * 0009 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "metadataselector.h" 0016 0017 // Qt includes 0018 0019 #include <QHeaderView> 0020 #include <QGridLayout> 0021 #include <QApplication> 0022 #include <QPushButton> 0023 #include <QStyle> 0024 0025 // KDE includes 0026 0027 #include <klocalizedstring.h> 0028 0029 // Local includes 0030 0031 #include "ditemtooltip.h" 0032 #include "mdkeylistviewitem.h" 0033 0034 namespace Digikam 0035 { 0036 0037 MetadataSelectorItem::MetadataSelectorItem(MdKeyListViewItem* const parent, 0038 const QString& key, 0039 const QString& title, 0040 const QString& desc) 0041 : QTreeWidgetItem(parent), 0042 m_key (key), 0043 m_parent (parent) 0044 { 0045 setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); 0046 setCheckState(0, Qt::Unchecked); 0047 setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator); 0048 0049 setText(0, title); 0050 setToolTip(0, key); 0051 0052 QString descVal = desc.simplified(); 0053 0054 if (descVal.length() > 512) 0055 { 0056 descVal.truncate(512); 0057 descVal.append(QLatin1String("...")); 0058 } 0059 0060 setText(1, descVal); 0061 0062 DToolTipStyleSheet cnt; 0063 setToolTip(1, QLatin1String 0064 ("<qt><p>") + cnt.breakString(descVal) + QLatin1String("</p></qt>")); 0065 } 0066 0067 MetadataSelectorItem::~MetadataSelectorItem() 0068 { 0069 } 0070 0071 QString MetadataSelectorItem::key() const 0072 { 0073 return m_key; 0074 } 0075 0076 QString MetadataSelectorItem::mdKeyTitle() const 0077 { 0078 return (m_parent ? m_parent->text(0) : QString()); 0079 } 0080 0081 // ------------------------------------------------------------------------------------ 0082 0083 MetadataSelector::MetadataSelector(MetadataSelectorView* const parent) 0084 : QTreeWidget(parent), 0085 m_parent (parent) 0086 { 0087 setSelectionMode(QAbstractItemView::SingleSelection); 0088 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0089 setAllColumnsShowFocus(true); 0090 setUniformRowHeights(true); 0091 setColumnCount(2); 0092 0093 QStringList labels; 0094 labels.append(i18nc("@title: metadata properties", "Name")); 0095 labels.append(i18nc("@title: metadata properties", "Description")); 0096 setHeaderLabels(labels); 0097 header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); 0098 header()->setSectionResizeMode(1, QHeaderView::Stretch); 0099 0100 setSortingEnabled(true); 0101 sortByColumn(0, Qt::AscendingOrder); 0102 } 0103 0104 MetadataSelector::~MetadataSelector() 0105 { 0106 } 0107 0108 void MetadataSelector::setTagsMap(const DMetadata::TagsMap& map) 0109 { 0110 clear(); 0111 0112 uint subItems = 0; 0113 QString ifDItemName, currentIfDName; 0114 MdKeyListViewItem* parentifDItem = nullptr; 0115 QList<QTreeWidgetItem*> toplevelItems; 0116 0117 for (DMetadata::TagsMap::const_iterator it = map.constBegin(); 0118 it != map.constEnd(); ++it) 0119 { 0120 // Check if we have changed group. 0121 0122 if (m_parent->backend() == MetadataSelectorView::Exiv2Backend) 0123 { 0124 currentIfDName = it.key().section(QLatin1Char('.'), 1, 1); 0125 } 0126 else 0127 { 0128 currentIfDName = it.key().section(QLatin1Char('.'), 0, 0); 0129 } 0130 0131 if (currentIfDName != ifDItemName) 0132 { 0133 ifDItemName = currentIfDName; 0134 0135 // Remove the group header if it has no items. 0136 0137 if ((subItems == 0) && parentifDItem) 0138 { 0139 delete parentifDItem; 0140 } 0141 0142 parentifDItem = new MdKeyListViewItem(nullptr, currentIfDName); 0143 toplevelItems << parentifDItem; 0144 subItems = 0; 0145 } 0146 0147 // Ignore all unknown Exif tags. 0148 0149 if (!it.key().section(QLatin1Char('.'), 2, 2).startsWith(QLatin1String("0x"))) 0150 { 0151 new MetadataSelectorItem(parentifDItem, it.key(), 0152 it.value().at(0), // Name 0153 it.value().at(2)); // Description 0154 ++subItems; 0155 } 0156 } 0157 0158 addTopLevelItems(toplevelItems); 0159 0160 // We need to call setFirstColumnSpanned() in here again because the 0161 // widgets were added parentless and therefore no layout information was 0162 // present at construction time. Now that all items have a parent, we need 0163 // to trigger the method again. 0164 0165 for (QList<QTreeWidgetItem*>::const_iterator it = toplevelItems.constBegin() ; 0166 it != toplevelItems.constEnd() ; ++it) 0167 { 0168 if (*it) 0169 { 0170 (*it)->setFirstColumnSpanned(true); 0171 } 0172 } 0173 0174 expandAll(); 0175 } 0176 0177 void MetadataSelector::setcheckedTagsList(const QStringList& list) 0178 { 0179 QTreeWidgetItemIterator it(this); 0180 0181 while (*it) 0182 { 0183 MetadataSelectorItem* const item = dynamic_cast<MetadataSelectorItem*>(*it); 0184 0185 if (item && list.contains(item->key())) 0186 { 0187 item->setCheckState(0, Qt::Checked); 0188 } 0189 0190 ++it; 0191 } 0192 } 0193 0194 QStringList MetadataSelector::checkedTagsList() 0195 { 0196 QStringList list; 0197 QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Checked); 0198 0199 while (*it) 0200 { 0201 MetadataSelectorItem* const item = dynamic_cast<MetadataSelectorItem*>(*it); 0202 0203 if (item) 0204 { 0205 list.append(item->key()); 0206 } 0207 0208 ++it; 0209 } 0210 0211 return list; 0212 } 0213 0214 void MetadataSelector::clearSelection() 0215 { 0216 collapseAll(); 0217 0218 QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Checked); 0219 0220 while (*it) 0221 { 0222 MetadataSelectorItem* const item = dynamic_cast<MetadataSelectorItem*>(*it); 0223 0224 if (item) 0225 { 0226 item->setCheckState(0, Qt::Unchecked); 0227 } 0228 0229 ++it; 0230 } 0231 0232 expandAll(); 0233 } 0234 0235 void MetadataSelector::selectAll() 0236 { 0237 collapseAll(); 0238 0239 QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::NotChecked); 0240 0241 while (*it) 0242 { 0243 MetadataSelectorItem* const item = dynamic_cast<MetadataSelectorItem*>(*it); 0244 0245 if (item) 0246 { 0247 item->setCheckState(0, Qt::Checked); 0248 } 0249 0250 ++it; 0251 } 0252 0253 expandAll(); 0254 } 0255 0256 // ------------------------------------------------------------------------------------ 0257 0258 class Q_DECL_HIDDEN MetadataSelectorView::Private 0259 { 0260 public: 0261 0262 explicit Private() 0263 : selectAllBtn (nullptr), 0264 clearSelectionBtn (nullptr), 0265 defaultSelectionBtn(nullptr), 0266 selector (nullptr), 0267 searchBar (nullptr), 0268 backend (Exiv2Backend) 0269 { 0270 } 0271 0272 QStringList defaultFilter; 0273 0274 QPushButton* selectAllBtn; 0275 QPushButton* clearSelectionBtn; 0276 QPushButton* defaultSelectionBtn; 0277 0278 MetadataSelector* selector; 0279 0280 SearchTextBar* searchBar; 0281 Backend backend; 0282 }; 0283 0284 MetadataSelectorView::MetadataSelectorView(QWidget* const parent, Backend be) 0285 : QWidget(parent), 0286 d (new Private) 0287 { 0288 d->backend = be; 0289 const int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0290 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0291 0292 QGridLayout* const grid = new QGridLayout(this); 0293 d->selector = new MetadataSelector(this); 0294 d->searchBar = new SearchTextBar(this, QLatin1String("MetadataSelectorView")); 0295 d->selectAllBtn = new QPushButton(i18nc("@action: metadata selector", "Select All"),this); 0296 d->clearSelectionBtn = new QPushButton(i18nc("@action: metadata selector", "Clear"),this); 0297 d->defaultSelectionBtn = new QPushButton(i18nc("@action: metadata selector", "Default"),this); 0298 0299 grid->addWidget(d->selector, 0, 0, 1, 5); 0300 grid->addWidget(d->searchBar, 1, 0, 1, 1); 0301 grid->addWidget(d->selectAllBtn, 1, 2, 1, 1); 0302 grid->addWidget(d->clearSelectionBtn, 1, 3, 1, 1); 0303 grid->addWidget(d->defaultSelectionBtn, 1, 4, 1, 1); 0304 grid->setColumnStretch(0, 10); 0305 grid->setRowStretch(0, 10); 0306 grid->setContentsMargins(spacing, spacing, spacing, spacing); 0307 grid->setSpacing(spacing); 0308 0309 setControlElements(SearchBar | SelectAllBtn | DefaultBtn | ClearBtn); 0310 0311 connect(d->searchBar, SIGNAL(signalSearchTextSettings(SearchTextSettings)), 0312 this, SLOT(slotSearchTextChanged(SearchTextSettings))); 0313 0314 connect(d->selectAllBtn, SIGNAL(clicked()), 0315 this, SLOT(slotSelectAll())); 0316 0317 connect(d->defaultSelectionBtn, SIGNAL(clicked()), 0318 this, SLOT(slotDeflautSelection())); 0319 0320 connect(d->clearSelectionBtn, SIGNAL(clicked()), 0321 this, SLOT(slotClearSelection())); 0322 } 0323 0324 MetadataSelectorView::~MetadataSelectorView() 0325 { 0326 delete d; 0327 } 0328 0329 void MetadataSelectorView::setTagsMap(const DMetadata::TagsMap& map) 0330 { 0331 d->selector->setTagsMap(map); 0332 } 0333 0334 void MetadataSelectorView::setcheckedTagsList(const QStringList& list) 0335 { 0336 d->selector->setcheckedTagsList(list); 0337 } 0338 0339 void MetadataSelectorView::setDefaultFilter(const QStringList& list) 0340 { 0341 d->defaultFilter = list; 0342 } 0343 0344 QStringList MetadataSelectorView::defaultFilter() const 0345 { 0346 return d->defaultFilter; 0347 } 0348 0349 int MetadataSelectorView::itemsCount() const 0350 { 0351 return d->selector->model()->rowCount(); 0352 } 0353 0354 MetadataSelectorView::Backend MetadataSelectorView::backend() const 0355 { 0356 return d->backend; 0357 } 0358 0359 QStringList MetadataSelectorView::checkedTagsList() const 0360 { 0361 d->searchBar->clear(); 0362 return d->selector->checkedTagsList(); 0363 } 0364 0365 void MetadataSelectorView::slotSearchTextChanged(const SearchTextSettings& settings) 0366 { 0367 QString search = settings.text; 0368 bool atleastOneMatch = false; 0369 0370 // Restore all MdKey items. 0371 0372 QTreeWidgetItemIterator it2(d->selector); 0373 0374 while (*it2) 0375 { 0376 MdKeyListViewItem* const item = dynamic_cast<MdKeyListViewItem*>(*it2); 0377 0378 if (item) 0379 { 0380 item->setHidden(false); 0381 } 0382 0383 ++it2; 0384 } 0385 0386 QTreeWidgetItemIterator it(d->selector); 0387 0388 while (*it) 0389 { 0390 MetadataSelectorItem* const item = dynamic_cast<MetadataSelectorItem*>(*it); 0391 0392 if (item) 0393 { 0394 bool match = item->text(0).contains(search, settings.caseSensitive) || 0395 item->mdKeyTitle().contains(search, settings.caseSensitive); 0396 0397 if (match) 0398 { 0399 atleastOneMatch = true; 0400 item->setHidden(false); 0401 } 0402 else 0403 { 0404 item->setHidden(true); 0405 } 0406 } 0407 0408 ++it; 0409 } 0410 0411 // If we found MdKey items alone, we hide it... 0412 0413 cleanUpMdKeyItem(); 0414 0415 d->searchBar->slotSearchResult(atleastOneMatch); 0416 } 0417 0418 void MetadataSelectorView::cleanUpMdKeyItem() 0419 { 0420 QTreeWidgetItemIterator it(d->selector); 0421 0422 while (*it) 0423 { 0424 MdKeyListViewItem* const item = dynamic_cast<MdKeyListViewItem*>(*it); 0425 0426 if (item) 0427 { 0428 int children = item->childCount(); 0429 int visibles = 0; 0430 0431 for (int i = 0 ; i < children ; ++i) 0432 { 0433 QTreeWidgetItem* const citem = (*it)->child(i); 0434 0435 if (!citem->isHidden()) 0436 { 0437 ++visibles; 0438 } 0439 } 0440 0441 if (!children || !visibles) 0442 { 0443 item->setHidden(true); 0444 } 0445 } 0446 0447 ++it; 0448 } 0449 } 0450 0451 void MetadataSelectorView::slotDeflautSelection() 0452 { 0453 slotClearSelection(); 0454 0455 QApplication::setOverrideCursor(Qt::WaitCursor); 0456 d->selector->collapseAll(); 0457 0458 QTreeWidgetItemIterator it(d->selector); 0459 0460 while (*it) 0461 { 0462 MetadataSelectorItem* const item = dynamic_cast<MetadataSelectorItem*>(*it); 0463 0464 if (item) 0465 { 0466 if (d->defaultFilter.contains(item->text(0))) 0467 { 0468 item->setCheckState(0, Qt::Checked); 0469 } 0470 } 0471 0472 ++it; 0473 } 0474 0475 d->selector->expandAll(); 0476 QApplication::restoreOverrideCursor(); 0477 } 0478 0479 void MetadataSelectorView::slotSelectAll() 0480 { 0481 QApplication::setOverrideCursor(Qt::WaitCursor); 0482 d->selector->selectAll(); 0483 QApplication::restoreOverrideCursor(); 0484 } 0485 0486 void MetadataSelectorView::slotClearSelection() 0487 { 0488 QApplication::setOverrideCursor(Qt::WaitCursor); 0489 d->selector->clearSelection(); 0490 QApplication::restoreOverrideCursor(); 0491 } 0492 0493 void MetadataSelectorView::setControlElements(ControlElements controllerMask) 0494 { 0495 d->searchBar->setVisible(controllerMask & SearchBar); 0496 d->selectAllBtn->setVisible(controllerMask & SelectAllBtn); 0497 d->clearSelectionBtn->setVisible(controllerMask & ClearBtn); 0498 d->defaultSelectionBtn->setVisible(controllerMask & DefaultBtn); 0499 } 0500 0501 void MetadataSelectorView::clearSelection() 0502 { 0503 slotClearSelection(); 0504 } 0505 0506 void MetadataSelectorView::selectAll() 0507 { 0508 slotSelectAll(); 0509 } 0510 0511 void MetadataSelectorView::selectDefault() 0512 { 0513 slotDeflautSelection(); 0514 } 0515 0516 } // namespace Digikam 0517 0518 #include "moc_metadataselector.cpp"