File indexing completed on 2025-01-19 03:50:50
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2013-03-02 0007 * Description : Table view: Tree view subelement 0008 * 0009 * SPDX-FileCopyrightText: 2017-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2013 by Michael G. Hansen <mike at mghansen dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "tableview_treeview.h" 0017 0018 // Qt includes 0019 0020 #include <QContextMenuEvent> 0021 #include <QHeaderView> 0022 #include <QMenu> 0023 #include <QAction> 0024 0025 // KDE includes 0026 0027 #include <klocalizedstring.h> 0028 0029 // Local includes 0030 0031 #include "digikam_debug.h" 0032 #include "contextmenuhelper.h" 0033 #include "iteminfo.h" 0034 #include "itemmodel.h" 0035 #include "tableview_column_configuration_dialog.h" 0036 #include "tableview_model.h" 0037 #include "tableview_selection_model_syncer.h" 0038 #include "tableview_treeview_delegate.h" 0039 #include "thumbnailsize.h" 0040 0041 namespace Digikam 0042 { 0043 0044 class Q_DECL_HIDDEN TableViewTreeView::Private 0045 { 0046 public: 0047 0048 explicit Private() 0049 : headerContextMenuActiveColumn (-1), 0050 actionHeaderContextMenuRemoveColumn (nullptr), 0051 actionHeaderContextMenuConfigureColumn(nullptr), 0052 dragDropThumbnailSize () 0053 { 0054 } 0055 0056 public: 0057 0058 int headerContextMenuActiveColumn; 0059 QAction* actionHeaderContextMenuRemoveColumn; 0060 QAction* actionHeaderContextMenuConfigureColumn; 0061 ThumbnailSize dragDropThumbnailSize; 0062 }; 0063 0064 TableViewTreeView::TableViewTreeView(TableViewShared* const tableViewShared, QWidget* const parent) 0065 : QTreeView(parent), 0066 d (new Private()), 0067 s (tableViewShared) 0068 { 0069 setModel(s->tableViewModel); 0070 setSelectionModel(s->tableViewSelectionModel); 0071 0072 s->itemDelegate = new TableViewItemDelegate(s, this); 0073 setSelectionMode(QAbstractItemView::ExtendedSelection); 0074 setItemDelegate(s->itemDelegate); 0075 setAlternatingRowColors(true); 0076 setAllColumnsShowFocus(true); 0077 setUniformRowHeights(true); 0078 setSortingEnabled(true); 0079 setDragEnabled(true); 0080 setAcceptDrops(true); 0081 setWordWrap(true); 0082 /* 0083 viewport()->setAcceptDrops(true); 0084 */ 0085 d->actionHeaderContextMenuRemoveColumn = new QAction(QIcon::fromTheme(QLatin1String("edit-table-delete-column")), i18n("Remove this column"), this); 0086 0087 connect(d->actionHeaderContextMenuRemoveColumn, SIGNAL(triggered(bool)), 0088 this, SLOT(slotHeaderContextMenuActionRemoveColumnTriggered())); 0089 0090 d->actionHeaderContextMenuConfigureColumn = new QAction(QIcon::fromTheme(QLatin1String("configure")), i18n("Configure this column"), this); 0091 0092 connect(d->actionHeaderContextMenuConfigureColumn, SIGNAL(triggered(bool)), 0093 this, SLOT(slotHeaderContextMenuConfigureColumn())); 0094 0095 header()->installEventFilter(this); 0096 0097 slotModelGroupingModeChanged(); 0098 0099 connect(s->tableViewModel, SIGNAL(signalGroupingModeChanged()), 0100 this, SLOT(slotModelGroupingModeChanged())); 0101 } 0102 0103 TableViewTreeView::~TableViewTreeView() 0104 { 0105 } 0106 0107 bool TableViewTreeView::eventFilter(QObject* watched, QEvent* event) 0108 { 0109 QHeaderView* const headerView = header(); 0110 0111 if ((watched == headerView) && (event->type() == QEvent::ContextMenu)) 0112 { 0113 showHeaderContextMenu(event); 0114 0115 return true; 0116 } 0117 0118 return QObject::eventFilter(watched, event); 0119 } 0120 0121 void TableViewTreeView::addColumnDescriptionsToMenu(const QList<TableViewColumnDescription>& columnDescriptions, QMenu* const menu) 0122 { 0123 for (int i = 0 ; i < columnDescriptions.count() ; ++i) 0124 { 0125 const TableViewColumnDescription& desc = columnDescriptions.at(i); 0126 QAction* const action = new QAction(desc.columnTitle, menu); 0127 0128 if (!desc.columnIcon.isEmpty()) 0129 { 0130 action->setIcon(QIcon::fromTheme(desc.columnIcon)); 0131 } 0132 0133 if (desc.subColumns.isEmpty()) 0134 { 0135 connect(action, SIGNAL(triggered(bool)), 0136 this, SLOT(slotHeaderContextMenuAddColumn())); 0137 0138 action->setData(QVariant::fromValue<TableViewColumnDescription>(desc)); 0139 } 0140 else 0141 { 0142 QMenu* const subMenu = new QMenu(menu); 0143 addColumnDescriptionsToMenu(desc.subColumns, subMenu); 0144 0145 action->setMenu(subMenu); 0146 } 0147 0148 menu->addAction(action); 0149 } 0150 } 0151 0152 void TableViewTreeView::showHeaderContextMenu(QEvent* const event) 0153 { 0154 QContextMenuEvent* const e = static_cast<QContextMenuEvent*>(event); 0155 QHeaderView* const headerView = header(); 0156 d->headerContextMenuActiveColumn = headerView->logicalIndexAt(e->pos()); 0157 const TableViewColumn* const columnObject = s->tableViewModel->getColumnObject(d->headerContextMenuActiveColumn); 0158 QMenu* const menu = new QMenu(this); 0159 0160 d->actionHeaderContextMenuRemoveColumn->setEnabled(s->tableViewModel->columnCount(QModelIndex())>1); 0161 menu->addAction(d->actionHeaderContextMenuRemoveColumn); 0162 const bool columnCanConfigure = columnObject->getColumnFlags().testFlag(TableViewColumn::ColumnHasConfigurationWidget); 0163 d->actionHeaderContextMenuConfigureColumn->setEnabled(columnCanConfigure); 0164 menu->addAction(d->actionHeaderContextMenuConfigureColumn); 0165 menu->addSeparator(); 0166 0167 // add actions for all columns 0168 0169 QList<TableViewColumnDescription> columnDescriptions = s->columnFactory->getColumnDescriptionList(); 0170 addColumnDescriptionsToMenu(columnDescriptions, menu); 0171 0172 menu->exec(e->globalPos()); 0173 } 0174 0175 void TableViewTreeView::slotHeaderContextMenuAddColumn() 0176 { 0177 QAction* const triggeredAction = qobject_cast<QAction*>(sender()); 0178 0179 const QVariant actionData = triggeredAction->data(); 0180 0181 if (!actionData.canConvert<TableViewColumnDescription>()) 0182 { 0183 return; 0184 } 0185 0186 const TableViewColumnDescription desc = actionData.value<TableViewColumnDescription>(); 0187 qCDebug(DIGIKAM_GENERAL_LOG) << "clicked: " << desc.columnTitle; 0188 const int newColumnLogicalIndex = d->headerContextMenuActiveColumn+1; 0189 s->tableViewModel->addColumnAt(desc, newColumnLogicalIndex); 0190 0191 // since the header column order is not the same as the model's column order, we need 0192 // to make sure the new column is moved directly behind the current column in the header: 0193 0194 const int clickedVisualIndex = header()->visualIndex(d->headerContextMenuActiveColumn); 0195 const int newColumnVisualIndex = header()->visualIndex(newColumnLogicalIndex); 0196 int newColumnVisualTargetIndex = clickedVisualIndex + 1; 0197 0198 // If the column is inserted before the clicked column, we have to 0199 // subtract one from the target index because it looks like QHeaderView first removes 0200 // the column and then inserts it. 0201 0202 if (newColumnVisualIndex < clickedVisualIndex) 0203 { 0204 newColumnVisualTargetIndex--; 0205 } 0206 0207 if (newColumnVisualIndex!=newColumnVisualTargetIndex) 0208 { 0209 header()->moveSection(newColumnVisualIndex, newColumnVisualTargetIndex); 0210 } 0211 0212 // Ensure that the newly created column is visible. 0213 // This is especially important if the new column is the last one, 0214 // because then it can be outside of the viewport. 0215 0216 const QModelIndex topIndex = indexAt(QPoint(0, 0)); 0217 const QModelIndex targetIndex = s->tableViewModel->index(topIndex.row(), newColumnLogicalIndex, topIndex.parent()); 0218 scrollTo(targetIndex, EnsureVisible); 0219 } 0220 0221 void TableViewTreeView::slotHeaderContextMenuActionRemoveColumnTriggered() 0222 { 0223 qCDebug(DIGIKAM_GENERAL_LOG) << "remove column " << d->headerContextMenuActiveColumn; 0224 s->tableViewModel->removeColumnAt(d->headerContextMenuActiveColumn); 0225 } 0226 0227 void TableViewTreeView::slotHeaderContextMenuConfigureColumn() 0228 { 0229 TableViewConfigurationDialog* const configurationDialog = new TableViewConfigurationDialog(s, d->headerContextMenuActiveColumn, this); 0230 const int result = configurationDialog->exec(); 0231 0232 if (result!=QDialog::Accepted) 0233 { 0234 return; 0235 } 0236 0237 const TableViewColumnConfiguration newConfiguration = configurationDialog->getNewConfiguration(); 0238 s->tableViewModel->getColumnObject(d->headerContextMenuActiveColumn)->setConfiguration(newConfiguration); 0239 } 0240 0241 AbstractItemDragDropHandler* TableViewTreeView::dragDropHandler() const 0242 { 0243 qCDebug(DIGIKAM_GENERAL_LOG)<<s->imageModel->dragDropHandler(); 0244 0245 return s->imageModel->dragDropHandler(); 0246 } 0247 0248 QModelIndex TableViewTreeView::mapIndexForDragDrop(const QModelIndex& index) const 0249 { 0250 // "index" is a TableViewModel index. 0251 // We are using the drag-drop-handler of ItemModel, thus 0252 // we have to convert it to an index of ItemModel. 0253 0254 // map to ItemModel 0255 0256 const QModelIndex imageModelIndex = s->tableViewModel->toItemModelIndex(index); 0257 0258 return imageModelIndex; 0259 } 0260 0261 QPixmap TableViewTreeView::pixmapForDrag(const QList< QModelIndex >& indexes) const 0262 { 0263 const QModelIndex& firstIndex = indexes.at(0); 0264 const ItemInfo info = s->tableViewModel->imageInfo(firstIndex); 0265 const QString path = info.filePath(); 0266 0267 QPixmap thumbnailPixmap; 0268 0269 /// @todo The first thumbnail load always fails. We have to add thumbnail pre-generation 0270 /// like in ItemModel. Getting thumbnails from ItemModel does not help, because it 0271 /// does not necessarily prepare them the same way. 0272 /// @todo Make a central drag-drop thumbnail generator? 0273 0274 if (!s->thumbnailLoadThread->find(info.thumbnailIdentifier(), thumbnailPixmap, d->dragDropThumbnailSize.size())) 0275 { 0276 /// @todo better default pixmap? 0277 0278 thumbnailPixmap.fill(); 0279 } 0280 0281 /// @todo Decorate the pixmap like the other drag-drop implementations? 0282 /// @todo Write number of images onto the pixmap 0283 0284 return thumbnailPixmap; 0285 /* 0286 const QModelIndex& firstIndex = indexes.at(0); 0287 const QModelIndex& imageModelIndex = s->sortModel->toItemModelIndex(firstIndex); 0288 ItemModel* const imageModel = s->imageFilterModel->sourceItemModel(); 0289 0290 /// @todo Determine how other views choose the size 0291 0292 const QSize thumbnailSize(60, 60); 0293 0294 imageModel->setData(imageModelIndex, qMax(thumbnailSize.width(), thumbnailSize.height()), ItemModel::ThumbnailRole); 0295 QVariant thumbnailData = imageModel->data(imageModelIndex, ItemModel::ThumbnailRole); 0296 imageModel->setData(imageModelIndex, QVariant(), ItemModel::ThumbnailRole); 0297 0298 QPixmap thumbnailPixmap = thumbnailData.value<QPixmap>(); 0299 0300 /// @todo Write number of images onto the pixmap 0301 0302 return thumbnailPixmap; 0303 */ 0304 } 0305 0306 Album* TableViewTreeView::albumAt(const QPoint& pos) const 0307 { 0308 Q_UNUSED(pos) 0309 0310 ItemAlbumModel* const albumModel = qobject_cast<ItemAlbumModel*>(s->imageModel); 0311 0312 if (albumModel) 0313 { 0314 if (!(albumModel->currentAlbums().isEmpty())) 0315 { 0316 return albumModel->currentAlbums().constFirst(); 0317 } 0318 } 0319 0320 return nullptr; 0321 } 0322 0323 void TableViewTreeView::wheelEvent(QWheelEvent* event) 0324 { 0325 if (event->modifiers() & Qt::ControlModifier) 0326 { 0327 const int delta = event->angleDelta().y(); 0328 0329 if (delta > 0) 0330 { 0331 Q_EMIT signalZoomInStep(); 0332 } 0333 else if (delta < 0) 0334 { 0335 Q_EMIT signalZoomOutStep(); 0336 } 0337 0338 event->accept(); 0339 0340 return; 0341 } 0342 0343 QTreeView::wheelEvent(event); 0344 } 0345 0346 bool TableViewTreeView::hasHiddenGroupedImages(const ItemInfo& info) const 0347 { 0348 return ( 0349 info.hasGroupedImages() && 0350 ( 0351 (s->tableViewModel->groupingMode() == s->tableViewModel->GroupingMode::GroupingHideGrouped) || 0352 ((s->tableViewModel->groupingMode() == s->tableViewModel->GroupingMode::GroupingShowSubItems) && 0353 (!s->treeView->isExpanded(s->tableViewModel->indexFromImageId(info.id(), 0))) 0354 ) 0355 ) 0356 ); 0357 } 0358 0359 void TableViewTreeView::slotModelGroupingModeChanged() 0360 { 0361 setRootIsDecorated(s->tableViewModel->groupingMode() == TableViewModel::GroupingShowSubItems); 0362 } 0363 0364 } // namespace Digikam 0365 0366 #include "moc_tableview_treeview.cpp"