File indexing completed on 2024-05-05 04:19:17
0001 /* 0002 Gwenview: an image viewer 0003 Copyright 2007 Aurélien Gâteau <agateau@kde.org> 0004 0005 This program is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU General Public License 0007 as published by the Free Software Foundation; either version 2 0008 of the License, or (at your option) any later version. 0009 0010 This program is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 GNU General Public License for more details. 0014 0015 You should have received a copy of the GNU General Public License 0016 along with this program; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 0019 */ 0020 #include "infocontextmanageritem.h" 0021 0022 // Qt 0023 #include <QLabel> 0024 #include <QPointer> 0025 #include <QPushButton> 0026 #include <QScrollArea> 0027 #include <QVBoxLayout> 0028 0029 // KF 0030 #include <KFileItem> 0031 #include <KLocalizedString> 0032 0033 // Local 0034 #include "imagemetainfodialog.h" 0035 #include "sidebar.h" 0036 #include <lib/archiveutils.h> 0037 #include <lib/contextmanager.h> 0038 #include <lib/document/document.h> 0039 #include <lib/document/documentfactory.h> 0040 #include <lib/eventwatcher.h> 0041 #include <lib/gvdebug.h> 0042 #include <lib/gwenviewconfig.h> 0043 #include <lib/preferredimagemetainfomodel.h> 0044 0045 namespace Gwenview 0046 { 0047 #undef ENABLE_LOG 0048 #undef LOG 0049 // #define ENABLE_LOG 0050 #ifdef ENABLE_LOG 0051 #define LOG(x) qCDebug(GWENVIEW_APP_LOG) << x 0052 #else 0053 #define LOG(x) ; 0054 #endif 0055 0056 /** 0057 * This widget is capable of showing multiple lines of key/value pairs. 0058 */ 0059 class KeyValueWidget : public QWidget 0060 { 0061 struct Row { 0062 Row(QWidget *parent) 0063 : keyLabel(new QLabel(parent)) 0064 , valueLabel(new QLabel(parent)) 0065 { 0066 initLabel(keyLabel); 0067 initLabel(valueLabel); 0068 0069 QPalette pal = keyLabel->palette(); 0070 QColor color = pal.color(QPalette::WindowText); 0071 color.setAlphaF(0.65); 0072 pal.setColor(QPalette::WindowText, color); 0073 keyLabel->setPalette(pal); 0074 0075 valueLabel->setIndent(parent->fontInfo().pixelSize()); 0076 } 0077 0078 ~Row() 0079 { 0080 delete keyLabel; 0081 delete valueLabel; 0082 } 0083 0084 int setLabelGeometries(int rowY, int labelWidth) 0085 { 0086 int labelHeight = keyLabel->heightForWidth(labelWidth); 0087 keyLabel->setGeometry(0, rowY, labelWidth, labelHeight); 0088 rowY += labelHeight; 0089 labelHeight = valueLabel->heightForWidth(labelWidth); 0090 valueLabel->setGeometry(0, rowY, labelWidth, labelHeight); 0091 rowY += labelHeight; 0092 return rowY; 0093 } 0094 0095 int heightForWidth(int width) const 0096 { 0097 return keyLabel->heightForWidth(width) + valueLabel->heightForWidth(width); 0098 } 0099 0100 static void initLabel(QLabel *label) 0101 { 0102 label->setWordWrap(true); 0103 label->show(); 0104 label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); 0105 } 0106 0107 QLabel *const keyLabel; 0108 QLabel *const valueLabel; 0109 }; 0110 0111 public: 0112 explicit KeyValueWidget(QWidget *parent = nullptr) 0113 : QWidget(parent) 0114 { 0115 QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Fixed); 0116 policy.setHeightForWidth(true); 0117 setSizePolicy(policy); 0118 } 0119 ~KeyValueWidget() override 0120 { 0121 qDeleteAll(mRows); 0122 mRows.clear(); 0123 } 0124 0125 QSize sizeHint() const override 0126 { 0127 int width = 150; 0128 int height = heightForWidth(width); 0129 return QSize(width, height); 0130 } 0131 0132 int heightForWidth(int w) const override 0133 { 0134 int height = 0; 0135 for (Row *row : qAsConst(mRows)) { 0136 height += row->heightForWidth(w); 0137 } 0138 return height; 0139 } 0140 0141 void clear() 0142 { 0143 qDeleteAll(mRows); 0144 mRows.clear(); 0145 updateGeometry(); 0146 } 0147 0148 void addRow(const QString &key, const QString &value) 0149 { 0150 Row *row = new Row(this); 0151 row->keyLabel->setText(i18nc("@item:intext %1 is a key, we append a colon to it. A value is displayed after", "%1:", key)); 0152 row->valueLabel->setText(value); 0153 mRows << row; 0154 } 0155 0156 static bool rowsLessThan(const Row *row1, const Row *row2) 0157 { 0158 return row1->keyLabel->text() < row2->keyLabel->text(); 0159 } 0160 0161 void finishAddRows() 0162 { 0163 std::sort(mRows.begin(), mRows.end(), KeyValueWidget::rowsLessThan); 0164 updateGeometry(); 0165 } 0166 0167 void layoutRows() 0168 { 0169 // Layout labels manually: I tried to use a QVBoxLayout but for some 0170 // reason when using software animations the widget blinks when going 0171 // from one image to another 0172 int rowY = 0; 0173 const int labelWidth = width(); 0174 for (Row *row : qAsConst(mRows)) { 0175 rowY = row->setLabelGeometries(rowY, labelWidth); 0176 } 0177 } 0178 0179 protected: 0180 void showEvent(QShowEvent *event) override 0181 { 0182 QWidget::showEvent(event); 0183 layoutRows(); 0184 } 0185 0186 void resizeEvent(QResizeEvent *event) override 0187 { 0188 QWidget::resizeEvent(event); 0189 layoutRows(); 0190 } 0191 0192 private: 0193 QVector<Row *> mRows; 0194 }; 0195 0196 struct InfoContextManagerItemPrivate { 0197 InfoContextManagerItem *q; 0198 SideBarGroup *mGroup; 0199 0200 // One selection fields 0201 QScrollArea *mOneFileWidget; 0202 KeyValueWidget *mKeyValueWidget; 0203 Document::Ptr mDocument; 0204 0205 // Multiple selection fields 0206 QLabel *mMultipleFilesLabel; 0207 0208 QPointer<ImageMetaInfoDialog> mImageMetaInfoDialog; 0209 0210 void updateMetaInfoDialog() 0211 { 0212 if (!mImageMetaInfoDialog) { 0213 return; 0214 } 0215 ImageMetaInfoModel *model = mDocument ? mDocument->metaInfo() : nullptr; 0216 mImageMetaInfoDialog->setMetaInfo(model, GwenviewConfig::preferredMetaInfoKeyList()); 0217 } 0218 0219 void setupGroup() 0220 { 0221 mOneFileWidget = new QScrollArea(); 0222 mOneFileWidget->setFrameStyle(QFrame::NoFrame); 0223 mOneFileWidget->setWidgetResizable(true); 0224 0225 mKeyValueWidget = new KeyValueWidget; 0226 0227 auto moreLabel = new QLabel(mOneFileWidget); 0228 moreLabel->setText(QStringLiteral("<a href='#'>%1</a>").arg(i18nc("@action show more image meta info", "Show more details..."))); 0229 // for some reason, this label appears much further down the page without the following line 0230 moreLabel->setAlignment(Qt::AlignLeft); 0231 0232 auto content = new QWidget; 0233 auto layout = new QVBoxLayout(content); 0234 layout->setContentsMargins(0, 0, 0, 0); 0235 layout->addWidget(mKeyValueWidget); 0236 layout->addWidget(moreLabel); 0237 0238 mOneFileWidget->setWidget(content); 0239 0240 mMultipleFilesLabel = new QLabel(); 0241 0242 mGroup = new SideBarGroup(i18nc("@title:group", "Image Information")); 0243 q->setWidget(mGroup); 0244 mGroup->addWidget(mOneFileWidget); 0245 mGroup->addWidget(mMultipleFilesLabel); 0246 0247 EventWatcher::install(mGroup, QEvent::Show, q, SLOT(updateSideBarContent())); 0248 0249 QObject::connect(moreLabel, &QLabel::linkActivated, q, &InfoContextManagerItem::showMetaInfoDialog); 0250 } 0251 0252 void forgetCurrentDocument() 0253 { 0254 if (mDocument) { 0255 QObject::disconnect(mDocument.data(), nullptr, q, nullptr); 0256 // "Garbage collect" document 0257 mDocument = nullptr; 0258 } 0259 } 0260 }; 0261 0262 InfoContextManagerItem::InfoContextManagerItem(ContextManager *manager) 0263 : AbstractContextManagerItem(manager) 0264 , d(new InfoContextManagerItemPrivate) 0265 { 0266 d->q = this; 0267 d->setupGroup(); 0268 connect(contextManager(), &ContextManager::selectionChanged, this, &InfoContextManagerItem::updateSideBarContent); 0269 connect(contextManager(), &ContextManager::selectionDataChanged, this, &InfoContextManagerItem::updateSideBarContent); 0270 } 0271 0272 InfoContextManagerItem::~InfoContextManagerItem() 0273 { 0274 delete d; 0275 } 0276 0277 void InfoContextManagerItem::updateSideBarContent() 0278 { 0279 LOG("updateSideBarContent"); 0280 if (!d->mGroup->isVisible()) { 0281 LOG("updateSideBarContent: not visible, not updating"); 0282 return; 0283 } 0284 LOG("updateSideBarContent: really updating"); 0285 0286 KFileItemList itemList = contextManager()->selectedFileItemList(); 0287 if (itemList.isEmpty()) { 0288 d->forgetCurrentDocument(); 0289 d->mOneFileWidget->hide(); 0290 d->mMultipleFilesLabel->hide(); 0291 d->updateMetaInfoDialog(); 0292 return; 0293 } 0294 0295 KFileItem item = itemList.first(); 0296 if (itemList.count() == 1 && !ArchiveUtils::fileItemIsDirOrArchive(item)) { 0297 fillOneFileGroup(item); 0298 } else { 0299 fillMultipleItemsGroup(itemList); 0300 } 0301 d->updateMetaInfoDialog(); 0302 } 0303 0304 void InfoContextManagerItem::fillOneFileGroup(const KFileItem &item) 0305 { 0306 d->mOneFileWidget->show(); 0307 d->mMultipleFilesLabel->hide(); 0308 0309 d->forgetCurrentDocument(); 0310 d->mDocument = DocumentFactory::instance()->load(item.url()); 0311 connect(d->mDocument.data(), &Document::metaInfoUpdated, this, &InfoContextManagerItem::updateOneFileInfo); 0312 0313 d->updateMetaInfoDialog(); 0314 updateOneFileInfo(); 0315 } 0316 0317 void InfoContextManagerItem::fillMultipleItemsGroup(const KFileItemList &itemList) 0318 { 0319 d->forgetCurrentDocument(); 0320 0321 int folderCount = 0, fileCount = 0; 0322 for (const KFileItem &item : itemList) { 0323 if (item.isDir()) { 0324 folderCount++; 0325 } else { 0326 fileCount++; 0327 } 0328 } 0329 0330 if (folderCount == 0) { 0331 d->mMultipleFilesLabel->setText(i18ncp("@label", "%1 file selected", "%1 files selected", fileCount)); 0332 } else if (fileCount == 0) { 0333 d->mMultipleFilesLabel->setText(i18ncp("@label", "%1 folder selected", "%1 folders selected", folderCount)); 0334 } else { 0335 d->mMultipleFilesLabel->setText(i18nc("@label. The two parameters are strings like '2 folders' and '1 file'.", 0336 "%1 and %2 selected", 0337 i18np("%1 folder", "%1 folders", folderCount), 0338 i18np("%1 file", "%1 files", fileCount))); 0339 } 0340 d->mOneFileWidget->hide(); 0341 d->mMultipleFilesLabel->show(); 0342 } 0343 0344 void InfoContextManagerItem::updateOneFileInfo() 0345 { 0346 if (!d->mDocument) { 0347 return; 0348 } 0349 0350 ImageMetaInfoModel *metaInfoModel = d->mDocument->metaInfo(); 0351 d->mKeyValueWidget->clear(); 0352 const QStringList preferredMetaInfoKeyList = GwenviewConfig::preferredMetaInfoKeyList(); 0353 for (const QString &key : preferredMetaInfoKeyList) { 0354 QString label; 0355 QString value; 0356 metaInfoModel->getInfoForKey(key, &label, &value); 0357 if (!label.isEmpty() && !value.isEmpty()) { 0358 d->mKeyValueWidget->addRow(label, value); 0359 } 0360 } 0361 d->mKeyValueWidget->finishAddRows(); 0362 d->mKeyValueWidget->layoutRows(); 0363 } 0364 0365 void InfoContextManagerItem::showMetaInfoDialog() 0366 { 0367 if (!d->mImageMetaInfoDialog) { 0368 d->mImageMetaInfoDialog = new ImageMetaInfoDialog(d->mOneFileWidget); 0369 d->mImageMetaInfoDialog->setAttribute(Qt::WA_DeleteOnClose, true); 0370 connect(d->mImageMetaInfoDialog.data(), 0371 &ImageMetaInfoDialog::preferredMetaInfoKeyListChanged, 0372 this, 0373 &InfoContextManagerItem::slotPreferredMetaInfoKeyListChanged); 0374 } 0375 d->mImageMetaInfoDialog->setMetaInfo(d->mDocument ? d->mDocument->metaInfo() : nullptr, GwenviewConfig::preferredMetaInfoKeyList()); 0376 d->mImageMetaInfoDialog->show(); 0377 } 0378 0379 void InfoContextManagerItem::slotPreferredMetaInfoKeyListChanged(const QStringList &list) 0380 { 0381 GwenviewConfig::setPreferredMetaInfoKeyList(list); 0382 GwenviewConfig::self()->save(); 0383 updateOneFileInfo(); 0384 } 0385 0386 } // namespace 0387 0388 #include "moc_infocontextmanageritem.cpp"