File indexing completed on 2025-01-19 03:53:21
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-03-25 0007 * Description : Tree View for album models 0008 * 0009 * SPDX-FileCopyrightText: 2009-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2010-2011 by Andi Clemens <andi dot clemens at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #ifndef DIGIKAM_ABSTRACT_ALBUM_TREE_VIEW_H 0018 #define DIGIKAM_ABSTRACT_ALBUM_TREE_VIEW_H 0019 0020 // Qt includes 0021 0022 #include <QTreeView> 0023 0024 // Local includes 0025 0026 #include "albummanager.h" 0027 #include "albummodel.h" 0028 #include "albumfiltermodel.h" 0029 #include "albumpointer.h" 0030 #include "statesavingobject.h" 0031 0032 namespace Digikam 0033 { 0034 0035 class ContextMenuHelper; 0036 class TagModificationHelper; 0037 0038 // NOTE: This structure name can be in conflict with QAbstractItemView::State. FIXME later. 0039 struct State; 0040 0041 /** 0042 * Base class for all tree views that display Album-based content provided by an 0043 * AbstractSpecificAlbumModel. This class enables various utility functions like 0044 * selecting albums on mouse actions or providing an infrastructure for 0045 * displaying a context menu for albums. 0046 * 0047 * Context menu handling is implemented as template methods with hook methods 0048 * that can be implemented by subclasses to provide a custom behavior. In 0049 * default mode no context menu is shown at all. It must be enabled via a call 0050 * to setEnableContextMenu. 0051 */ 0052 class AbstractAlbumTreeView : public QTreeView, 0053 public StateSavingObject 0054 { 0055 Q_OBJECT 0056 0057 public: 0058 0059 enum Flag 0060 { 0061 /** 0062 * Create a default model. Not supported by abstract classes. 0063 * Not part of default flags! 0064 */ 0065 CreateDefaultModel, 0066 0067 /** 0068 * Create a default filter model. 0069 */ 0070 CreateDefaultFilterModel, 0071 0072 /** 0073 * Create a delegate which paints according to settings. 0074 * If not set, the Qt default delegate of the view is used. 0075 */ 0076 CreateDefaultDelegate, 0077 0078 /** 0079 * Show the count according to the settings. 0080 * If not set, call setShowCount() on the model yourself. 0081 */ 0082 ShowCountAccordingToSettings, 0083 0084 /** 0085 * Always show the inclusive counts. 0086 * Not part of default flags! 0087 */ 0088 AlwaysShowInclusiveCounts, 0089 0090 DefaultFlags = CreateDefaultFilterModel | CreateDefaultDelegate | ShowCountAccordingToSettings 0091 }; 0092 Q_DECLARE_FLAGS(Flags, Flag) 0093 0094 public: 0095 0096 /** 0097 * Constructs an album tree view. 0098 * If you give 0 for model, call setAlbumModel afterwards. 0099 * If you supply 0 for filterModel, call setAlbumFilterModel afterwards. 0100 */ 0101 AbstractAlbumTreeView(QWidget* const parent, Flags flags); 0102 ~AbstractAlbumTreeView() override; 0103 0104 AbstractSpecificAlbumModel* albumModel() const; 0105 AlbumFilterModel* albumFilterModel() const; 0106 0107 /** 0108 * Enable expanding of tree items on single click on the item (default: off) 0109 */ 0110 void setExpandOnSingleClick(const bool doThat); 0111 0112 /** 0113 * Expand an item when making it the new current item 0114 */ 0115 void setExpandNewCurrentItem(const bool doThat); 0116 0117 /** 0118 * Sets whether to select an album on click via the album manager or not. 0119 * 0120 * @param selectOnClick if true, a click on an item automatically sets this 0121 * item as the current album in the album manager 0122 */ 0123 void setSelectAlbumOnClick(const bool selectOnClick); 0124 0125 /** 0126 * Determines the global decision to show a popup menu or not. More detailed 0127 * decision at which position a menu can be shown and where not can be made 0128 * by implementing showContextMenuAt. 0129 * 0130 * @param enable if true, a context menu can be shown 0131 */ 0132 void setEnableContextMenu(const bool enable); 0133 0134 /** 0135 * Sets whether to select the album under the mouse cursor on a context menu 0136 * request (so that the album is shown using the album manager) or not 0137 * 0138 * Defaults to true. 0139 * 0140 * @param select true if a context menu request shall select the album 0141 */ 0142 void setSelectOnContextMenu(const bool select); 0143 0144 /** 0145 * Set the context menu title and icon. 0146 * This is used by the default implementation of contextMenuIcon() 0147 * and contextMenuTitle(). You can alternatively reimplement these methods. 0148 */ 0149 void setContextMenuIcon(const QPixmap& pixmap); 0150 void setContextMenuTitle(const QString& title); 0151 0152 /** 0153 * This is a combination of indexAt() checked with visualRect(). 0154 * p must be in the viewport currently. Decoration will not be included. 0155 * Suitable for mouse click positions. 0156 */ 0157 QModelIndex indexVisuallyAt(const QPoint& p); 0158 0159 /** 0160 * Ensures that every current match is visible by expanding all parent 0161 * entries. 0162 * 0163 * @param index the index to start ensuring expansion state 0164 * @return <code>true</code> if there was a match under <code>index</code>. 0165 * This return value can normally be ignored by the caller because 0166 * it is only used for an internal recursion. 0167 */ 0168 bool expandMatches(const QModelIndex& index); 0169 0170 //@{ 0171 0172 /** 0173 * Implements state loading for the album tree view in a somewhat clumsy 0174 * procedure because the model may not be fully loaded when this method is 0175 * called. Therefore the config is first parsed into d->statesByAlbumId 0176 * which holds the state of all tree view entries indexed by album id. 0177 * Afterwards an initial sync run is done restoring the state of all model 0178 * entries that are already present at this time. Every processed entry 0179 * is removed from d->statesByAlbumId. If there are still entries left in 0180 * this map we assume that the model is not fully loaded at the moment. 0181 * Therefore the rowsInserted signal is connected to a slot that restores 0182 * the state of new rows based on the remaining entries in 0183 * d->statesByAlbumId. 0184 */ 0185 void doLoadState() override; 0186 void doSaveState() override; 0187 0188 //@} 0189 0190 /** 0191 * Some treeviews shall control the global current album kept by AlbumManager. 0192 * Other treeview are self-contained and shall not change the current album. 0193 * Default: false 0194 */ 0195 void setAlbumManagerCurrentAlbum(const bool setCurrentAlbum); 0196 0197 /** 0198 * Add a context menu element which can add actions to the context 0199 * menu when the menu is generated. 0200 * First, addCustomContextMenuActions is called, then 0201 * all elements' addActions method is called in order of addition. 0202 */ 0203 class ContextMenuElement 0204 { 0205 public: 0206 0207 ContextMenuElement() = default; 0208 virtual ~ContextMenuElement() = default; 0209 0210 /** 0211 * Add actions to the context menu being generated 0212 * 0213 * @param view The AbstractAlbumTreeView which generates the menu 0214 * @param cmh helper object to create the context menu 0215 * @param album the album on which the context menu will be created. May be null if 0216 * it is requested on no tag entry 0217 */ 0218 virtual void addActions(AbstractAlbumTreeView* view, 0219 ContextMenuHelper& cmh, 0220 Album* album) = 0; 0221 0222 private: 0223 0224 Q_DISABLE_COPY(ContextMenuElement) 0225 }; 0226 0227 void addContextMenuElement(ContextMenuElement* const element); 0228 void removeContextMenuElement(ContextMenuElement* const element); 0229 QList<ContextMenuElement*> contextMenuElements() const; 0230 0231 template<class A> 0232 QList<A*> currentAlbums(); 0233 0234 /** 0235 * For internal use only. 0236 */ 0237 bool viewportEvent(QEvent* event) override; 0238 0239 /** 0240 * @brief selectedItems() - 0241 */ 0242 QList<Album*> selectedItems(); 0243 0244 public Q_SLOTS: 0245 0246 void setSearchTextSettings(const SearchTextSettings& settings); 0247 0248 /** 0249 * Selects the given album. 0250 * 0251 * @param albums the albums to select 0252 * @param selectInAlbumManager the album will be set as current album, if both 0253 * this parameter is true and setAlbumManagerCurrentAlbum() was set to true. 0254 */ 0255 void setCurrentAlbums(const QList<Album*>& albums, bool selectInAlbumManager = true); 0256 0257 /** 0258 * Adapt the column sizes to the contents of the tree view. 0259 */ 0260 void adaptColumnsToContent(); 0261 0262 /** 0263 * Scrolls to the first selected album if there is one. 0264 */ 0265 void scrollToSelectedAlbum(); 0266 0267 /** 0268 * Expands the complete tree under the given index. 0269 * 0270 * @param index the index to start expanding everything 0271 */ 0272 void expandEverything(const QModelIndex& index); 0273 0274 /** 0275 * @brief slotExpandNode - expands recursively selected nodes 0276 */ 0277 void slotExpandNode(); 0278 0279 /** 0280 * @brief slotCollapseNode - collapse recursively selected nodes 0281 */ 0282 void slotCollapseNode(); 0283 0284 /** 0285 * @brief slotCollapseAllNodes - collapse all nodes without root node 0286 */ 0287 void slotCollapseAllNodes(); 0288 0289 Q_SIGNALS: 0290 0291 /** 0292 * Emitted when the currently selected album changes 0293 */ 0294 void currentAlbumChanged(Album* currentAlbum); 0295 0296 /** 0297 * Emitted when the current selection changes. Use currentChanged unless in multi-selection mode. 0298 */ 0299 void selectedAlbumsChanged(const QList<Album*>& selectedAlbums); 0300 0301 protected Q_SLOTS: 0302 0303 /** 0304 * override if implemented behavior is not as intended 0305 */ 0306 virtual void slotRootAlbumAvailable(); 0307 0308 void slotSearchTextSettingsChanged(bool wasSearching, bool searching); 0309 void slotSearchTextSettingsAboutToChange(bool searched, bool willSearch); 0310 void slotCurrentChanged(); 0311 void slotSelectionChanged(); 0312 0313 void albumSettingsChanged(); 0314 0315 protected: 0316 0317 //@{ 0318 0319 // Context menu handling 0320 0321 /** 0322 * Hook method to implement that determines if a context menu shall be 0323 * displayed for the given event at the position coded in the event. 0324 * 0325 * @param event context menu event to react on 0326 * @param albumForEvent the album at the mouse position or null if there is 0327 * no album at that position 0328 * @return true if a context menu shall be displayed at the event 0329 * coordinates, else false 0330 */ 0331 virtual bool showContextMenuAt(QContextMenuEvent* event, Album* albumForEvent); 0332 0333 /** 0334 * Hook method that can be implemented to return a special icon used for the 0335 * context menu. 0336 * 0337 * @return the icon for the context menu 0338 */ 0339 virtual QPixmap contextMenuIcon() const; 0340 0341 /** 0342 * Hook method to implement that returns the title for the context menu. 0343 * 0344 * @return title for the context menu 0345 */ 0346 virtual QString contextMenuTitle() const; 0347 0348 /** 0349 * Hook method to add custom actions to the generated context menu. 0350 * 0351 * @param cmh helper object to create the context menu 0352 * @param album tag on which the context menu will be created. May be null if 0353 * it is requested on no tag entry 0354 */ 0355 virtual void addCustomContextMenuActions(ContextMenuHelper& cmh, Album* album); 0356 0357 /** 0358 * Hook method to handle the custom context menu actions that were added 0359 * with addCustomContextMenuActions. 0360 * 0361 * @param action the action that was chosen by the user, may be null if none 0362 * of the custom actions were selected 0363 * @param album the tag on which the context menu was requested. May be null 0364 * if there was no 0365 */ 0366 virtual void handleCustomContextMenuAction(QAction* action, 0367 const AlbumPointer<Album>& album); 0368 0369 //@} 0370 0371 //@{ 0372 0373 /// Other helper methods. 0374 0375 void mousePressEvent(QMouseEvent* e) override; 0376 0377 void rowsInserted(const QModelIndex& index, int start, int end) override; 0378 void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; 0379 void startDrag(Qt::DropActions supportedActions) override; 0380 void dragEnterEvent(QDragEnterEvent* e) override; 0381 void dragMoveEvent(QDragMoveEvent* e) override; 0382 void dragLeaveEvent(QDragLeaveEvent* e) override; 0383 void dropEvent(QDropEvent* e) override; 0384 0385 virtual void middleButtonPressed(Album* a); 0386 virtual QPixmap pixmapForDrag(const QStyleOptionViewItem& option, QList<QModelIndex> indexes); 0387 0388 void setAlbumFilterModel(AlbumFilterModel* const filterModel); 0389 void setAlbumModel(AbstractSpecificAlbumModel* const model); 0390 0391 //@} 0392 0393 protected: 0394 0395 AbstractSpecificAlbumModel* m_albumModel; 0396 AlbumFilterModel* m_albumFilterModel; 0397 AlbumModelDragDropHandler* m_dragDropHandler; 0398 0399 int m_lastScrollBarValue; 0400 bool m_checkOnMiddleClick; 0401 bool m_restoreCheckState; 0402 Flags m_flags; 0403 0404 private: 0405 0406 void saveStateRecursive(const QModelIndex& index, 0407 QList<int>& selection, QList<int>& expansion); 0408 0409 /** 0410 * Restores the state of the index and all sub-indexes if there is an entry 0411 * for this index in stateStore. Every album that is restored is removed 0412 * from the stateStore. 0413 * 0414 * @param index the index to start restoring 0415 * @param stateStore states indexed by album id 0416 */ 0417 void restoreStateForHierarchy(const QModelIndex& index, 0418 const QMap<int, Digikam::State>& stateStore); 0419 0420 /** 0421 * Restore the state for this index. 0422 */ 0423 void restoreState(const QModelIndex& index, 0424 const QMap<int, Digikam::State>& stateStore); 0425 0426 /** 0427 * Creates the context menu. 0428 * 0429 * @param event the event that requested the menu 0430 */ 0431 void contextMenuEvent(QContextMenuEvent* event) override; 0432 0433 private Q_SLOTS: 0434 0435 /** 0436 * Adapts the columns in between the given model indices to the content 0437 * size. This can be connected to dataChanged. 0438 * 0439 * @param topLeft top left index of changed data 0440 * @param bottomRight index of changed data 0441 */ 0442 void adaptColumnsOnDataChange(const QModelIndex& topLeft, const QModelIndex& bottomRight); 0443 0444 /** 0445 * Adapt the column sizes to new contents. This can be connected to all 0446 * signals indicating row changes. 0447 * 0448 * @param parent the parent index of changed rows 0449 * @param start the start row changed under the parent 0450 * @param end the end row changed under the parent 0451 */ 0452 void adaptColumnsOnRowChange(const QModelIndex& parent, int start, int end); 0453 0454 /** 0455 * Adapts the column sizes if the layout changes. 0456 */ 0457 void adaptColumnsOnLayoutChange(); 0458 0459 /** 0460 * This slot is used to ensure that after searching for entries the correct 0461 * album is selected again. Therefore it tracks new selections. 0462 */ 0463 void currentAlbumChangedForBackupSelection(Album* currentAlbum); 0464 0465 /** 0466 * This slots is used to fix bug 400960. 0467 */ 0468 void slotScrollBarValueChanged(int value); 0469 void slotScrollBarActionTriggered(int action); 0470 0471 private: 0472 0473 class Private; 0474 Private* d; 0475 }; 0476 0477 } // namespace Digikam 0478 0479 Q_DECLARE_OPERATORS_FOR_FLAGS(Digikam::AbstractAlbumTreeView::Flags) 0480 0481 #endif // DIGIKAM_ABSTRACT_ALBUM_TREE_VIEW_H