File indexing completed on 2025-01-05 03:58:31

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-03-22
0007  * Description : A view to display a list of items with GPS info.
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010      by Michael G. Hansen <mike at mghansen dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "gpsitemlist.h"
0017 
0018 // Qt includes
0019 
0020 #include <QDrag>
0021 #include <QHeaderView>
0022 #include <QWheelEvent>
0023 #include <QMenu>
0024 #include <QAction>
0025 
0026 // KDE includes
0027 
0028 #include <kconfiggroup.h>
0029 
0030 // Local includes
0031 
0032 #include "digikam_debug.h"
0033 #include "gpsitemdelegate.h"
0034 #include "gpsitemlistdragdrophandler.h"
0035 
0036 namespace Digikam
0037 {
0038 
0039 class Q_DECL_HIDDEN GPSItemList::Private
0040 {
0041 public:
0042 
0043     explicit Private()
0044       : editEnabled         (true),
0045         dragEnabled         (false),
0046         model               (nullptr),
0047         selectionModel      (nullptr),
0048         itemDelegate        (nullptr),
0049         imageSortProxyModel (nullptr),
0050         dragDropHandler     (nullptr)
0051     {
0052     }
0053 
0054     bool                     editEnabled;
0055     bool                     dragEnabled;
0056     GPSItemModel*            model;
0057     QItemSelectionModel*     selectionModel;
0058     GPSItemDelegate*         itemDelegate;
0059     GPSItemSortProxyModel*   imageSortProxyModel;
0060     ItemListDragDropHandler* dragDropHandler;
0061 };
0062 
0063 GPSItemList::GPSItemList(QWidget* const parent)
0064     : QTreeView(parent),
0065       d(new Private())
0066 {
0067     header()->setSectionsMovable(true);
0068     setAlternatingRowColors(true);
0069     setUniformRowHeights(true);
0070     setRootIsDecorated(false);
0071 
0072     d->itemDelegate = new GPSItemDelegate(this, this);
0073     setItemDelegate(d->itemDelegate);
0074     setThumbnailSize(60);
0075     slotUpdateActionsEnabled();
0076 
0077     header()->installEventFilter(this);
0078 }
0079 
0080 GPSItemList::~GPSItemList()
0081 {
0082     delete d;
0083 }
0084 
0085 void GPSItemList::startDrag(Qt::DropActions supportedActions)
0086 {
0087     if (!d->dragDropHandler)
0088     {
0089         QTreeView::startDrag(supportedActions);
0090         return;
0091     }
0092 
0093     // NOTE: read the selected indices from the source selection model, not our selection model,
0094     // which is for the sorted model!
0095 
0096     const QList<QModelIndex> selectedIndicesFromModel = d->selectionModel->selectedIndexes();
0097     QList<QPersistentModelIndex> selectedIndices;
0098 
0099     for (int i = 0 ; i < selectedIndicesFromModel.count() ; ++i)
0100     {
0101         selectedIndices << selectedIndicesFromModel.at(i);
0102     }
0103 
0104     QMimeData* const dragMimeData = d->dragDropHandler->createMimeData(selectedIndices);
0105 
0106     if (!dragMimeData)
0107     {
0108         return;
0109     }
0110 
0111     QDrag* const drag = new QDrag(this);
0112     drag->setMimeData(dragMimeData);
0113     drag->exec(Qt::CopyAction);
0114 }
0115 
0116 void GPSItemList::setModelAndSelectionModel(GPSItemModel* const model, QItemSelectionModel* const selectionModel)
0117 {
0118     d->model               = model;
0119     d->selectionModel      = selectionModel;
0120     d->imageSortProxyModel = new GPSItemSortProxyModel(d->model, d->selectionModel);
0121     setModel(d->imageSortProxyModel);
0122 
0123     connect(d->model, SIGNAL(signalThumbnailForIndexAvailable(QPersistentModelIndex,QPixmap)),
0124             this, SLOT(slotThumbnailFromModel(QPersistentModelIndex,QPixmap)));
0125 
0126     connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
0127             this, SLOT(slotInternalTreeViewImageActivated(QModelIndex,QModelIndex)));
0128 
0129     if (d->imageSortProxyModel->mappedSelectionModel())
0130     {
0131         setSelectionModel(d->imageSortProxyModel->mappedSelectionModel());
0132     }
0133 }
0134 
0135 void GPSItemList::setDragDropHandler(ItemListDragDropHandler* const dragDropHandler)
0136 {
0137     d->dragDropHandler = dragDropHandler;
0138 }
0139 
0140 GPSItemModel* GPSItemList::getModel() const
0141 {
0142     return d->model;
0143 }
0144 
0145 void GPSItemList::setThumbnailSize(const int size)
0146 {
0147     d->itemDelegate->setThumbnailSize(size);
0148     setColumnWidth(GPSItemContainer::ColumnThumbnail, size);
0149 }
0150 
0151 void GPSItemList::slotIncreaseThumbnailSize()
0152 {
0153     // TODO: pick reasonable limits and make sure we stay on multiples of 5
0154 
0155     const int currentThumbnailSize = d->itemDelegate->getThumbnailSize();
0156 
0157     if (currentThumbnailSize < 200)
0158     {
0159         setThumbnailSize(currentThumbnailSize + 5);
0160     }
0161 }
0162 
0163 void GPSItemList::slotDecreaseThumbnailSize()
0164 {
0165     const int currentThumbnailSize = d->itemDelegate->getThumbnailSize();
0166 
0167     if (currentThumbnailSize > 30)
0168     {
0169         setThumbnailSize(currentThumbnailSize - 5);
0170     }
0171 }
0172 
0173 void GPSItemList::wheelEvent(QWheelEvent* we)
0174 {
0175     if ((we->modifiers() & Qt::ControlModifier) == 0)
0176     {
0177         QTreeView::wheelEvent(we);
0178         return;
0179     }
0180 
0181     we->accept();
0182 
0183     if      (we->angleDelta().y() > 0)
0184     {
0185         slotIncreaseThumbnailSize();
0186     }
0187     else if (we->angleDelta().y() < 0)
0188     {
0189         slotDecreaseThumbnailSize();
0190     }
0191 }
0192 
0193 void GPSItemList::slotThumbnailFromModel(const QPersistentModelIndex& index, const QPixmap& /*pixmap*/)
0194 {
0195     // TODO: verify that the size corresponds to the size of our thumbnails!
0196 
0197     update(d->imageSortProxyModel->mapFromSource(index));
0198 }
0199 
0200 void GPSItemList::saveSettingsToGroup(KConfigGroup* const group)
0201 {
0202     group->writeEntry("Image List Thumbnail Size", d->itemDelegate->getThumbnailSize());
0203     group->writeEntry("Header State",              header()->saveState());
0204 }
0205 
0206 void GPSItemList::readSettingsFromGroup(const KConfigGroup* const group)
0207 {
0208     setThumbnailSize(group->readEntry("Image List Thumbnail Size", 60));
0209 
0210     const QByteArray headerState = group->readEntry("Header State", QByteArray());
0211 
0212     if (!headerState.isEmpty())
0213     {
0214         header()->restoreState(headerState);
0215     }
0216     else
0217     {
0218         // by default, hide the advanced columns:
0219 
0220         header()->setSectionHidden(GPSItemContainer::ColumnDOP,         true);
0221         header()->setSectionHidden(GPSItemContainer::ColumnFixType,     true);
0222         header()->setSectionHidden(GPSItemContainer::ColumnNSatellites, true);
0223     }
0224 }
0225 
0226 QItemSelectionModel* GPSItemList::getSelectionModel() const
0227 {
0228     return d->selectionModel;
0229 }
0230 
0231 void GPSItemList::slotInternalTreeViewImageActivated(const QModelIndex& current,
0232                                                      const QModelIndex& /*previous*/)
0233 {
0234     // qCDebug(DIGIKAM_GENERAL_LOG) << current;
0235 
0236     Q_EMIT signalImageActivated(current);
0237 }
0238 
0239 GPSItemSortProxyModel* GPSItemList::getSortProxyModel() const
0240 {
0241     return d->imageSortProxyModel;
0242 }
0243 
0244 void GPSItemList::setEditEnabled(const bool state)
0245 {
0246     d->editEnabled = state;
0247     slotUpdateActionsEnabled();
0248 }
0249 
0250 void GPSItemList::setDragEnabled(const bool state)
0251 {
0252     d->dragEnabled = state;
0253     slotUpdateActionsEnabled();
0254 }
0255 
0256 void GPSItemList::slotUpdateActionsEnabled()
0257 {
0258     QTreeView::setDragEnabled(d->dragEnabled && d->editEnabled);
0259 
0260     if (d->dragEnabled && d->editEnabled)
0261     {
0262         QTreeView::setDragDropMode(QAbstractItemView::DragOnly);
0263     }
0264 }
0265 
0266 bool GPSItemList::eventFilter(QObject* watched, QEvent* event)
0267 {
0268     QHeaderView* const headerView = header();
0269 
0270     if (!d->model || (watched != headerView) || (event->type() != QEvent::ContextMenu))
0271     {
0272         return QWidget::eventFilter(watched, event);
0273     }
0274 
0275     QMenu* const menu               = new QMenu(this);
0276 
0277     // add action for all the columns
0278 
0279     for (int i = 0 ; i < d->model->columnCount() ; ++i)
0280     {
0281         const QString columnName    = d->model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
0282         const bool isVisible        = !headerView->isSectionHidden(i);
0283 
0284         QAction* const columnAction = new QAction(columnName, menu);
0285         columnAction->setCheckable(true);
0286         columnAction->setChecked(isVisible);
0287         columnAction->setData(i);
0288 
0289         menu->addAction(columnAction);
0290     }
0291 
0292     connect(menu, SIGNAL(triggered(QAction*)),
0293             this, SLOT(slotColumnVisibilityActionTriggered(QAction*)));
0294 
0295     QContextMenuEvent* const e = static_cast<QContextMenuEvent*>(event);
0296     menu->exec(e->globalPos());
0297 
0298     return true;
0299 }
0300 
0301 void GPSItemList::slotColumnVisibilityActionTriggered(QAction* action)
0302 {
0303     const int columnNumber     = action->data().toInt();
0304     const bool columnIsVisible = action->isChecked();
0305 
0306     header()->setSectionHidden(columnNumber, !columnIsVisible);
0307 }
0308 
0309 } // namespace Digikam
0310 
0311 #include "moc_gpsitemlist.cpp"