File indexing completed on 2025-01-19 03:51:18
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-05-16 0007 * Description : A tool to edit geolocation 0008 * 0009 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2010-2014 by Michael G. Hansen <mike at mghansen dot de> 0011 * SPDX-FileCopyrightText: 2010 by Gabriel Voicu <ping dot gabi at gmail dot com> 0012 * SPDX-FileCopyrightText: 2014 by Justus Schwartz <justus at gmx dot li> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "geolocationedit.h" 0019 0020 // Qt includes 0021 0022 #include <QtConcurrentMap> 0023 #include <QButtonGroup> 0024 #include <QCheckBox> 0025 #include <QCloseEvent> 0026 #include <QFuture> 0027 #include <QFutureWatcher> 0028 #include <QGroupBox> 0029 #include <QHBoxLayout> 0030 #include <QHeaderView> 0031 #include <QLabel> 0032 #include <QPushButton> 0033 #include <QGridLayout> 0034 #include <QPointer> 0035 #include <QRadioButton> 0036 #include <QSplitter> 0037 #include <QStackedLayout> 0038 #include <QStackedWidget> 0039 #include <QTimer> 0040 #include <QToolButton> 0041 #include <QTreeView> 0042 #include <QMenu> 0043 #include <QUndoView> 0044 #include <QAction> 0045 #include <QApplication> 0046 #include <QComboBox> 0047 #include <QDialogButtonBox> 0048 #include <QActionGroup> 0049 0050 // KDE includes 0051 0052 #include <kconfiggroup.h> 0053 #include <ksharedconfig.h> 0054 #include <klocalizedstring.h> 0055 0056 // Local includes 0057 0058 #include "dlayoutbox.h" 0059 #include "digikam_config.h" 0060 #include "itemmarkertiler.h" 0061 #include "gpscommon.h" 0062 #include "gpsitemmodel.h" 0063 #include "mapdragdrophandler.h" 0064 #include "gpsitemlist.h" 0065 #include "gpsitemlistdragdrophandler.h" 0066 #include "gpsitemlistcontextmenu.h" 0067 #include "gpscorrelatorwidget.h" 0068 #include "digikam_debug.h" 0069 #include "dmessagebox.h" 0070 #include "rgwidget.h" 0071 #include "kmlwidget.h" 0072 #include "statusprogressbar.h" 0073 #include "searchresultwidget.h" 0074 #include "backend-rg.h" 0075 #include "gpsitemdetails.h" 0076 #include "gpsgeoifacemodelhelper.h" 0077 #include "dxmlguiwindow.h" 0078 #include "gpsbookmarkowner.h" 0079 #include "gpsbookmarkmodelhelper.h" 0080 0081 #ifdef GPSSYNC_MODELTEST 0082 # include <modeltest.h> 0083 #endif 0084 0085 using namespace Digikam; 0086 0087 namespace DigikamGenericGeolocationEditPlugin 0088 { 0089 0090 struct SaveChangedImagesHelper 0091 { 0092 public: 0093 0094 explicit SaveChangedImagesHelper(GPSItemModel* const model) 0095 : imageModel(model) 0096 { 0097 } 0098 0099 QPair<QUrl, QString> operator()(const QPersistentModelIndex& itemIndex) 0100 { 0101 GPSItemContainer* const item = imageModel->itemFromIndex(itemIndex); 0102 0103 if (!item) 0104 { 0105 return QPair<QUrl, QString>(QUrl(), QString()); 0106 } 0107 0108 return QPair<QUrl, QString>(item->url(), item->saveChanges()); 0109 } 0110 0111 public: 0112 0113 typedef QPair<QUrl, QString> result_type; 0114 GPSItemModel* const imageModel; 0115 }; 0116 0117 // --------------------------------------------------------------------------------- 0118 0119 struct LoadFileMetadataHelper 0120 { 0121 public: 0122 0123 explicit LoadFileMetadataHelper(GPSItemModel* const model) 0124 : imageModel(model) 0125 { 0126 } 0127 0128 QPair<QUrl, QString> operator()(const QPersistentModelIndex& itemIndex) 0129 { 0130 GPSItemContainer* const item = imageModel->itemFromIndex(itemIndex); 0131 0132 if (!item) 0133 { 0134 return QPair<QUrl, QString>(QUrl(), QString()); 0135 } 0136 0137 item->loadImageData(); 0138 0139 return QPair<QUrl, QString>(item->url(), QString()); 0140 } 0141 0142 public: 0143 0144 typedef QPair<QUrl, QString> result_type; 0145 GPSItemModel* const imageModel; 0146 }; 0147 0148 // --------------------------------------------------------------------------------- 0149 0150 class Q_DECL_HIDDEN GeolocationEdit::Private 0151 { 0152 public: 0153 0154 explicit Private() 0155 : imageModel (nullptr), 0156 selectionModel (nullptr), 0157 uiEnabled (true), 0158 listViewContextMenu (nullptr), 0159 trackManager (nullptr), 0160 fileIOFutureWatcher (nullptr), 0161 fileIOCountDone (0), 0162 fileIOCountTotal (0), 0163 fileIOCloseAfterSaving (false), 0164 buttonBox (nullptr), 0165 VSplitter (nullptr), 0166 HSplitter (nullptr), 0167 treeView (nullptr), 0168 stackedWidget (nullptr), 0169 tabBar (nullptr), 0170 splitterSize (0), 0171 undoStack (nullptr), 0172 undoView (nullptr), 0173 progressBar (nullptr), 0174 progressCancelButton (nullptr), 0175 progressCancelObject (nullptr), 0176 detailsWidget (nullptr), 0177 correlatorWidget (nullptr), 0178 rgWidget (nullptr), 0179 searchWidget (nullptr), 0180 kmlWidget (nullptr), 0181 mapLayout (MapLayoutOne), 0182 mapSplitter (nullptr), 0183 mapWidget (nullptr), 0184 mapWidget2 (nullptr), 0185 mapDragDropHandler (nullptr), 0186 mapModelHelper (nullptr), 0187 geoifaceMarkerModel (nullptr), 0188 sortActionOldestFirst (nullptr), 0189 sortActionYoungestFirst (nullptr), 0190 sortMenu (nullptr), 0191 cbMapLayout (nullptr), 0192 bookmarkOwner (nullptr), 0193 actionBookmarkVisibility (nullptr), 0194 iface (nullptr) 0195 { 0196 } 0197 0198 // General things 0199 GPSItemModel* imageModel; 0200 QItemSelectionModel* selectionModel; 0201 bool uiEnabled; 0202 GPSItemListContextMenu* listViewContextMenu; 0203 TrackManager* trackManager; 0204 0205 // Loading and saving 0206 QFuture<QPair<QUrl,QString> > fileIOFuture; 0207 QFutureWatcher<QPair<QUrl,QString> >* fileIOFutureWatcher; 0208 int fileIOCountDone; 0209 int fileIOCountTotal; 0210 bool fileIOCloseAfterSaving; 0211 0212 // UI 0213 QDialogButtonBox* buttonBox; 0214 QSplitter* VSplitter; 0215 QSplitter* HSplitter; 0216 GPSItemList* treeView; 0217 QStackedWidget* stackedWidget; 0218 QTabBar* tabBar; 0219 int splitterSize; 0220 QUndoStack* undoStack; 0221 QUndoView* undoView; 0222 0223 // UI: progress 0224 StatusProgressBar* progressBar; 0225 QPushButton* progressCancelButton; 0226 QObject* progressCancelObject; 0227 QString progressCancelSlot; 0228 0229 // UI: tab widgets 0230 GPSItemDetails* detailsWidget; 0231 GPSCorrelatorWidget* correlatorWidget; 0232 RGWidget* rgWidget; 0233 SearchResultWidget* searchWidget; 0234 KmlWidget* kmlWidget; 0235 0236 // map: UI 0237 MapLayout mapLayout; 0238 QSplitter* mapSplitter; 0239 MapWidget* mapWidget; 0240 MapWidget* mapWidget2; 0241 0242 // map: helpers 0243 MapDragDropHandler* mapDragDropHandler; 0244 GPSGeoIfaceModelHelper* mapModelHelper; 0245 ItemMarkerTiler* geoifaceMarkerModel; 0246 0247 // map: actions 0248 QAction* sortActionOldestFirst; 0249 QAction* sortActionYoungestFirst; 0250 QMenu* sortMenu; 0251 QComboBox* cbMapLayout; 0252 0253 GPSBookmarkOwner* bookmarkOwner; 0254 QAction* actionBookmarkVisibility; 0255 0256 DInfoInterface* iface; 0257 }; 0258 0259 GeolocationEdit::GeolocationEdit(QWidget* const parent, DInfoInterface* const iface) 0260 : DPluginDialog(parent, QLatin1String("Geolocation Edit Settings")), 0261 d (new Private) 0262 { 0263 setAttribute(Qt::WA_DeleteOnClose, true); 0264 setWindowTitle(i18nc("@title:window", "Geolocation Editor")); 0265 setMinimumSize(300, 400); 0266 setModal(true); 0267 0268 d->iface = iface; 0269 d->imageModel = new GPSItemModel(this); 0270 d->selectionModel = new QItemSelectionModel(d->imageModel); 0271 d->trackManager = new TrackManager(this); 0272 0273 #ifdef GPSSYNC_MODELTEST 0274 0275 new ModelTest(d->imageModel, this); 0276 0277 #endif 0278 0279 d->bookmarkOwner = new GPSBookmarkOwner(d->imageModel, this); 0280 d->undoStack = new QUndoStack(this); 0281 d->stackedWidget = new QStackedWidget(); 0282 d->searchWidget = new SearchResultWidget(d->bookmarkOwner, 0283 d->imageModel, 0284 d->selectionModel, 0285 d->stackedWidget); 0286 0287 GPSItemContainer::setHeaderData(d->imageModel); 0288 d->mapModelHelper = new GPSGeoIfaceModelHelper(d->imageModel, d->selectionModel, this); 0289 d->mapModelHelper->addUngroupedModelHelper(d->bookmarkOwner->bookmarkModelHelper()); 0290 0291 d->mapModelHelper->addUngroupedModelHelper(d->searchWidget->getModelHelper()); 0292 d->mapDragDropHandler = new MapDragDropHandler(d->imageModel, d->mapModelHelper); 0293 d->geoifaceMarkerModel = new ItemMarkerTiler(d->mapModelHelper, this); 0294 0295 d->actionBookmarkVisibility = new QAction(this); 0296 d->actionBookmarkVisibility->setIcon(QIcon::fromTheme(QLatin1String("bookmark-new"))); 0297 d->actionBookmarkVisibility->setToolTip(i18nc("@info", "Display bookmarked positions on the map.")); 0298 d->actionBookmarkVisibility->setCheckable(true); 0299 0300 connect(d->actionBookmarkVisibility, SIGNAL(changed()), 0301 this, SLOT(slotBookmarkVisibilityToggled())); 0302 0303 QVBoxLayout* const mainLayout = new QVBoxLayout(this); 0304 setLayout(mainLayout); 0305 0306 DHBox* const hboxMain = new DHBox(this); 0307 mainLayout->addWidget(hboxMain, 10); 0308 0309 d->HSplitter = new QSplitter(Qt::Horizontal, hboxMain); 0310 d->HSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0311 0312 // ------------------------------------------------------------------------------------------------ 0313 0314 DHBox* const hbox = new DHBox(this); 0315 QLabel* const labelMapLayout = new QLabel(i18nc("@label", "Layout:"), hbox); 0316 d->cbMapLayout = new QComboBox(hbox); 0317 d->cbMapLayout->addItem(i18nc("@item", "One map"), QVariant::fromValue(MapLayoutOne)); 0318 d->cbMapLayout->addItem(i18nc("@item", "Two maps - left and right"), QVariant::fromValue(MapLayoutVertical)); 0319 d->cbMapLayout->addItem(i18nc("@item", "Two maps - top and bottom"), QVariant::fromValue(MapLayoutHorizontal)); 0320 labelMapLayout->setBuddy(d->cbMapLayout); 0321 0322 d->progressBar = new StatusProgressBar(hbox); 0323 d->progressBar->setVisible(false); 0324 d->progressBar->setProgressBarMode(StatusProgressBar::ProgressBarMode); 0325 d->progressBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 0326 0327 d->progressCancelButton = new QPushButton(hbox); 0328 d->progressCancelButton->setVisible(false); 0329 d->progressCancelButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 0330 d->progressCancelButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))); 0331 0332 connect(d->progressCancelButton, SIGNAL(clicked()), 0333 this, SLOT(slotProgressCancelButtonClicked())); 0334 0335 m_buttons->addButton(QDialogButtonBox::Yes); 0336 m_buttons->addButton(QDialogButtonBox::Apply); 0337 m_buttons->addButton(QDialogButtonBox::Close); 0338 m_buttons->button(QDialogButtonBox::Yes)->setText(i18n("OK")); 0339 m_buttons->button(QDialogButtonBox::Yes)->setAutoDefault(false); 0340 m_buttons->button(QDialogButtonBox::Apply)->setAutoDefault(false); 0341 m_buttons->button(QDialogButtonBox::Close)->setAutoDefault(false); 0342 m_buttons->setParent(hbox); 0343 0344 connect(m_buttons->button(QDialogButtonBox::Yes), &QPushButton::clicked, 0345 this, &GeolocationEdit::slotOkClicked); 0346 0347 connect(m_buttons->button(QDialogButtonBox::Apply), &QPushButton::clicked, 0348 this, &GeolocationEdit::slotApplyClicked); 0349 0350 connect(m_buttons->button(QDialogButtonBox::Close), &QPushButton::clicked, 0351 this, &GeolocationEdit::close); 0352 0353 mainLayout->addWidget(hbox, 0); 0354 0355 // ------------------------------------------------------------------------------------------------ 0356 0357 d->VSplitter = new QSplitter(Qt::Vertical, d->HSplitter); 0358 d->HSplitter->addWidget(d->VSplitter); 0359 d->HSplitter->setStretchFactor(0, 10); 0360 0361 d->sortMenu = new QMenu(this); 0362 d->sortMenu->setTitle(i18nc("@title", "Sorting")); 0363 QActionGroup* const sortOrderExclusive = new QActionGroup(d->sortMenu); 0364 sortOrderExclusive->setExclusive(true); 0365 0366 connect(sortOrderExclusive, SIGNAL(triggered(QAction*)), 0367 this, SLOT(slotSortOptionTriggered(QAction*))); 0368 0369 d->sortActionOldestFirst = new QAction(i18nc("@action", "Show oldest first"), sortOrderExclusive); 0370 d->sortActionOldestFirst->setCheckable(true); 0371 d->sortMenu->addAction(d->sortActionOldestFirst); 0372 0373 d->sortActionYoungestFirst = new QAction(i18nc("@action", "Show youngest first"), sortOrderExclusive); 0374 d->sortMenu->addAction(d->sortActionYoungestFirst); 0375 d->sortActionYoungestFirst->setCheckable(true); 0376 0377 QWidget* mapVBox = nullptr; 0378 d->mapWidget = makeMapWidget(&mapVBox); 0379 d->searchWidget->setPrimaryMapWidget(d->mapWidget); 0380 d->mapSplitter = new QSplitter(this); 0381 d->mapSplitter->addWidget(mapVBox); 0382 d->VSplitter->addWidget(d->mapSplitter); 0383 0384 d->treeView = new GPSItemList(this); 0385 d->treeView->setModelAndSelectionModel(d->imageModel, d->selectionModel); 0386 d->treeView->setDragDropHandler(new GPSItemListDragDropHandler(this)); 0387 d->treeView->setDragEnabled(true); 0388 0389 /** 0390 * TODO: save and restore the state of the header 0391 * TODO: add a context menu to the header to select which columns should be visible 0392 * TODO: add sorting by column 0393 */ 0394 0395 d->treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); 0396 d->treeView->setSortingEnabled(true); 0397 d->VSplitter->addWidget(d->treeView); 0398 0399 d->listViewContextMenu = new GPSItemListContextMenu(d->treeView, d->bookmarkOwner); 0400 d->HSplitter->addWidget(d->stackedWidget); 0401 d->HSplitter->setCollapsible(1, true); 0402 d->splitterSize = 0; 0403 0404 DVBox* const vboxTabBar = new DVBox(hboxMain); 0405 vboxTabBar->layout()->setContentsMargins(QMargins()); 0406 vboxTabBar->layout()->setSpacing(0); 0407 0408 d->tabBar = new QTabBar(vboxTabBar); 0409 d->tabBar->setShape(QTabBar::RoundedEast); 0410 0411 dynamic_cast<QVBoxLayout*>(vboxTabBar->layout())->addStretch(200); 0412 0413 d->tabBar->addTab(i18nc("@item: map tool", "Details")); 0414 d->tabBar->addTab(i18nc("@item: map tool", "GPS Correlator")); 0415 d->tabBar->addTab(i18nc("@item: map tool", "Undo/Redo")); 0416 d->tabBar->addTab(i18nc("@item: map tool", "Reverse Geocoding")); 0417 d->tabBar->addTab(i18nc("@item: map tool", "Search")); 0418 d->tabBar->addTab(i18nc("@item: map tool", "KML Export")); 0419 0420 d->tabBar->installEventFilter(this); 0421 0422 d->detailsWidget = new GPSItemDetails(d->stackedWidget, d->imageModel); 0423 d->stackedWidget->addWidget(d->detailsWidget); 0424 0425 d->correlatorWidget = new GPSCorrelatorWidget(d->stackedWidget, d->imageModel, d->selectionModel, d->trackManager); 0426 d->stackedWidget->addWidget(d->correlatorWidget); 0427 0428 d->undoView = new QUndoView(d->undoStack, d->stackedWidget); 0429 d->stackedWidget->addWidget(d->undoView); 0430 0431 d->rgWidget = new RGWidget(d->imageModel, d->selectionModel, d->iface->tagFilterModel(), d->stackedWidget); 0432 d->stackedWidget->addWidget(d->rgWidget); 0433 0434 d->stackedWidget->addWidget(d->searchWidget); 0435 0436 d->kmlWidget = new KmlWidget(this, d->imageModel, d->iface); 0437 d->stackedWidget->addWidget(d->kmlWidget); 0438 0439 // --------------------------------------------------------------- 0440 0441 connect(d->treeView, SIGNAL(signalImageActivated(QModelIndex)), 0442 this, SLOT(slotImageActivated(QModelIndex))); 0443 0444 connect(d->correlatorWidget, SIGNAL(signalSetUIEnabled(bool)), 0445 this, SLOT(slotSetUIEnabled(bool))); 0446 0447 connect(d->correlatorWidget, SIGNAL(signalSetUIEnabled(bool,QObject*const,QString)), 0448 this, SLOT(slotSetUIEnabled(bool,QObject*const,QString))); 0449 0450 connect(d->correlatorWidget, SIGNAL(signalProgressSetup(int,QString)), 0451 this, SLOT(slotProgressSetup(int,QString))); 0452 0453 connect(d->correlatorWidget, SIGNAL(signalProgressChanged(int)), 0454 this, SLOT(slotProgressChanged(int))); 0455 0456 connect(d->correlatorWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0457 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0458 0459 connect(d->correlatorWidget, SIGNAL(signalTrackListChanged(Digikam::GeoCoordinates)), 0460 this, SLOT(slotTrackListChanged(Digikam::GeoCoordinates))); 0461 0462 connect(d->mapModelHelper, SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0463 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0464 0465 connect(d->rgWidget, SIGNAL(signalSetUIEnabled(bool)), 0466 this, SLOT(slotSetUIEnabled(bool))); 0467 0468 connect(d->rgWidget, SIGNAL(signalSetUIEnabled(bool,QObject*const,QString)), 0469 this, SLOT(slotSetUIEnabled(bool,QObject*const,QString))); 0470 0471 connect(d->rgWidget, SIGNAL(signalProgressSetup(int,QString)), 0472 this, SLOT(slotProgressSetup(int,QString))); 0473 0474 connect(d->rgWidget, SIGNAL(signalProgressChanged(int)), 0475 this, SLOT(slotProgressChanged(int))); 0476 0477 connect(d->rgWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0478 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0479 0480 connect(d->searchWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0481 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0482 0483 connect(d->listViewContextMenu, SIGNAL(signalSetUIEnabled(bool)), 0484 this, SLOT(slotSetUIEnabled(bool))); 0485 0486 connect(d->listViewContextMenu, SIGNAL(signalSetUIEnabled(bool,QObject*const,QString)), 0487 this, SLOT(slotSetUIEnabled(bool,QObject*const,QString))); 0488 0489 connect(d->listViewContextMenu, SIGNAL(signalProgressSetup(int,QString)), 0490 this, SLOT(slotProgressSetup(int,QString))); 0491 0492 connect(d->listViewContextMenu, SIGNAL(signalProgressChanged(int)), 0493 this, SLOT(slotProgressChanged(int))); 0494 0495 connect(d->listViewContextMenu, SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0496 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0497 0498 connect(d->tabBar, SIGNAL(currentChanged(int)), 0499 this, SLOT(slotCurrentTabChanged(int))); 0500 0501 connect(d->bookmarkOwner->bookmarkModelHelper(), 0502 SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0503 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0504 0505 connect(d->detailsWidget, SIGNAL(signalUndoCommand(GPSUndoCommand*)), 0506 this, SLOT(slotGPSUndoCommand(GPSUndoCommand*))); 0507 0508 connect(d->cbMapLayout, SIGNAL(activated(int)), 0509 this, SLOT(slotLayoutChanged(int))); 0510 0511 connect(this, SIGNAL(signalMetadataChangedForUrl(QUrl)), 0512 d->iface, SLOT(slotMetadataChangedForUrl(QUrl))); 0513 0514 readSettings(); 0515 0516 d->mapWidget->setActive(true); 0517 0518 setItems(d->iface->currentGPSItems()); 0519 } 0520 0521 GeolocationEdit::~GeolocationEdit() 0522 { 0523 delete d; 0524 } 0525 0526 bool GeolocationEdit::eventFilter(QObject* const o, QEvent* const e) 0527 { 0528 if ((o == d->tabBar) && (e->type() == QEvent::MouseButtonPress)) 0529 { 0530 QMouseEvent const* m = static_cast<QMouseEvent*>(e); 0531 0532 QPoint p( 0533 0534 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0535 0536 m->position().toPoint() 0537 #else 0538 0539 m->x(), m->y() 0540 0541 #endif 0542 0543 ); 0544 0545 const int var = d->tabBar->tabAt(p); 0546 0547 if (var < 0) 0548 { 0549 return false; 0550 } 0551 0552 QList<int> sizes = d->HSplitter->sizes(); 0553 0554 if (d->splitterSize == 0) 0555 { 0556 if (sizes.at(1) == 0) 0557 { 0558 sizes[1] = d->stackedWidget->widget(var)->minimumSizeHint().width(); 0559 } 0560 else if (d->tabBar->currentIndex() == var) 0561 { 0562 d->splitterSize = sizes.at(1); 0563 sizes[1] = 0; 0564 } 0565 } 0566 else 0567 { 0568 sizes[1] = d->splitterSize; 0569 d->splitterSize = 0; 0570 } 0571 0572 d->tabBar->setCurrentIndex(var); 0573 d->stackedWidget->setCurrentIndex(var); 0574 d->HSplitter->setSizes(sizes); 0575 d->detailsWidget->slotSetActive((d->stackedWidget->currentWidget() == d->detailsWidget) && 0576 (d->splitterSize == 0)); 0577 0578 return true; 0579 } 0580 0581 return QWidget::eventFilter(o, e); 0582 } 0583 0584 void GeolocationEdit::slotCurrentTabChanged(int index) 0585 { 0586 d->tabBar->setCurrentIndex(index); 0587 d->stackedWidget->setCurrentIndex(index); 0588 d->detailsWidget->slotSetActive(d->stackedWidget->currentWidget() == d->detailsWidget); 0589 } 0590 0591 void GeolocationEdit::setCurrentTab(int index) 0592 { 0593 d->tabBar->setCurrentIndex(index); 0594 d->stackedWidget->setCurrentIndex(index); 0595 QList<int> sizes = d->HSplitter->sizes(); 0596 0597 if (d->splitterSize >= 0) 0598 { 0599 sizes[1] = d->splitterSize; 0600 d->splitterSize = 0; 0601 } 0602 0603 d->HSplitter->setSizes(sizes); 0604 d->detailsWidget->slotSetActive((d->stackedWidget->currentWidget() == d->detailsWidget) && 0605 (d->splitterSize == 0)); 0606 } 0607 0608 void GeolocationEdit::setImages(const QList<QUrl>& images) 0609 { 0610 QList<GPSItemContainer*> items; 0611 0612 Q_FOREACH (const QUrl& u, images) 0613 { 0614 items << new GPSItemContainer(u); 0615 } 0616 0617 setItems(items); 0618 } 0619 0620 void GeolocationEdit::setItems(const QList<GPSItemContainer*>& items) 0621 { 0622 Q_FOREACH (GPSItemContainer* const newItem, items) 0623 { 0624 newItem->loadImageData(); 0625 d->imageModel->addItem(newItem); 0626 } 0627 0628 QList<QPersistentModelIndex> imagesToLoad; 0629 0630 for (int i = 0 ; i < d->imageModel->rowCount() ; ++i) 0631 { 0632 imagesToLoad << d->imageModel->index(i, 0); 0633 } 0634 0635 slotSetUIEnabled(false); 0636 slotProgressSetup(imagesToLoad.count(), i18nc("@info", "Loading metadata -")); 0637 0638 // initiate the saving 0639 0640 d->fileIOCountDone = 0; 0641 d->fileIOCountTotal = imagesToLoad.count(); 0642 d->fileIOFutureWatcher = new QFutureWatcher<QPair<QUrl, QString> >(this); 0643 0644 connect(d->fileIOFutureWatcher, SIGNAL(resultsReadyAt(int,int)), 0645 this, SLOT(slotFileMetadataLoaded(int,int))); 0646 0647 d->fileIOFuture = QtConcurrent::mapped(imagesToLoad, LoadFileMetadataHelper(d->imageModel)); 0648 d->fileIOFutureWatcher->setFuture(d->fileIOFuture); 0649 } 0650 0651 void GeolocationEdit::slotFileMetadataLoaded(int beginIndex, int endIndex) 0652 { 0653 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << beginIndex << endIndex; 0654 d->fileIOCountDone += (endIndex-beginIndex); 0655 slotProgressChanged(d->fileIOCountDone); 0656 0657 if (d->fileIOCountDone == d->fileIOCountTotal) 0658 { 0659 slotSetUIEnabled(true); 0660 } 0661 } 0662 0663 void GeolocationEdit::readSettings() 0664 { 0665 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0666 KConfigGroup group = config->group(QLatin1String("Geolocation Edit Settings")); 0667 0668 // -------------------------- 0669 0670 // TODO: sanely determine a default backend 0671 0672 const KConfigGroup groupMapWidget = KConfigGroup(&group, QLatin1String("Map Widget")); 0673 d->mapWidget->readSettingsFromGroup(&groupMapWidget); 0674 0675 const KConfigGroup groupCorrelatorWidget = KConfigGroup(&group, QLatin1String("Correlator Widget")); 0676 d->correlatorWidget->readSettingsFromGroup(&groupCorrelatorWidget); 0677 0678 const KConfigGroup groupTreeView = KConfigGroup(&group, QLatin1String("Tree View")); 0679 d->treeView->readSettingsFromGroup(&groupTreeView); 0680 0681 const KConfigGroup groupSearchResultWidget = KConfigGroup(&group, QLatin1String("Search Widget")); 0682 d->searchWidget->readSettingsFromGroup(&groupSearchResultWidget); 0683 0684 const KConfigGroup groupRGWidget = KConfigGroup(&group, QLatin1String("Reverse Geocoding Widget")); 0685 d->rgWidget->readSettingsFromGroup(&groupRGWidget); 0686 0687 const KConfigGroup groupDialog = KConfigGroup(&group, QLatin1String("Dialog")); 0688 0689 // -------------------------- 0690 0691 setCurrentTab(group.readEntry("Current Tab", 0)); 0692 const bool showOldestFirst = group.readEntry("Show oldest images first", false); 0693 0694 if (showOldestFirst) 0695 { 0696 d->sortActionOldestFirst->setChecked(true); 0697 d->mapWidget->setSortKey(1); 0698 } 0699 else 0700 { 0701 d->sortActionYoungestFirst->setChecked(true); 0702 d->mapWidget->setSortKey(0); 0703 } 0704 0705 d->actionBookmarkVisibility->setChecked(group.readEntry("Bookmarks visible", false)); 0706 slotBookmarkVisibilityToggled(); 0707 0708 if (group.hasKey("SplitterState V1")) 0709 { 0710 const QByteArray splitterState = QByteArray::fromBase64(group.readEntry("SplitterState V1", QByteArray())); 0711 0712 if (!splitterState.isEmpty()) 0713 { 0714 d->VSplitter->restoreState(splitterState); 0715 } 0716 } 0717 0718 if (group.hasKey("SplitterState H1")) 0719 { 0720 const QByteArray splitterState = QByteArray::fromBase64(group.readEntry("SplitterState H1", QByteArray())); 0721 0722 if (!splitterState.isEmpty()) 0723 { 0724 d->HSplitter->restoreState(splitterState); 0725 } 0726 } 0727 0728 d->splitterSize = group.readEntry("Splitter H1 CollapsedSize", 0); 0729 0730 // ---------------------------------- 0731 0732 d->mapLayout = MapLayout(group.readEntry("Map Layout", QVariant::fromValue(int(MapLayoutOne))).value<int>()); 0733 d->cbMapLayout->setCurrentIndex(d->mapLayout); 0734 adjustMapLayout(false); 0735 0736 if (d->mapWidget2) 0737 { 0738 const KConfigGroup groupMapWidget2 = KConfigGroup(&group, QLatin1String("Map Widget 2")); 0739 d->mapWidget2->readSettingsFromGroup(&groupMapWidget2); 0740 0741 d->mapWidget2->setActive(true); 0742 } 0743 } 0744 0745 void GeolocationEdit::saveSettings() 0746 { 0747 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0748 KConfigGroup group = config->group(QLatin1String("Geolocation Edit Settings")); 0749 0750 // -------------------------- 0751 0752 KConfigGroup groupMapWidget = KConfigGroup(&group, QLatin1String("Map Widget")); 0753 d->mapWidget->saveSettingsToGroup(&groupMapWidget); 0754 0755 if (d->mapWidget2) 0756 { 0757 KConfigGroup groupMapWidget2 = KConfigGroup(&group, QLatin1String("Map Widget 2")); 0758 d->mapWidget2->saveSettingsToGroup(&groupMapWidget2); 0759 } 0760 0761 KConfigGroup groupCorrelatorWidget = KConfigGroup(&group, QLatin1String("Correlator Widget")); 0762 d->correlatorWidget->saveSettingsToGroup(&groupCorrelatorWidget); 0763 0764 KConfigGroup groupTreeView = KConfigGroup(&group, QLatin1String("Tree View")); 0765 d->treeView->saveSettingsToGroup(&groupTreeView); 0766 0767 KConfigGroup groupSearchResultWidget = KConfigGroup(&group, QLatin1String("Search Widget")); 0768 d->searchWidget->saveSettingsToGroup(&groupSearchResultWidget); 0769 0770 KConfigGroup groupRGWidget = KConfigGroup(&group, QLatin1String("Reverse Geocoding Widget")); 0771 d->rgWidget->saveSettingsToGroup(&groupRGWidget); 0772 0773 // -------------------------- 0774 0775 group.writeEntry("Current Tab", d->tabBar->currentIndex()); 0776 group.writeEntry("Show oldest images first", d->sortActionOldestFirst->isChecked()); 0777 group.writeEntry("SplitterState V1", d->VSplitter->saveState().toBase64()); 0778 group.writeEntry("SplitterState H1", d->HSplitter->saveState().toBase64()); 0779 group.writeEntry("Splitter H1 CollapsedSize", d->splitterSize); 0780 group.writeEntry("Map Layout", QVariant::fromValue(int(d->mapLayout))); 0781 group.writeEntry("Bookmarks visible", d->actionBookmarkVisibility->isChecked()); 0782 0783 config->sync(); 0784 } 0785 0786 void GeolocationEdit::reject() 0787 { 0788 close(); 0789 } 0790 0791 void GeolocationEdit::closeEvent(QCloseEvent *e) 0792 { 0793 if (!e) 0794 { 0795 return; 0796 } 0797 0798 // is the UI locked? 0799 0800 if (!d->uiEnabled) 0801 { 0802 // please wait until we are done ... 0803 0804 e->ignore(); 0805 0806 return; 0807 } 0808 0809 // are there any modified images? 0810 0811 int dirtyImagesCount = 0; 0812 0813 for (int i = 0 ; i < d->imageModel->rowCount() ; ++i) 0814 { 0815 const QModelIndex itemIndex = d->imageModel->index(i, 0); 0816 GPSItemContainer* const item = d->imageModel->itemFromIndex(itemIndex); 0817 0818 if (item->isDirty() || item->isTagListDirty()) 0819 { 0820 ++dirtyImagesCount; 0821 } 0822 } 0823 0824 if (dirtyImagesCount > 0) 0825 { 0826 const QString message = i18ncp("@info", 0827 "You have 1 modified image.", 0828 "You have %1 modified images.", 0829 dirtyImagesCount 0830 ); 0831 0832 const int chosenAction = DMessageBox::showYesNo(QMessageBox::Warning, 0833 this, 0834 i18nc("@title:window", "Unsaved Changes"), 0835 i18nc("@info", "%1 Would you like to save the changes you made to them?", message) 0836 ); 0837 0838 if (chosenAction == QMessageBox::No) 0839 { 0840 saveSettings(); 0841 e->accept(); 0842 return; 0843 } 0844 0845 if (chosenAction == QMessageBox::Yes) 0846 { 0847 // the user wants to save his changes. 0848 // this will initiate the saving process and then close the dialog. 0849 0850 saveChanges(true); 0851 } 0852 0853 // do not close the dialog for now 0854 0855 e->ignore(); 0856 return; 0857 } 0858 0859 saveSettings(); 0860 e->accept(); 0861 } 0862 0863 void GeolocationEdit::slotImageActivated(const QModelIndex& index) 0864 { 0865 d->detailsWidget->slotSetCurrentImage(index); 0866 0867 if (!index.isValid()) 0868 { 0869 return; 0870 } 0871 0872 GPSItemContainer* const item = d->imageModel->itemFromIndex(index); 0873 0874 if (!item) 0875 { 0876 return; 0877 } 0878 0879 const GeoCoordinates imageCoordinates = item->coordinates(); 0880 0881 if (imageCoordinates.hasCoordinates()) 0882 { 0883 d->mapWidget->setCenter(imageCoordinates); 0884 } 0885 } 0886 0887 void GeolocationEdit::slotSetUIEnabled(const bool enabledState, 0888 QObject* const cancelObject, 0889 const QString& cancelSlot) 0890 { 0891 if (enabledState) 0892 { 0893 // hide the progress bar 0894 0895 d->progressBar->setVisible(false); 0896 d->progressCancelButton->setVisible(false); 0897 d->progressBar->setProgressValue(d->progressBar->progressTotalSteps()); 0898 } 0899 0900 /// TODO: disable the worldmapwidget and the images list (at least disable editing operations) 0901 0902 d->progressCancelObject = cancelObject; 0903 d->progressCancelSlot = cancelSlot; 0904 d->uiEnabled = enabledState; 0905 m_buttons->setEnabled(enabledState); 0906 d->correlatorWidget->setUIEnabledExternal(enabledState); 0907 d->detailsWidget->setUIEnabledExternal(enabledState); 0908 d->rgWidget->setUIEnabled(enabledState); 0909 d->treeView->setEditEnabled(enabledState); 0910 d->listViewContextMenu->setEnabled(enabledState); 0911 d->mapWidget->setAllowModifications(enabledState); 0912 } 0913 0914 void GeolocationEdit::slotSetUIEnabled(const bool enabledState) 0915 { 0916 slotSetUIEnabled(enabledState, nullptr, QString()); 0917 } 0918 0919 void GeolocationEdit::saveChanges(const bool closeAfterwards) 0920 { 0921 /** 0922 * TODO: actually save the changes are there any modified images? 0923 */ 0924 0925 QList<QPersistentModelIndex> dirtyImages; 0926 0927 for (int i = 0 ; i < d->imageModel->rowCount() ; ++i) 0928 { 0929 const QModelIndex itemIndex = d->imageModel->index(i, 0); 0930 GPSItemContainer* const item = d->imageModel->itemFromIndex(itemIndex); 0931 0932 if (item->isDirty() || item->isTagListDirty()) 0933 { 0934 dirtyImages << itemIndex; 0935 } 0936 } 0937 0938 if (dirtyImages.isEmpty()) 0939 { 0940 if (closeAfterwards) 0941 { 0942 close(); 0943 } 0944 0945 return; 0946 } 0947 0948 /// TODO: disable the UI and provide progress and cancel information 0949 0950 slotSetUIEnabled(false); 0951 slotProgressSetup(dirtyImages.count(), i18nc("@info", "Saving changes -")); 0952 0953 // initiate the saving 0954 0955 d->fileIOCountDone = 0; 0956 d->fileIOCountTotal = dirtyImages.count(); 0957 d->fileIOCloseAfterSaving = closeAfterwards; 0958 d->fileIOFutureWatcher = new QFutureWatcher<QPair<QUrl, QString> >(this); 0959 0960 connect(d->fileIOFutureWatcher, SIGNAL(resultsReadyAt(int,int)), 0961 this, SLOT(slotFileChangesSaved(int,int))); 0962 0963 d->fileIOFuture = QtConcurrent::mapped(dirtyImages, SaveChangedImagesHelper(d->imageModel)); 0964 d->fileIOFutureWatcher->setFuture(d->fileIOFuture); 0965 } 0966 0967 void GeolocationEdit::slotFileChangesSaved(int beginIndex, int endIndex) 0968 { 0969 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << beginIndex << endIndex; 0970 0971 d->fileIOCountDone += (endIndex-beginIndex); 0972 slotProgressChanged(d->fileIOCountDone); 0973 0974 if (d->fileIOCountDone == d->fileIOCountTotal) 0975 { 0976 slotSetUIEnabled(true); 0977 0978 // any errors? 0979 0980 QList<QPair<QUrl, QString> > errorList; 0981 0982 for (int i = 0 ; i < d->fileIOFuture.resultCount() ; ++i) 0983 { 0984 if (!d->fileIOFuture.resultAt(i).second.isEmpty()) 0985 { 0986 errorList << d->fileIOFuture.resultAt(i); 0987 } 0988 0989 if (!d->iface->supportAlbums()) 0990 { 0991 // To rescan item metadata from host. 0992 0993 Q_EMIT signalMetadataChangedForUrl(d->fileIOFuture.resultAt(i).first); 0994 } 0995 } 0996 0997 if (!errorList.isEmpty()) 0998 { 0999 QStringList errorStrings; 1000 1001 for (int i = 0 ; i < errorList.count() ; ++i) 1002 { 1003 errorStrings << QString::fromLatin1("%1: %2") 1004 .arg(errorList.at(i).first.toLocalFile()) 1005 .arg(errorList.at(i).second); 1006 } 1007 1008 DMessageBox::showInformationList(QMessageBox::Critical, 1009 this, 1010 i18nc("@title:window", "Error"), 1011 i18nc("@info", "Failed to save some information:"), 1012 errorStrings); 1013 } 1014 1015 // done saving files 1016 1017 if (d->fileIOCloseAfterSaving) 1018 { 1019 close(); 1020 } 1021 } 1022 } 1023 1024 void GeolocationEdit::slotOkClicked() 1025 { 1026 // save the changes, and close afterwards 1027 1028 saveChanges(true); 1029 } 1030 1031 void GeolocationEdit::slotApplyClicked() 1032 { 1033 // save the changes, but do not close afterwards 1034 1035 saveChanges(false); 1036 } 1037 1038 void GeolocationEdit::slotProgressChanged(const int currentProgress) 1039 { 1040 d->progressBar->setProgressValue(currentProgress); 1041 } 1042 1043 void GeolocationEdit::slotProgressSetup(const int maxProgress, const QString& progressText) 1044 { 1045 d->progressBar->setProgressText(progressText); 1046 d->progressBar->setProgressTotalSteps(maxProgress); 1047 d->progressBar->setProgressValue(0); 1048 d->progressBar->setNotify(true); 1049 d->progressBar->setNotificationTitle(i18nc("@title", "Edit Geolocation"), QIcon::fromTheme(QLatin1String("globe"))); 1050 d->progressBar->setVisible(true); 1051 d->progressCancelButton->setVisible(d->progressCancelObject != nullptr); 1052 } 1053 1054 void GeolocationEdit::slotGPSUndoCommand(GPSUndoCommand* undoCommand) 1055 { 1056 d->undoStack->push(undoCommand); 1057 } 1058 1059 void GeolocationEdit::slotSortOptionTriggered(QAction* /*sortAction*/) 1060 { 1061 int newSortKey = 0; 1062 1063 if (d->sortActionOldestFirst->isChecked()) 1064 { 1065 newSortKey |= 1; 1066 } 1067 1068 d->mapWidget->setSortKey(newSortKey); 1069 } 1070 1071 void GeolocationEdit::slotProgressCancelButtonClicked() 1072 { 1073 if (d->progressCancelObject) 1074 { 1075 QTimer::singleShot(0, d->progressCancelObject, d->progressCancelSlot.toUtf8().constData()); 1076 1077 d->progressBar->setProgressValue(d->progressBar->progressTotalSteps()); 1078 } 1079 } 1080 1081 void GeolocationEdit::slotBookmarkVisibilityToggled() 1082 { 1083 d->bookmarkOwner->bookmarkModelHelper()->setVisible(d->actionBookmarkVisibility->isChecked()); 1084 } 1085 1086 void GeolocationEdit::slotLayoutChanged(int lay) 1087 { 1088 d->mapLayout = (MapLayout)lay; 1089 adjustMapLayout(true); 1090 } 1091 1092 void GeolocationEdit::slotTrackListChanged(const GeoCoordinates& coordinate) 1093 { 1094 d->mapWidget->setCenter(coordinate); 1095 } 1096 1097 MapWidget* GeolocationEdit::makeMapWidget(QWidget** const pvbox) 1098 { 1099 QWidget* const dummyWidget = new QWidget(this); 1100 QVBoxLayout* const vbox = new QVBoxLayout(dummyWidget); 1101 MapWidget* const mapWidget = new MapWidget(dummyWidget); 1102 mapWidget->setAvailableMouseModes(MouseModePan | MouseModeZoomIntoGroup | MouseModeSelectThumbnail); 1103 mapWidget->setVisibleMouseModes(MouseModePan | MouseModeZoomIntoGroup | MouseModeSelectThumbnail); 1104 mapWidget->setMouseMode(MouseModeSelectThumbnail); 1105 mapWidget->setGroupedModel(d->geoifaceMarkerModel); 1106 mapWidget->setDragDropHandler(d->mapDragDropHandler); 1107 mapWidget->addUngroupedModel(d->bookmarkOwner->bookmarkModelHelper()); 1108 mapWidget->addUngroupedModel(d->searchWidget->getModelHelper()); 1109 mapWidget->setTrackManager(d->trackManager); 1110 mapWidget->setSortOptionsMenu(d->sortMenu); 1111 1112 vbox->addWidget(mapWidget); 1113 vbox->addWidget(mapWidget->getControlWidget()); 1114 1115 QToolButton* const bookmarkVisibilityButton = new QToolButton(mapWidget); 1116 bookmarkVisibilityButton->setDefaultAction(d->actionBookmarkVisibility); 1117 mapWidget->addWidgetToControlWidget(bookmarkVisibilityButton); 1118 1119 *pvbox = dummyWidget; 1120 1121 return mapWidget; 1122 } 1123 1124 void GeolocationEdit::adjustMapLayout(const bool syncSettings) 1125 { 1126 if (d->mapLayout == MapLayoutOne) 1127 { 1128 if (d->mapSplitter->count() > 1) 1129 { 1130 delete d->mapSplitter->widget(1); 1131 d->mapWidget2 = nullptr; 1132 } 1133 } 1134 else 1135 { 1136 if (d->mapSplitter->count() == 1) 1137 { 1138 QWidget* mapHolder = nullptr; 1139 d->mapWidget2 = makeMapWidget(&mapHolder); 1140 d->mapSplitter->addWidget(mapHolder); 1141 1142 if (syncSettings) 1143 { 1144 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 1145 KConfigGroup group = config->group(QLatin1String("Geolocation Edit Settings")); 1146 const KConfigGroup groupMapWidget = KConfigGroup(&group, QLatin1String("Map Widget")); 1147 d->mapWidget2->readSettingsFromGroup(&groupMapWidget); 1148 d->mapWidget2->setActive(true); 1149 } 1150 } 1151 1152 if (d->mapLayout == MapLayoutHorizontal) 1153 { 1154 d->mapSplitter->setOrientation(Qt::Horizontal); 1155 } 1156 else 1157 { 1158 d->mapSplitter->setOrientation(Qt::Vertical); 1159 } 1160 } 1161 } 1162 1163 } // namespace DigikamGenericGeolocationEditPlugin 1164 1165 #include "moc_geolocationedit.cpp"