1 /***************************************************************************
2     qgsdualview.h
3      --------------------------------------
4     Date                 : 10.2.2013
5     Copyright            : (C) 2013 Matthias Kuhn
6     Email                : matthias at opengis dot ch
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #ifndef QGSDUALVIEW_H
17 #define QGSDUALVIEW_H
18 
19 #include <QStackedWidget>
20 
21 #include "ui_qgsdualviewbase.h"
22 
23 #include "qgsattributeeditorcontext.h"
24 #include "qgsattributetablefiltermodel.h"
25 #include "qgsattributeform.h"
26 #include "qgis_gui.h"
27 
28 class QgsFeatureRequest;
29 class QgsMapLayerAction;
30 class QgsScrollArea;
31 class QgsFieldConditionalFormatWidget;
32 
33 /**
34  * \ingroup gui
35  * \brief This widget is used to show the attributes of a set of features of a QgsVectorLayer.
36  * The attributes can be edited.
37  * It supports two different layouts: the table layout, in which the attributes for the features
38  * are shown in a table and the editor layout, where the features are shown as a selectable list
39  * and the attributes for the currently selected feature are shown in a form.
40  */
41 class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBase
42 {
43     Q_OBJECT
44 
45   public:
46 
47     /**
48      * The view modes, in which this widget can present information.
49      * Relates to the QStackedWidget stacks.
50      *
51      */
52     enum ViewMode
53     {
54 
55       /**
56        * Shows the features and attributes in a table layout
57        */
58       AttributeTable = 0,
59 
60       /**
61        * Show a list of the features, where one can be chosen
62        * and the according attribute dialog will be presented
63        * in the neighbouring frame.
64        */
65       AttributeEditor = 1
66     };
67     Q_ENUM( ViewMode )
68 
69 
70     //! Action on the map canvas when browsing the list of features
71     enum FeatureListBrowsingAction
72     {
73       NoAction = 0, //!< No action is done
74       PanToFeature, //!< The map is panned to the center of the feature bounding-box
75       ZoomToFeature, //!< The map is zoomed to contained the feature bounding-box
76     };
77     Q_ENUM( FeatureListBrowsingAction )
78 
79     /**
80      * \brief Constructor
81      * \param parent  The parent widget
82      */
83     explicit QgsDualView( QWidget *parent SIP_TRANSFERTHIS = nullptr );
84     ~QgsDualView() override;
85 
86     /**
87      * Has to be called to initialize the dual view.
88      *
89      * \param layer      The layer which should be used to fetch features
90      * \param mapCanvas  The mapCanvas (used for the FilterMode
91      *                   QgsAttributeTableFilterModel::ShowVisible)
92      * \param request    Use a modified request to limit the shown features
93      * \param context    The context in which this view is shown
94      * \param loadFeatures whether to initially load all features into the view. If set to
95      *                   FALSE, limited features can later be loaded using setFilterMode()
96      */
97     void init( QgsVectorLayer *layer,
98                QgsMapCanvas *mapCanvas,
99                const QgsFeatureRequest &request = QgsFeatureRequest(),
100                const QgsAttributeEditorContext &context = QgsAttributeEditorContext(),
101                bool loadFeatures = true );
102 
103     /**
104      * Change the current view mode.
105      *
106      * \param view The view mode to set
107      * \see view()
108      */
109     void setView( ViewMode view );
110 
111     /**
112      * Returns the current view mode.
113      * \see setView()
114      * \since QGIS 2.16
115      */
116     ViewMode view() const;
117 
118     /**
119      * Set the filter mode
120      *
121      * \param filterMode
122      */
123     void setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode );
124 
125     /**
126      * Gets the filter mode
127      *
128      * \returns the filter mode
129      */
filterMode()130     QgsAttributeTableFilterModel::FilterMode filterMode() { return mFilterModel->filterMode(); }
131 
132     /**
133      * Toggle the selectedOnTop flag. If enabled, selected features will be moved to top.
134      *
135      * \param selectedOnTop TRUE: Show selected features on top.
136      *                      FALSE: Use defined sorting column.
137      */
138     void setSelectedOnTop( bool selectedOnTop );
139 
140     /**
141      * Returns the number of features on the layer.
142      *
143      * \returns Number of features
144      */
145     int featureCount();
146 
147     /**
148      * Returns the number of features which are currently visible, according to the
149      * filter restrictions
150      *
151      * \returns Number of features
152      */
153     int filteredFeatureCount();
154 
155     /**
156      * Set a list of currently visible features
157      *
158      * \param filteredFeatures  A list of feature ids
159      *
160      * \deprecated since filterFeatures is handled in the attribute filter model itself
161     */
162     Q_DECL_DEPRECATED void setFilteredFeatures( const QgsFeatureIds &filteredFeatures );
163 
164     /**
165      * Sets the expression and Updates the filtered features in the filter model.
166      * It is called when the filter expression changed.
167      *
168      * \since QGIS 3.10.3
169      */
170     void filterFeatures( const QgsExpression &filterExpression, const QgsExpressionContext &context );
171 
172     /**
173      * Gets a list of currently visible feature ids.
174      */
filteredFeatures()175     QgsFeatureIds filteredFeatures() { return mFilterModel->filteredFeatures(); }
176 
177     /**
178      * Returns the model which has the information about all features (not only filtered)
179      *
180      * \returns The master model
181      */
masterModel()182     QgsAttributeTableModel *masterModel() const { return mMasterModel; }
183 
184     /**
185      * Set the request
186      *
187      * \param request The request
188      */
189     void setRequest( const QgsFeatureRequest &request );
190 
191     /**
192      * Set the feature selection model
193      *
194      * \param featureSelectionManager the feature selection model
195      */
196     void setFeatureSelectionManager( QgsIFeatureSelectionManager *featureSelectionManager );
197 
198     /**
199      * Returns the table view
200      *
201      * \returns The table view
202      */
tableView()203     QgsAttributeTableView *tableView() { return mTableView; }
204 
205     /**
206      * Set the attribute table config which should be used to control
207      * the appearance of the attribute table.
208      */
209     void setAttributeTableConfig( const QgsAttributeTableConfig &config );
210 
211     /**
212      * Set the expression used for sorting the table and feature list.
213      */
214     void setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder = Qt::AscendingOrder );
215 
216     /**
217      * Gets the expression used for sorting the table and feature list.
218      */
219     QString sortExpression() const;
220 
221     /**
222      * The config used for the attribute table.
223      * \returns The config used for the attribute table.
224      */
225     QgsAttributeTableConfig attributeTableConfig() const;
226 
227   public slots:
228 
229     /**
230      * \brief Set the current edit selection in the AttributeEditor mode.
231      *
232      * \param fids   A list of edited features (Currently only one at a time is supported)
233      */
234     void setCurrentEditSelection( const QgsFeatureIds &fids );
235 
236     /**
237      * \brief saveEditChanges
238      *
239      * \returns TRUE if the saving was OK. FALSE is possible due to connected
240      *         validation logic.
241      */
242     bool saveEditChanges();
243 
244     void openConditionalStyles();
245 
246     /**
247      * Sets whether multi edit mode is enabled.
248      * \since QGIS 2.16
249      */
250     void setMultiEditEnabled( bool enabled );
251 
252     /**
253      * Toggles whether search mode should be enabled in the form.
254      * \param enabled set to TRUE to switch on search mode
255      * \since QGIS 2.16
256      */
257     void toggleSearchMode( bool enabled );
258 
259     /**
260      * Copy the content of the selected cell in the clipboard.
261      * \since QGIS 1.16
262      */
263     void copyCellContent() const;
264 
265     /**
266      * Cancel the progress dialog (if any)
267      * \since QGIS 3.0
268      */
269     void cancelProgress( );
270 
271     /**
272      * Called in embedded forms when an \a attribute \a value in the parent form has changed.
273      *
274      * Notify the form widgets that something has changed in case they
275      * have filter expression that depend on the parent form scope.
276      *
277      * \since QGIS 3.14
278      */
279     void parentFormValueChanged( const QString &attribute, const QVariant &value );
280 
281   signals:
282 
283     /**
284      * Emitted whenever the display expression is successfully changed
285      * \param expression The expression that was applied
286      */
287     void displayExpressionChanged( const QString &expression );
288 
289     /**
290      * Emitted whenever the filter changes
291      */
292     void filterChanged();
293 
294     /**
295      * Emitted when a filter expression is set using the view.
296      * \param expression filter expression
297      * \param type filter type
298      * \since QGIS 2.16
299      */
300     void filterExpressionSet( const QString &expression, QgsAttributeForm::FilterType type );
301 
302     /**
303      * Emitted when the form changes mode.
304      * \param mode new mode
305      */
306     void formModeChanged( QgsAttributeEditorContext::Mode mode );
307 
308     /**
309      * Emitted when selecting context menu on the feature list to create the context menu individually
310      * \param menu context menu
311      * \param fid feature id of the selected feature
312      */
313     void showContextMenuExternally( QgsActionMenu *menu, QgsFeatureId fid );
314 
315   protected:
316     void hideEvent( QHideEvent *event ) override;
317 
318   private slots:
319 
320     void featureListAboutToChangeEditSelection( bool &ok );
321 
322     /**
323      * Changes the currently visible feature within the attribute editor
324      *
325      * \param feat  The newly visible feature
326      */
327     void featureListCurrentEditSelectionChanged( const QgsFeature &feat );
328 
329     void previewExpressionBuilder();
330 
331     void previewColumnChanged( QAction *previewAction, const QString &expression );
332 
333     void viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex );
334 
335     void widgetWillShowContextMenu( QgsActionMenu *menu, const QModelIndex &atIndex );
336 
337     void showViewHeaderMenu( QPoint point );
338 
339     void organizeColumns();
340 
341     void tableColumnResized( int column, int width );
342 
343     void hideColumn();
344 
345     void resizeColumn();
346 
347     void autosizeColumn();
348 
349     void previewExpressionChanged( const QString &expression );
350 
351     void onSortColumnChanged();
352 
353     void updateSelectedFeatures();
354 
355     void extentChanged();
356 
357     /**
358      * Will be called whenever the currently shown feature form changes.
359      * Will forward this signal to the feature list to visually represent
360      * that there has been an edit event.
361      */
362     void featureFormAttributeChanged( const QString &attribute, const QVariant &value, bool attributeChanged );
363 
364     /**
365      * Will be called periodically, when loading layers from slow data providers.
366      *
367      * \param i       The number of features already loaded
368      * \param cancel  Set to TRUE to cancel
369      */
370     virtual void progress( int i, bool &cancel );
371 
372     /**
373      * Will be called, once all the features are loaded.
374      * Use e.g. to close a dialog created from progress( int i, bool &cancel )
375      */
376     virtual void finished();
377 
378     //! Zooms to the active feature
379     void zoomToCurrentFeature();
380     //! Pans to the active feature
381     void panToCurrentFeature();
382 
383     void flashCurrentFeature();
384 
385     void rebuildFullLayerCache();
386 
387     void panZoomGroupButtonToggled( QAbstractButton *button, bool checked );
388 
389     void flashButtonClicked( bool clicked );
390 
391     void filterError( const QString &errorMessage );
392 
393   private:
394 
395     /**
396      * Initializes widgets which depend on the attributes of this layer
397      */
398     void columnBoxInit();
399     void initLayerCache( bool cacheGeometry );
400     void initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures );
401     void restoreRecentDisplayExpressions();
402     void saveRecentDisplayExpressions() const;
403     void setDisplayExpression( const QString &expression );
404     void insertRecentlyUsedDisplayExpression( const QString &expression );
405     void updateEditSelectionProgress( int progress, int count );
406     void panOrZoomToFeature( const QgsFeatureIds &featureset );
407     //! disable/enable the buttons of the browsing toolbar (feature list view)
408     void setBrowsingAutoPanScaleAllowed( bool allowed );
409 
410     //! Returns TRUE if the expression dialog has been accepted
411     bool modifySort();
412 
413 
414     QgsFieldConditionalFormatWidget *mConditionalFormatWidget = nullptr;
415     QgsAttributeEditorContext mEditorContext;
416     QgsAttributeTableModel *mMasterModel = nullptr;
417     QgsAttributeTableFilterModel *mFilterModel = nullptr;
418     QgsFeatureListModel *mFeatureListModel = nullptr;
419     QgsAttributeForm *mAttributeForm = nullptr;
420     QMenu *mPreviewColumnsMenu = nullptr;
421     QMenu *mPreviewActionMenu = nullptr;
422     QAction *mLastDisplayExpressionAction = nullptr;
423     QMenu *mHorizontalHeaderMenu = nullptr;
424     QgsVectorLayerCache *mLayerCache = nullptr;
425     QPointer< QgsVectorLayer > mLayer = nullptr;
426     QProgressDialog *mProgressDlg = nullptr;
427     QgsIFeatureSelectionManager *mFeatureSelectionManager = nullptr;
428     QString mDisplayExpression;
429     QgsAttributeTableConfig mConfig;
430     QgsScrollArea *mAttributeEditorScrollArea = nullptr;
431     // If the current feature is set, while the form is still not initialized
432     // we will temporarily save it in here and set it on init
433     QgsFeature mTempAttributeFormFeature;
434     QgsFeatureIds mLastFeatureSet;
435     bool mBrowsingAutoPanScaleAllowed = true;
436     ViewMode mPreviousView = AttributeTable;
437 
438     friend class TestQgsDualView;
439     friend class TestQgsAttributeTable;
440 };
441 
442 /**
443  * \ingroup gui
444  * \class QgsAttributeTableAction
445  */
446 class GUI_EXPORT QgsAttributeTableAction : public QAction
447 {
448     Q_OBJECT
449 
450   public:
451 
452     /**
453      * Create a new attribute table action.
454      *
455      * \since QGIS 3.0
456      */
QgsAttributeTableAction(const QString & name,QgsDualView * dualView,QUuid action,const QModelIndex & fieldIdx)457     QgsAttributeTableAction( const QString &name, QgsDualView *dualView, QUuid action, const QModelIndex &fieldIdx )
458       : QAction( name, dualView )
459       , mDualView( dualView )
460       , mAction( action )
461       , mFieldIdx( fieldIdx )
462     {}
463 
464   public slots:
465     void execute();
466     void featureForm();
467 
468   private:
469     QgsDualView *mDualView = nullptr;
470     QUuid mAction;
471     QModelIndex mFieldIdx;
472 };
473 
474 /**
475  * \ingroup gui
476  * \class QgsAttributeTableMapLayerAction
477  */
478 class GUI_EXPORT QgsAttributeTableMapLayerAction : public QAction
479 {
480     Q_OBJECT
481 
482   public:
QgsAttributeTableMapLayerAction(const QString & name,QgsDualView * dualView,QgsMapLayerAction * action,const QModelIndex & fieldIdx)483     QgsAttributeTableMapLayerAction( const QString &name, QgsDualView *dualView, QgsMapLayerAction *action, const QModelIndex &fieldIdx )
484       : QAction( name, dualView )
485       , mDualView( dualView )
486       , mAction( action )
487       , mFieldIdx( fieldIdx )
488     {}
489 
490   public slots:
491     void execute();
492 
493   private:
494     QgsDualView *mDualView = nullptr;
495     QgsMapLayerAction *mAction = nullptr;
496     QModelIndex mFieldIdx;
497 };
498 
499 Q_DECLARE_METATYPE( QModelIndex );
500 
501 #endif // QGSDUALVIEW_H
502