File indexing completed on 2025-01-05 03:59:54
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2012-07-13 0007 * Description : Modified context menu helper for import tool 0008 * 0009 * SPDX-FileCopyrightText: 2012 by Islam Wazery <wazery at ubuntu dot com> 0010 * SPDX-FileCopyrightText: 2012-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 "importcontextmenu.h" 0017 0018 // Qt includes 0019 0020 #include <QAction> 0021 #include <QIcon> 0022 #include <QMimeType> 0023 #include <QMimeDatabase> 0024 0025 // KDE includes 0026 0027 // Pragma directives to reduce warnings from KDE header files. 0028 #if defined(Q_CC_GNU) 0029 # pragma GCC diagnostic push 0030 # pragma GCC diagnostic ignored "-Wdeprecated-declarations" 0031 #endif 0032 0033 #if defined(Q_CC_CLANG) 0034 # pragma clang diagnostic push 0035 # pragma clang diagnostic ignored "-Wmismatched-tags" 0036 # pragma clang diagnostic ignored "-Wdeprecated-declarations" 0037 #endif 0038 0039 #include <kservice_version.h> 0040 0041 #if KSERVICE_VERSION > QT_VERSION_CHECK(5, 81, 0) 0042 # include <kapplicationtrader.h> 0043 #else 0044 # include <kmimetypetrader.h> 0045 #endif 0046 0047 #include <klocalizedstring.h> 0048 #include <kactioncollection.h> 0049 0050 #ifdef HAVE_KIO 0051 # include <kopenwithdialog.h> 0052 #endif 0053 0054 // Restore warnings 0055 #if defined(Q_CC_GNU) 0056 # pragma GCC diagnostic pop 0057 #endif 0058 0059 #if defined(Q_CC_CLANG) 0060 # pragma clang diagnostic pop 0061 #endif 0062 0063 // Local includes 0064 0065 #include "importui.h" 0066 #include "picklabelwidget.h" 0067 #include "colorlabelwidget.h" 0068 #include "ratingwidget.h" 0069 #include "tagmodificationhelper.h" 0070 #include "tagspopupmenu.h" 0071 #include "fileactionmngr.h" 0072 #include "dservicemenu.h" 0073 0074 namespace Digikam 0075 { 0076 0077 class Q_DECL_HIDDEN ImportContextMenuHelper::Private 0078 { 0079 public: 0080 0081 explicit Private(ImportContextMenuHelper* const q) 0082 : importFilterModel (nullptr), 0083 parent (nullptr), 0084 ABCmenu (nullptr), 0085 stdActionCollection(nullptr), 0086 q (q) 0087 { 0088 } 0089 0090 QList<qlonglong> selectedIds; 0091 QList<QUrl> selectedItems; 0092 0093 QMap<int, QAction*> queueActions; 0094 QMap<QString, KService::Ptr> servicesMap; 0095 0096 ImportFilterModel* importFilterModel; 0097 0098 QMenu* parent; 0099 QMenu* ABCmenu; 0100 0101 KActionCollection* stdActionCollection; 0102 0103 ImportContextMenuHelper* q; 0104 0105 public: 0106 0107 QAction* copyFromMainCollection(const QString& name) const 0108 { 0109 QAction* const mainAction = stdActionCollection->action(name); 0110 0111 if (!mainAction) 0112 { 0113 return nullptr; 0114 } 0115 0116 QAction* const action = new QAction(mainAction->icon(), mainAction->text(), q); 0117 action->setToolTip(mainAction->toolTip()); 0118 0119 return action; 0120 } 0121 }; 0122 0123 ImportContextMenuHelper::ImportContextMenuHelper(QMenu* const parent, KActionCollection* const actionCollection) 0124 : QObject(parent), 0125 d (new Private(this)) 0126 { 0127 d->parent = parent; 0128 0129 if (!actionCollection) 0130 { 0131 d->stdActionCollection = ImportUI::instance()->actionCollection(); 0132 } 0133 else 0134 { 0135 d->stdActionCollection = actionCollection; 0136 } 0137 } 0138 0139 ImportContextMenuHelper::~ImportContextMenuHelper() 0140 { 0141 delete d; 0142 } 0143 0144 void ImportContextMenuHelper::addAction(const QString& name, bool addDisabled) 0145 { 0146 QAction* const action = d->stdActionCollection->action(name); 0147 addAction(action, addDisabled); 0148 } 0149 0150 void ImportContextMenuHelper::addAction(QAction* action, bool addDisabled) 0151 { 0152 if (!action) 0153 { 0154 return; 0155 } 0156 0157 if (action->isEnabled() || addDisabled) 0158 { 0159 d->parent->addAction(action); 0160 } 0161 } 0162 0163 void ImportContextMenuHelper::addSubMenu(QMenu* subMenu) 0164 { 0165 d->parent->addMenu(subMenu); 0166 } 0167 0168 void ImportContextMenuHelper::addSeparator() 0169 { 0170 d->parent->addSeparator(); 0171 } 0172 0173 void ImportContextMenuHelper::addAction(QAction* action, QObject* recv, const char* slot, 0174 bool addDisabled) 0175 { 0176 if (!action) 0177 { 0178 return; 0179 } 0180 0181 connect(action, SIGNAL(triggered()), 0182 recv, slot); 0183 0184 addAction(action, addDisabled); 0185 } 0186 0187 void ImportContextMenuHelper::addServicesMenu(const QList<QUrl>& selectedItems) 0188 { 0189 setSelectedItems(selectedItems); 0190 0191 // This code is inspired by KonqMenuActions: 0192 // kdebase/apps/lib/konq/konq_menuactions.cpp 0193 0194 QStringList mimeTypes; 0195 KService::List offers; 0196 0197 Q_FOREACH (const QUrl& item, d->selectedItems) 0198 { 0199 const QString mimeType = QMimeDatabase().mimeTypeForFile(item.toLocalFile(), QMimeDatabase::MatchExtension).name(); 0200 0201 if (!mimeTypes.contains(mimeType)) 0202 { 0203 mimeTypes << mimeType; 0204 } 0205 } 0206 0207 if (!mimeTypes.isEmpty()) 0208 { 0209 // Query trader 0210 0211 const QString firstMimeType = mimeTypes.takeFirst(); 0212 const QString constraintTemplate = QString::fromUtf8("'%1' in ServiceTypes"); 0213 QStringList constraints; 0214 0215 Q_FOREACH (const QString& mimeType, mimeTypes) 0216 { 0217 constraints << constraintTemplate.arg(mimeType); 0218 } 0219 0220 #if KSERVICE_VERSION > QT_VERSION_CHECK(5, 81, 0) 0221 offers = KApplicationTrader::queryByMimeType(firstMimeType); 0222 #else 0223 offers = KMimeTypeTrader::self()->query(firstMimeType, 0224 QLatin1String("Application"), 0225 constraints.join(QLatin1String(" and "))); 0226 #endif 0227 0228 // remove duplicate service entries 0229 0230 QSet<QString> seenApps; 0231 0232 for (KService::List::iterator it = offers.begin() ; it != offers.end() ; ) 0233 { 0234 const QString appName((*it)->name()); 0235 0236 if (!seenApps.contains(appName)) 0237 { 0238 seenApps.insert(appName); 0239 ++it; 0240 } 0241 else 0242 { 0243 it = offers.erase(it); 0244 } 0245 } 0246 } 0247 0248 if (!offers.isEmpty() && ImportUI::instance()->cameraUseUMSDriver()) 0249 { 0250 QMenu* const servicesMenu = new QMenu(d->parent); 0251 qDeleteAll(servicesMenu->actions()); 0252 0253 QAction* const serviceAction = servicesMenu->menuAction(); 0254 serviceAction->setText(i18nc("@title:menu open with desktop application", "Open With")); 0255 0256 Q_FOREACH (KService::Ptr service, offers) 0257 { 0258 QString name = service->name().replace(QLatin1Char('&'), QLatin1String("&&")); 0259 QAction* const action = servicesMenu->addAction(name); 0260 action->setIcon(QIcon::fromTheme(service->icon())); 0261 action->setData(service->name()); 0262 d->servicesMap[name] = service; 0263 } 0264 0265 #ifdef HAVE_KIO 0266 0267 servicesMenu->addSeparator(); 0268 servicesMenu->addAction(i18nc("@item:inmenu open with other application", "Other...")); 0269 0270 addAction(serviceAction); 0271 0272 connect(servicesMenu, SIGNAL(triggered(QAction*)), 0273 this, SLOT(slotOpenWith(QAction*))); 0274 } 0275 else if (ImportUI::instance()->cameraUseUMSDriver()) 0276 { 0277 QAction* const serviceAction = new QAction(i18nc("@title:menu", "Open With..."), this); 0278 addAction(serviceAction); 0279 0280 connect(serviceAction, SIGNAL(triggered()), 0281 this, SLOT(slotOpenWith())); 0282 0283 #endif // HAVE_KIO 0284 0285 } 0286 } 0287 0288 void ImportContextMenuHelper::slotOpenWith() 0289 { 0290 // call the slot with an "empty" action 0291 0292 slotOpenWith(nullptr); 0293 } 0294 0295 void ImportContextMenuHelper::slotOpenWith(QAction* action) 0296 { 0297 KService::Ptr service; 0298 QList<QUrl> list = d->selectedItems; 0299 0300 QString name = action ? action->data().toString() : QString(); 0301 0302 #ifdef HAVE_KIO 0303 0304 if (name.isEmpty()) 0305 { 0306 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(list); 0307 0308 if (dlg->exec() != KOpenWithDialog::Accepted) 0309 { 0310 delete dlg; 0311 return; 0312 } 0313 0314 service = dlg->service(); 0315 0316 if (!service) 0317 { 0318 // User entered a custom command 0319 0320 if (!dlg->text().isEmpty()) 0321 { 0322 DServiceMenu::runFiles(dlg->text(), list); 0323 } 0324 0325 delete dlg; 0326 return; 0327 } 0328 0329 delete dlg; 0330 } 0331 else 0332 0333 #endif // HAVE_KIO 0334 0335 { 0336 service = d->servicesMap[name]; 0337 } 0338 0339 DServiceMenu::runFiles(service, list); 0340 } 0341 0342 void ImportContextMenuHelper::addRotateMenu(itemIds& /*ids*/) 0343 { 0344 /* 0345 setSelectedIds(ids); 0346 0347 QMenu* const imageRotateMenu = new QMenu(i18n("Rotate"), d->parent); 0348 imageRotateMenu->setIcon(QIcon::fromTheme(QLatin1String("object-rotate-right"))); 0349 0350 QAction* const left = new QAction(this); 0351 left->setObjectName(QLatin1String("rotate_ccw")); 0352 left->setText(i18nc("rotate image left", "Left")); 0353 connect(left, SIGNAL(triggered(bool)), 0354 this, SLOT(slotRotate())); 0355 imageRotateMenu->addAction(left); 0356 0357 QAction* const right = new QAction(this); 0358 right->setObjectName(QLatin1String("rotate_cw"); 0359 right->setText(i18nc("rotate image right", "Right"))); 0360 connect(right, SIGNAL(triggered(bool)), 0361 this, SLOT(slotRotate())); 0362 imageRotateMenu->addAction(right); 0363 0364 d->parent->addMenu(imageRotateMenu); 0365 */ 0366 } 0367 0368 void ImportContextMenuHelper::slotRotate() 0369 { 0370 /* 0371 TODO: Implement rotate in import tool. 0372 0373 if (sender()->objectName() == "rotate_ccw") 0374 { 0375 FileActionMngr::instance()->transform(CamItemInfoList(d->selectedIds), MetaEngineRotation::Rotate270); 0376 } 0377 else 0378 { 0379 FileActionMngr::instance()->transform(CamItemInfoList(d->selectedIds), MetaEngineRotation::Rotate90); 0380 } 0381 */ 0382 } 0383 0384 void ImportContextMenuHelper::addAssignTagsMenu(itemIds& /*ids*/) 0385 { 0386 /* 0387 setSelectedIds(ids); 0388 0389 QMenu* const assignTagsPopup = new TagsPopupMenu(ids, TagsPopupMenu::RECENTLYASSIGNED, d->parent); 0390 assignTagsPopup->menuAction()->setText(i18n("Assign Tag")); 0391 assignTagsPopup->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("tag"))); 0392 d->parent->addMenu(assignTagsPopup); 0393 0394 connect(assignTagsPopup, SIGNAL(signalTagActivated(int)), 0395 this, SIGNAL(signalAssignTag(int))); 0396 0397 connect(assignTagsPopup, SIGNAL(signalPopupTagsView()), 0398 this, SIGNAL(signalPopupTagsView())); 0399 */ 0400 } 0401 0402 void ImportContextMenuHelper::addRemoveTagsMenu(itemIds& /*ids*/) 0403 { 0404 /* 0405 setSelectedIds(ids); 0406 0407 QMenu* const removeTagsPopup = new TagsPopupMenu(ids, TagsPopupMenu::REMOVE, d->parent); 0408 removeTagsPopup->menuAction()->setText(i18n("Remove Tag")); 0409 removeTagsPopup->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("tag"))); 0410 d->parent->addMenu(removeTagsPopup); 0411 0412 connect(removeTagsPopup, SIGNAL(signalTagActivated(int)), 0413 this, SIGNAL(signalRemoveTag(int))); 0414 */ 0415 } 0416 0417 void ImportContextMenuHelper::addLabelsAction() 0418 { 0419 QMenu* const menuLabels = new QMenu(i18nc("@title:menu", "Assign Labels"), d->parent); 0420 PickLabelMenuAction* const pmenu = new PickLabelMenuAction(d->parent); 0421 ColorLabelMenuAction* const cmenu = new ColorLabelMenuAction(d->parent); 0422 RatingMenuAction* const rmenu = new RatingMenuAction(d->parent); 0423 menuLabels->addAction(pmenu->menuAction()); 0424 menuLabels->addAction(cmenu->menuAction()); 0425 menuLabels->addAction(rmenu->menuAction()); 0426 addSubMenu(menuLabels); 0427 0428 connect(pmenu, SIGNAL(signalPickLabelChanged(int)), 0429 this, SIGNAL(signalAssignPickLabel(int))); 0430 0431 connect(cmenu, SIGNAL(signalColorLabelChanged(int)), 0432 this, SIGNAL(signalAssignColorLabel(int))); 0433 0434 connect(rmenu, SIGNAL(signalRatingChanged(int)), 0435 this, SIGNAL(signalAssignRating(int))); 0436 } 0437 0438 void ImportContextMenuHelper::slotABCMenuTriggered(QAction* action) 0439 { 0440 QString name = action->iconText(); 0441 Q_EMIT signalAddNewTagFromABCMenu(name); 0442 } 0443 0444 void ImportContextMenuHelper::setImportFilterModel(ImportFilterModel* model) 0445 { 0446 d->importFilterModel = model; 0447 } 0448 0449 QAction* ImportContextMenuHelper::exec(const QPoint& pos, QAction* at) 0450 { 0451 QAction* const choice = d->parent->exec(pos, at); 0452 0453 if (choice) 0454 { 0455 // check if a BQM action has been triggered 0456 0457 for (QMap<int, QAction*>::const_iterator it = d->queueActions.constBegin() ; 0458 it != d->queueActions.constEnd() ; ++it) 0459 { 0460 if (choice == it.value()) 0461 { 0462 /* 0463 Q_EMIT signalAddToExistingQueue(it.key()); 0464 */ 0465 return choice; 0466 } 0467 } 0468 } 0469 0470 return choice; 0471 } 0472 0473 void ImportContextMenuHelper::setSelectedIds(itemIds& ids) 0474 { 0475 if (d->selectedIds.isEmpty()) 0476 { 0477 d->selectedIds = ids; 0478 } 0479 } 0480 0481 void ImportContextMenuHelper::setSelectedItems(const QList<QUrl>& urls) 0482 { 0483 if (d->selectedItems.isEmpty()) 0484 { 0485 d->selectedItems = urls; 0486 } 0487 } 0488 0489 } // namespace Digikam 0490 0491 #include "moc_importcontextmenu.cpp"