1 /***************************************************************************
2     qgsfieldvalueslineedit.h
3      -----------------------
4     Date                 : 20-08-2016
5     Copyright            : (C) 2016 by Nyall Dawson
6     Email                : nyall dot dawson at gmail dot com
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 #ifndef QGSFIELDVALUESLINEEDIT_H
16 #define QGSFIELDVALUESLINEEDIT_H
17 
18 #include "qgsfilterlineedit.h"
19 #include "qgis_sip.h"
20 #include "qgsfeedback.h"
21 
22 #include <QStringListModel>
23 #include <QTreeView>
24 #include <QFocusEvent>
25 #include <QHeaderView>
26 #include <QTimer>
27 #include <QThread>
28 #include <QMutex>
29 
30 #include "qgis_gui.h"
31 
32 class QgsFloatingWidget;
33 class QgsVectorLayer;
34 
35 
36 #ifndef SIP_RUN
37 
38 // just internal guff - definitely not for exposing to public API!
39 ///@cond PRIVATE
40 
41 /**
42  * \class QgsFieldValuesLineEditValuesGatherer
43  * Collates unique values containing a matching substring in a thread.
44  */
45 class QgsFieldValuesLineEditValuesGatherer: public QThread
46 {
47     Q_OBJECT
48 
49   public:
QgsFieldValuesLineEditValuesGatherer(QgsVectorLayer * layer,int attributeIndex)50     QgsFieldValuesLineEditValuesGatherer( QgsVectorLayer *layer, int attributeIndex )
51       : mLayer( layer )
52       , mAttributeIndex( attributeIndex )
53       , mWasCanceled( false )
54     {}
55 
56     /**
57      * Sets the substring to find matching values containing
58      */
setSubstring(const QString & string)59     void setSubstring( const QString &string ) { mSubstring = string; }
60 
61     void run() override;
62 
63     //! Informs the gatherer to immediately stop collecting values
64     void stop();
65 
66     //! Returns TRUE if collection was canceled before completion
wasCanceled()67     bool wasCanceled() const { return mWasCanceled; }
68 
69   signals:
70 
71     /**
72      * Emitted when values have been collected
73      * \param values list of unique matching string values
74      */
75     void collectedValues( const QStringList &values );
76 
77   private:
78 
79     QgsVectorLayer *mLayer = nullptr;
80     int mAttributeIndex;
81     QString mSubstring;
82     QStringList mValues;
83     QgsFeedback *mFeedback = nullptr;
84     QMutex mFeedbackMutex;
85     bool mWasCanceled;
86 };
87 
88 ///@endcond
89 
90 #endif
91 
92 /**
93  * \class QgsFieldValuesLineEdit
94  * \ingroup gui
95  * \brief A line edit with an autocompleter which takes unique values from a vector layer's fields.
96  * The autocompleter is populated from the vector layer in the background to ensure responsive
97  * interaction with the widget.
98  * \since QGIS 3.0
99  */
100 class GUI_EXPORT QgsFieldValuesLineEdit: public QgsFilterLineEdit
101 {
102     Q_OBJECT
103 
104     Q_PROPERTY( QgsVectorLayer *layer READ layer WRITE setLayer NOTIFY layerChanged )
105     Q_PROPERTY( int attributeIndex READ attributeIndex WRITE setAttributeIndex NOTIFY attributeIndexChanged )
106 
107   public:
108 
109     /**
110      * Constructor for QgsFieldValuesLineEdit
111      * \param parent parent widget
112      */
113     QgsFieldValuesLineEdit( QWidget *parent SIP_TRANSFERTHIS = nullptr );
114 
115     ~QgsFieldValuesLineEdit() override;
116 
117     /**
118      * Sets the layer containing the field that values will be shown from.
119      * \param layer vector layer
120      * \see layer()
121      * \see setAttributeIndex()
122      */
123     void setLayer( QgsVectorLayer *layer );
124 
125     /**
126      * Returns the layer containing the field that values will be shown from.
127      * \see setLayer()
128      * \see attributeIndex()
129      */
layer()130     QgsVectorLayer *layer() const { return mLayer; }
131 
132     /**
133      * Sets the attribute index for the field containing values to show in the widget.
134      * \param index index of attribute
135      * \see attributeIndex()
136      * \see setLayer()
137      */
138     void setAttributeIndex( int index );
139 
140     /**
141      * Returns the attribute index for the field containing values shown in the widget.
142      * \see setAttributeIndex()
143      * \see layer()
144      */
attributeIndex()145     int attributeIndex() const { return mAttributeIndex; }
146 
147   signals:
148 
149     /**
150      * Emitted when the layer associated with the widget changes.
151      * \param layer vector layer
152      */
153     void layerChanged( QgsVectorLayer *layer );
154 
155     /**
156      * Emitted when the field associated with the widget changes.
157      * \param index new attribute index for field
158      */
159     void attributeIndexChanged( int index );
160 
161   private slots:
162 
163     /**
164      * Requests that the autocompleter updates its completion list. The update will not occur immediately
165      * but after a preset timeout to avoid multiple updates while a user is quickly typing.
166      */
167     void requestCompleterUpdate();
168 
169     /**
170      * Updates the autocompleter list immediately. Calling
171      * this will trigger a background request to the layer to fetch matching unique values.
172      */
173     void triggerCompleterUpdate();
174 
175     /**
176      * Updates the values shown in the completer list.
177      * \param values list of string values to show
178      */
179     void updateCompleter( const QStringList &values );
180 
181     /**
182      * Called when the gatherer thread is complete, regardless of whether it finished collecting values.
183      * Cleans up the gatherer thread and triggers a new background thread if the widget's text has changed
184      * in the meantime.
185      */
186     void gathererThreadFinished();
187 
188   private:
189 
190     QgsVectorLayer *mLayer = nullptr;
191     int mAttributeIndex = -1;
192 
193     //! Will be TRUE when a background update of the completer values is occurring
194     bool mUpdateRequested = false;
195 
196     //! Timer to prevent multiple updates of autocomplete list
197     QTimer mShowPopupTimer;
198 
199     //! Background value gatherer thread
200     QgsFieldValuesLineEditValuesGatherer *mGatherer = nullptr;
201 
202     //! Will be set to the latest completion text string which should be requested
203     QString mRequestedCompletionText;
204 
205     //! Kicks off the gathering of completer text values for a specified substring
206     void updateCompletionList( const QString &substring );
207 
208 };
209 
210 
211 #endif //QGSFIELDVALUESLINEEDIT_H
212