File indexing completed on 2025-04-27 03:58:35
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-02-21 0007 * Description : a generic list view widget to 0008 * display metadata 0009 * 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "metadatalistview.h" 0017 0018 // Qt includes 0019 0020 #include <QHeaderView> 0021 #include <QTimer> 0022 #include <QPalette> 0023 #include <QApplication> 0024 #include <QStyle> 0025 0026 // KDE includes 0027 0028 #include <klocalizedstring.h> 0029 0030 // Local includes 0031 0032 #include "mdkeylistviewitem.h" 0033 #include "metadatalistviewitem.h" 0034 0035 namespace Digikam 0036 { 0037 0038 MetadataListView::MetadataListView(QWidget* const parent) 0039 : QTreeWidget(parent) 0040 { 0041 setColumnCount(2); 0042 setRootIsDecorated(false); 0043 setUniformRowHeights(true); 0044 setAllColumnsShowFocus(true); 0045 setSelectionMode(QAbstractItemView::SingleSelection); 0046 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0047 setIndentation(qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0048 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing))); 0049 header()->setSectionResizeMode(QHeaderView::Stretch); 0050 header()->hide(); 0051 0052 QStringList labels; 0053 labels.append(QLatin1String("Name")); // no i18n here: hidden header 0054 labels.append(QLatin1String("Value")); // no i18n here: hidden header 0055 setHeaderLabels(labels); 0056 0057 setSortingEnabled(true); 0058 sortByColumn(0, Qt::AscendingOrder); 0059 0060 m_parent = dynamic_cast<MetadataWidget*>(parent); 0061 0062 connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), 0063 this, SLOT(slotSelectionChanged(QTreeWidgetItem*,int))); 0064 } 0065 0066 MetadataListView::~MetadataListView() 0067 { 0068 } 0069 0070 QString MetadataListView::getCurrentItemKey() const 0071 { 0072 if (currentItem() && (currentItem()->flags() & Qt::ItemIsSelectable)) 0073 { 0074 MetadataListViewItem* const item = static_cast<MetadataListViewItem*>(currentItem()); 0075 return item->getKey(); 0076 } 0077 0078 return QString(); 0079 } 0080 0081 void MetadataListView::setCurrentItemByKey(const QString& itemKey) 0082 { 0083 if (itemKey.isNull()) 0084 { 0085 return; 0086 } 0087 0088 int i = 0; 0089 QTreeWidgetItem* item = nullptr; 0090 0091 do 0092 { 0093 item = topLevelItem(i); 0094 0095 if (item && (item->flags() & Qt::ItemIsSelectable)) 0096 { 0097 MetadataListViewItem* const lvItem = dynamic_cast<MetadataListViewItem*>(item); 0098 0099 if (lvItem) 0100 { 0101 if (lvItem->getKey() == itemKey) 0102 { 0103 setCurrentItem(item); 0104 scrollToItem(item); 0105 m_selectedItemKey = itemKey; 0106 0107 return; 0108 } 0109 } 0110 } 0111 0112 ++i; 0113 } 0114 while (item); 0115 } 0116 0117 void MetadataListView::slotSelectionChanged(QTreeWidgetItem* item, int) 0118 { 0119 0120 MetadataListViewItem* const viewItem = dynamic_cast<MetadataListViewItem*>(item); 0121 0122 if (!viewItem) 0123 { 0124 return; 0125 } 0126 0127 m_selectedItemKey = viewItem->getKey(); 0128 QString tagValue = viewItem->getValue().simplified(); 0129 QString tagTitle = m_parent->getTagTitle(m_selectedItemKey); 0130 QString tagDesc = m_parent->getTagDescription(m_selectedItemKey); 0131 0132 if (tagValue.length() > 128) 0133 { 0134 tagValue.truncate(128); 0135 tagValue.append(QLatin1String("...")); 0136 } 0137 0138 this->setWhatsThis(i18n("<b>Title: </b><p>%1</p>" 0139 "<b>Value: </b><p>%2</p>" 0140 "<b>Description: </b><p>%3</p>", 0141 tagTitle, tagValue, tagDesc)); 0142 } 0143 0144 void MetadataListView::setIfdList(const DMetadata::MetaDataMap& ifds, const QStringList& tagsFilter) 0145 { 0146 clear(); 0147 0148 uint subItems = 0; 0149 MdKeyListViewItem* parentifDItem = nullptr; 0150 QStringList filters = tagsFilter; 0151 QString ifDItemName; 0152 0153 for (DMetadata::MetaDataMap::const_iterator it = ifds.constBegin() ; it != ifds.constEnd() ; ++it) 0154 { 0155 // We checking if we have changed of ifDName 0156 0157 QString currentIfDName = it.key().section(QLatin1Char('.'), 1, 1); 0158 0159 if (currentIfDName != ifDItemName) 0160 { 0161 ifDItemName = currentIfDName; 0162 0163 // Check if the current IfD have any items. If no remove it before to toggle to the next IfD. 0164 0165 if ((subItems == 0) && parentifDItem) 0166 { 0167 delete parentifDItem; 0168 } 0169 0170 parentifDItem = new MdKeyListViewItem(this, currentIfDName); 0171 subItems = 0; 0172 } 0173 0174 if (tagsFilter.isEmpty()) 0175 { 0176 QString tagTitle = m_parent->getTagTitle(it.key()); 0177 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0178 ++subItems; 0179 } 0180 else if (!it.key().section(QLatin1Char('.'), 2, 2).startsWith(QLatin1String("0x"))) 0181 { 0182 // We ignore all unknown tags if necessary. 0183 0184 if (filters.contains(QLatin1String("FULL"))) 0185 { 0186 // We don't filter the output (Photo Mode) 0187 0188 QString tagTitle = m_parent->getTagTitle(it.key()); 0189 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0190 ++subItems; 0191 } 0192 else if (!filters.isEmpty()) 0193 { 0194 // We using the filter to make a more user friendly output (Custom Mode) 0195 0196 // Filter is not a list of complete tag keys 0197 0198 if (!filters.at(0).contains(QLatin1Char('.')) && filters.contains(it.key().section(QLatin1Char('.'), 2, 2))) 0199 { 0200 QString tagTitle = m_parent->getTagTitle(it.key()); 0201 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0202 ++subItems; 0203 filters.removeAll(it.key()); 0204 } 0205 else if (filters.contains(it.key())) 0206 { 0207 QString tagTitle = m_parent->getTagTitle(it.key()); 0208 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0209 ++subItems; 0210 filters.removeAll(it.key()); 0211 } 0212 } 0213 } 0214 } 0215 0216 // To check if the last IfD have any items... 0217 0218 if ((subItems == 0) && parentifDItem) 0219 { 0220 delete parentifDItem; 0221 } 0222 0223 // Add not found tags from filter as grey items. 0224 0225 if (!filters.isEmpty() && 0226 (filters.at(0) != QLatin1String("FULL")) && 0227 filters.at(0).contains(QLatin1Char('.'))) 0228 { 0229 Q_FOREACH (const QString& key, filters) 0230 { 0231 MdKeyListViewItem* pitem = findMdKeyItem(key); 0232 0233 if (!pitem) 0234 { 0235 pitem = new MdKeyListViewItem(this, key.section(QLatin1Char('.'), 1, 1)); 0236 } 0237 0238 QString tagTitle = m_parent->getTagTitle(key); 0239 new MetadataListViewItem(pitem, key, tagTitle); 0240 } 0241 } 0242 0243 setCurrentItemByKey(m_selectedItemKey); 0244 update(); 0245 } 0246 0247 void MetadataListView::setIfdList(const DMetadata::MetaDataMap& ifds, const QStringList& keysFilter, 0248 const QStringList& tagsFilter) 0249 { 0250 clear(); 0251 0252 QStringList filters = tagsFilter; 0253 uint subItems = 0; 0254 MdKeyListViewItem* parentifDItem = nullptr; 0255 0256 if (ifds.count() == 0) 0257 { 0258 return; 0259 } 0260 0261 for (QStringList::const_iterator itKeysFilter = keysFilter.constBegin() ; 0262 itKeysFilter != keysFilter.constEnd() ; ++itKeysFilter) 0263 { 0264 subItems = 0; 0265 parentifDItem = new MdKeyListViewItem(this, *itKeysFilter); 0266 0267 DMetadata::MetaDataMap::const_iterator it = ifds.constEnd(); 0268 0269 while (it != ifds.constBegin()) 0270 { 0271 --it; 0272 0273 if (*itKeysFilter == it.key().section(QLatin1Char('.'), 1, 1)) 0274 { 0275 if (tagsFilter.isEmpty()) 0276 { 0277 QString tagTitle = m_parent->getTagTitle(it.key()); 0278 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0279 ++subItems; 0280 } 0281 else if (!it.key().section(QLatin1Char('.'), 2, 2).startsWith(QLatin1String("0x"))) 0282 { 0283 // We ignore all unknown tags if necessary. 0284 0285 if (filters.contains(QLatin1String("FULL"))) 0286 { 0287 // We don't filter the output (Photo Mode) 0288 0289 QString tagTitle = m_parent->getTagTitle(it.key()); 0290 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0291 ++subItems; 0292 } 0293 else if (!filters.isEmpty()) 0294 { 0295 // We using the filter to make a more user friendly output (Custom Mode) 0296 0297 // Filter is not a list of complete tag keys 0298 0299 if (!filters.at(0).contains(QLatin1Char('.')) && 0300 filters.contains(it.key().section(QLatin1Char('.'), 2, 2))) 0301 { 0302 QString tagTitle = m_parent->getTagTitle(it.key()); 0303 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0304 ++subItems; 0305 filters.removeAll(it.key()); 0306 } 0307 else if (filters.contains(it.key())) 0308 { 0309 QString tagTitle = m_parent->getTagTitle(it.key()); 0310 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0311 ++subItems; 0312 filters.removeAll(it.key()); 0313 } 0314 else if (it.key().contains(QLatin1String("]/"))) 0315 { 0316 // Special case to filter metadata tags in bag containers 0317 0318 int propIndex = it.key().lastIndexOf(QLatin1Char(':')); 0319 int nameIndex = it.key().lastIndexOf(QLatin1Char('.')); 0320 0321 if ((propIndex != -1) && (nameIndex != -1)) 0322 { 0323 QString property = it.key().mid(propIndex + 1); 0324 QString nameSpace = it.key().left(nameIndex + 1); 0325 0326 if (filters.contains(nameSpace + property)) 0327 { 0328 QString tagTitle = m_parent->getTagTitle(it.key()); 0329 new MetadataListViewItem(parentifDItem, it.key(), tagTitle, it.value()); 0330 ++subItems; 0331 0332 if (it.key().contains(QLatin1String("[1]"))) 0333 { 0334 filters.removeAll(nameSpace + property); 0335 } 0336 } 0337 } 0338 } 0339 } 0340 } 0341 } 0342 } 0343 0344 // We checking if the last IfD have any items. If no, we remove it. 0345 0346 if ((subItems == 0) && parentifDItem) 0347 { 0348 delete parentifDItem; 0349 } 0350 } 0351 0352 // Add not found tags from filter as grey items. 0353 0354 if (!filters.isEmpty() && 0355 (filters.at(0) != QLatin1String("FULL")) && 0356 filters.at(0).contains(QLatin1Char('.'))) 0357 { 0358 Q_FOREACH (const QString& key, filters) 0359 { 0360 MdKeyListViewItem* pitem = findMdKeyItem(key); 0361 0362 if (!pitem) 0363 { 0364 pitem = new MdKeyListViewItem(this, key.section(QLatin1Char('.'), 1, 1)); 0365 } 0366 0367 QString tagTitle = m_parent->getTagTitle(key); 0368 new MetadataListViewItem(pitem, key, tagTitle); 0369 } 0370 } 0371 0372 setCurrentItemByKey(m_selectedItemKey); 0373 update(); 0374 } 0375 0376 void MetadataListView::slotSearchTextChanged(const SearchTextSettings& settings) 0377 { 0378 bool query = false; 0379 QString search = settings.text; 0380 0381 // Restore all MdKey items. 0382 0383 QTreeWidgetItemIterator it2(this); 0384 0385 while (*it2) 0386 { 0387 MdKeyListViewItem* const item = dynamic_cast<MdKeyListViewItem*>(*it2); 0388 0389 if (item) 0390 { 0391 item->setHidden(false); 0392 } 0393 0394 ++it2; 0395 } 0396 0397 QTreeWidgetItemIterator it(this); 0398 0399 while (*it) 0400 { 0401 MetadataListViewItem* const item = dynamic_cast<MetadataListViewItem*>(*it); 0402 0403 if (item) 0404 { 0405 if (item->text(0).contains(search, settings.caseSensitive) || 0406 item->text(1).contains(search, settings.caseSensitive)) 0407 { 0408 query = true; 0409 item->setHidden(false); 0410 } 0411 else 0412 { 0413 item->setHidden(true); 0414 } 0415 } 0416 0417 ++it; 0418 } 0419 0420 // If we found MdKey items alone, we hide it... 0421 0422 cleanUpMdKeyItem(); 0423 0424 Q_EMIT signalTextFilterMatch(query); 0425 } 0426 0427 void MetadataListView::cleanUpMdKeyItem() 0428 { 0429 QTreeWidgetItemIterator it(this); 0430 0431 while (*it) 0432 { 0433 MdKeyListViewItem* const item = dynamic_cast<MdKeyListViewItem*>(*it); 0434 0435 if (item) 0436 { 0437 int children = item->childCount(); 0438 int visibles = 0; 0439 0440 for (int i = 0 ; i < children ; ++i) 0441 { 0442 QTreeWidgetItem* const citem = (*it)->child(i); 0443 0444 if (!citem->isHidden()) 0445 { 0446 ++visibles; 0447 } 0448 } 0449 0450 if (!children || !visibles) 0451 { 0452 item->setHidden(true); 0453 } 0454 } 0455 0456 ++it; 0457 } 0458 } 0459 0460 MdKeyListViewItem* MetadataListView::findMdKeyItem(const QString& key) 0461 { 0462 QTreeWidgetItemIterator it(this); 0463 0464 while (*it) 0465 { 0466 MdKeyListViewItem* const item = dynamic_cast<MdKeyListViewItem*>(*it); 0467 0468 if (item) 0469 { 0470 if (key.section(QLatin1Char('.'), 1, 1) == item->getKey()) 0471 { 0472 return item; 0473 } 0474 } 0475 0476 ++it; 0477 } 0478 0479 return nullptr; 0480 } 0481 0482 } // namespace Digikam 0483 0484 #include "moc_metadatalistview.cpp"