13
13
* *
14
14
***************************************************************************/
15
15
16
+ #include < QGraphicsProxyWidget>
17
+ #include < QMouseEvent>
18
+ #include < QGridLayout>
19
+ #include < QLabel>
20
+
16
21
#include " qgsdoublespinbox.h"
17
22
#include " qgsfeatureiterator.h"
18
23
#include " qgsmaptooloffsetcurve.h"
21
26
#include " qgsrubberband.h"
22
27
#include " qgssnappingutils.h"
23
28
#include " qgsvectorlayer.h"
24
- #include " qgsvertexmarker .h"
29
+ #include " qgssnapindicator .h"
25
30
#include " qgssnappingconfig.h"
26
31
#include " qgssettings.h"
27
32
#include " qgisapp.h"
28
33
29
- #include < QGraphicsProxyWidget>
30
- #include < QMouseEvent>
31
34
#include " qgisapp.h"
32
35
33
36
QgsMapToolOffsetCurve::QgsMapToolOffsetCurve ( QgsMapCanvas *canvas )
34
37
: QgsMapToolEdit( canvas )
38
+ , mSnapIndicator( qgis::make_unique< QgsSnapIndicator >( canvas ) )
35
39
{
36
40
}
37
41
38
42
QgsMapToolOffsetCurve::~QgsMapToolOffsetCurve ()
39
43
{
40
- deleteRubberBandAndGeometry ();
41
- deleteDistanceWidget ();
42
- delete mSnapVertexMarker ;
44
+ cancel ();
43
45
}
44
46
45
47
void QgsMapToolOffsetCurve::keyPressEvent ( QKeyEvent *e )
46
48
{
47
49
if ( e && e->key () == Qt::Key_Escape && !e->isAutoRepeat () )
48
50
{
49
- deleteRubberBandAndGeometry ();
50
- deleteDistanceWidget ();
51
+ cancel ();
51
52
}
52
53
else
53
54
{
@@ -58,23 +59,11 @@ void QgsMapToolOffsetCurve::keyPressEvent( QKeyEvent *e )
58
59
59
60
void QgsMapToolOffsetCurve::canvasReleaseEvent ( QgsMapMouseEvent *e )
60
61
{
61
- if ( !mCanvas )
62
- {
63
- return ;
64
- }
65
-
66
- QgsVectorLayer *layer = currentVectorLayer ();
67
- if ( !layer )
68
- {
69
- deleteRubberBandAndGeometry ();
70
- notifyNotVectorLayer ();
71
- return ;
72
- }
62
+ mCtrlHeldOnFirstClick = false ;
73
63
74
64
if ( e->button () == Qt::RightButton )
75
65
{
76
- deleteRubberBandAndGeometry ();
77
- deleteDistanceWidget ();
66
+ cancel ();
78
67
return ;
79
68
}
80
69
@@ -83,7 +72,6 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
83
72
// first click, get feature to modify
84
73
deleteRubberBandAndGeometry ();
85
74
mGeometryModified = false ;
86
- mCtrlWasHeldOnFeatureSelection = false ;
87
75
88
76
QgsSnappingUtils *snapping = mCanvas ->snappingUtils ();
89
77
@@ -106,50 +94,53 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
106
94
107
95
if ( match.hasEdge () && match.layer () )
108
96
{
109
- mSourceLayerId = match.layer ()-> id ();
97
+ mLayer = match.layer ();
110
98
QgsFeature fet;
111
99
if ( match.layer ()->getFeatures ( QgsFeatureRequest ( match.featureId () ) ).nextFeature ( fet ) )
112
100
{
113
- mCtrlWasHeldOnFeatureSelection = ( e->modifiers () & Qt::ControlModifier ); // no geometry modification if ctrl is pressed
101
+ mCtrlHeldOnFirstClick = ( e->modifiers () & Qt::ControlModifier ); // no geometry modification if ctrl is pressed
114
102
prepareGeometry ( match.layer (), match, fet );
115
103
mRubberBand = createRubberBand ();
116
104
if ( mRubberBand )
117
105
{
118
- mRubberBand ->setToGeometry ( mManipulatedGeometry , layer );
106
+ mRubberBand ->setToGeometry ( mManipulatedGeometry , match. layer () );
119
107
}
120
108
mModifiedFeature = fet.id ();
121
- createDistanceWidget ();
109
+ createUserInputWidget ();
122
110
}
123
111
}
124
112
125
113
if ( mOriginalGeometry .isNull () )
126
114
{
127
115
emit messageEmitted ( tr ( " Could not find a nearby feature in any vector layer." ) );
116
+ cancel ();
117
+ notifyNotVectorLayer ();
128
118
}
129
119
}
130
120
else
131
121
{
132
122
// second click - apply changes
133
- applyOffset ( e->modifiers () & Qt::ControlModifier );
123
+ double offset = calculateOffset ( e->snapPoint () );
124
+ applyOffset ( offset, e->modifiers () );
134
125
}
135
126
}
136
127
137
- void QgsMapToolOffsetCurve::applyOffset ( bool forceCopy )
128
+ void QgsMapToolOffsetCurve::applyOffset ( const double &offset, const Qt::KeyboardModifiers &modifiers )
138
129
{
139
- QgsVectorLayer *layer = currentVectorLayer ();
140
- if ( !layer )
130
+ if ( !mLayer || offset == 0.0 )
141
131
{
142
- deleteRubberBandAndGeometry ();
132
+ cancel ();
143
133
notifyNotVectorLayer ();
144
134
return ;
145
135
}
146
136
137
+ updateGeometryAndRubberBand ( offset );
138
+
147
139
// no modification
148
140
if ( !mGeometryModified )
149
141
{
150
- deleteRubberBandAndGeometry ();
151
- layer->destroyEditCommand ();
152
- deleteDistanceWidget ();
142
+ mLayer ->destroyEditCommand ();
143
+ cancel ();
153
144
return ;
154
145
}
155
146
@@ -207,110 +198,99 @@ void QgsMapToolOffsetCurve::applyOffset( bool forceCopy )
207
198
mModifiedGeometry = geometry;
208
199
}
209
200
210
- layer ->beginEditCommand ( tr ( " Offset curve" ) );
201
+ mLayer ->beginEditCommand ( tr ( " Offset curve" ) );
211
202
212
203
bool editOk;
213
- if ( mSourceLayerId == layer-> id () && !mCtrlWasHeldOnFeatureSelection && !forceCopy )
204
+ if ( ! mCtrlHeldOnFirstClick && !modifiers. testFlag ( Qt::ControlModifier ) )
214
205
{
215
- editOk = layer ->changeGeometry ( mModifiedFeature , mModifiedGeometry );
206
+ editOk = mLayer ->changeGeometry ( mModifiedFeature , mModifiedGeometry );
216
207
}
217
208
else
218
209
{
219
210
QgsFeature f;
220
211
f.setGeometry ( mModifiedGeometry );
221
212
222
213
// add empty values for all fields (allows inserting attribute values via the feature form in the same session)
223
- QgsAttributes attrs ( layer ->fields ().count () );
224
- const QgsFields &fields = layer ->fields ();
214
+ QgsAttributes attrs ( mLayer ->fields ().count () );
215
+ const QgsFields &fields = mLayer ->fields ();
225
216
for ( int idx = 0 ; idx < fields.count (); ++idx )
226
217
{
227
218
attrs[idx] = QVariant ();
228
219
}
229
220
f.setAttributes ( attrs );
230
- editOk = layer ->addFeature ( f );
221
+ editOk = mLayer ->addFeature ( f );
231
222
}
232
223
233
224
if ( editOk )
234
225
{
235
- layer ->endEditCommand ();
226
+ mLayer ->endEditCommand ();
236
227
}
237
228
else
238
229
{
239
- layer->destroyEditCommand ();
230
+ mLayer ->destroyEditCommand ();
231
+ emit messageEmitted ( " Could not apply offset" , Qgis::Critical );
240
232
}
241
233
242
234
deleteRubberBandAndGeometry ();
243
- deleteDistanceWidget ();
244
- delete mSnapVertexMarker ;
245
- mSnapVertexMarker = nullptr ;
246
- mCtrlWasHeldOnFeatureSelection = false ;
247
- layer->triggerRepaint ();
235
+ deleteUserInputWidget ();
236
+ mLayer ->triggerRepaint ();
237
+ mLayer = nullptr ;
248
238
}
249
239
250
- void QgsMapToolOffsetCurve::placeOffsetCurveToValue ()
240
+ void QgsMapToolOffsetCurve::cancel ()
251
241
{
252
- setOffsetForRubberBand ( mDistanceWidget ->value () );
242
+ deleteUserInputWidget ();
243
+ deleteRubberBandAndGeometry ();
244
+ mLayer = nullptr ;
253
245
}
254
246
255
- void QgsMapToolOffsetCurve::canvasMoveEvent ( QgsMapMouseEvent *e )
247
+ double QgsMapToolOffsetCurve::calculateOffset ( QgsPointXY mapPoint )
256
248
{
257
- delete mSnapVertexMarker ;
258
- mSnapVertexMarker = nullptr ;
259
-
260
- if ( mOriginalGeometry .isNull () || !mRubberBand )
249
+ double offset = 0.0 ;
250
+ if ( mLayer )
261
251
{
262
- return ;
252
+ // get offset from current position rectangular to feature
253
+ QgsPointXY layerCoords = toLayerCoordinates ( mLayer , mapPoint );
254
+
255
+ QgsPointXY minDistPoint;
256
+ int beforeVertex;
257
+ int leftOf = 0 ;
258
+
259
+ offset = std::sqrt ( mOriginalGeometry .closestSegmentWithContext ( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
260
+ offset = leftOf < 0 ? offset : -offset;
263
261
}
262
+ return offset;
263
+ }
264
264
265
- QgsVectorLayer *layer = currentVectorLayer ();
266
- if ( !layer )
265
+ void QgsMapToolOffsetCurve::canvasMoveEvent ( QgsMapMouseEvent *e )
266
+ {
267
+ if ( mOriginalGeometry .isNull () || !mRubberBand )
267
268
{
268
269
return ;
269
270
}
270
271
271
-
272
272
mGeometryModified = true ;
273
273
274
- // get offset from current position rectangular to feature
275
- QgsPointXY layerCoords = toLayerCoordinates ( layer, e->pos () );
274
+ QgsPointXY mapPoint = e-> snapPoint ();
275
+ mSnapIndicator -> setMatch ( e->mapPointMatch () );
276
276
277
- // snap cursor to background layers
278
- QgsPointLocator::Match m = mCanvas ->snappingUtils ()->snapToMap ( e->pos () );
279
- if ( m.isValid () )
280
- {
281
- if ( ( m.layer () && m.layer ()->id () != mSourceLayerId ) || m.featureId () != mModifiedFeature )
282
- {
283
- layerCoords = toLayerCoordinates ( layer, m.point () );
284
- mSnapVertexMarker = new QgsVertexMarker ( mCanvas );
285
- mSnapVertexMarker ->setIconType ( QgsVertexMarker::ICON_CROSS );
286
- mSnapVertexMarker ->setColor ( Qt::green );
287
- mSnapVertexMarker ->setPenWidth ( 1 );
288
- mSnapVertexMarker ->setCenter ( m.point () );
289
- }
290
- }
291
-
292
- QgsPointXY minDistPoint;
293
- int beforeVertex;
294
- int leftOf = 0 ;
295
- double offset = std::sqrt ( mOriginalGeometry .closestSegmentWithContext ( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
277
+ double offset = calculateOffset ( mapPoint );
296
278
if ( offset == 0.0 )
297
279
{
298
280
return ;
299
281
}
300
- offset = leftOf < 0 ? offset : -offset;
301
282
302
-
303
- if ( mDistanceWidget )
283
+ if ( mUserInputWidget )
304
284
{
305
- // this will also set the rubber band
306
- mDistanceWidget ->setValue ( offset );
307
- mDistanceWidget ->setFocus ( Qt::TabFocusReason );
308
- }
309
- else
310
- {
311
- // create offset geometry using geos
312
- setOffsetForRubberBand ( offset );
285
+ disconnect ( mUserInputWidget , &QgsOffsetUserWidget::offsetChanged, this , &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
286
+ mUserInputWidget ->setOffset ( offset );
287
+ connect ( mUserInputWidget , &QgsOffsetUserWidget::offsetChanged, this , &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
288
+ mUserInputWidget ->setFocus ( Qt::TabFocusReason );
289
+ mUserInputWidget ->editor ()->selectAll ();
313
290
}
291
+
292
+ // create offset geometry using geos
293
+ updateGeometryAndRubberBand ( offset );
314
294
}
315
295
316
296
void QgsMapToolOffsetCurve::prepareGeometry ( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature )
@@ -374,38 +354,34 @@ void QgsMapToolOffsetCurve::prepareGeometry( QgsVectorLayer *vl, const QgsPointL
374
354
}
375
355
}
376
356
377
- void QgsMapToolOffsetCurve::createDistanceWidget ()
357
+ void QgsMapToolOffsetCurve::createUserInputWidget ()
378
358
{
379
359
if ( !mCanvas )
380
360
{
381
361
return ;
382
362
}
383
363
384
- deleteDistanceWidget ();
385
-
386
- mDistanceWidget = new QgsDoubleSpinBox ();
387
- mDistanceWidget ->setMinimum ( -99999999 );
388
- mDistanceWidget ->setMaximum ( 99999999 );
389
- mDistanceWidget ->setDecimals ( 6 );
390
- mDistanceWidget ->setPrefix ( tr ( " Offset: " ) );
391
- mDistanceWidget ->setClearValue ( 0.0 );
392
- QgisApp::instance ()->addUserInputWidget ( mDistanceWidget );
364
+ deleteUserInputWidget ();
365
+ mUserInputWidget = new QgsOffsetUserWidget ();
366
+ QgisApp::instance ()->addUserInputWidget ( mUserInputWidget );
367
+ mUserInputWidget ->setFocus ( Qt::TabFocusReason );
393
368
394
- mDistanceWidget ->setFocus ( Qt::TabFocusReason );
395
-
396
- connect ( mDistanceWidget , static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this , &QgsMapToolOffsetCurve::placeOffsetCurveToValue );
397
- connect ( mDistanceWidget , &QAbstractSpinBox::editingFinished, this , [ = ] { applyOffset (); } );
369
+ connect ( mUserInputWidget , &QgsOffsetUserWidget::offsetChanged, this , &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
370
+ connect ( mUserInputWidget , &QgsOffsetUserWidget::offsetEditingFinished, this , &QgsMapToolOffsetCurve::applyOffset );
371
+ connect ( mUserInputWidget , &QgsOffsetUserWidget::offsetEditingCanceled, this , &QgsMapToolOffsetCurve::cancel );
398
372
}
399
373
400
- void QgsMapToolOffsetCurve::deleteDistanceWidget ()
374
+ void QgsMapToolOffsetCurve::deleteUserInputWidget ()
401
375
{
402
- if ( mDistanceWidget )
376
+ if ( mUserInputWidget )
403
377
{
404
- disconnect ( mDistanceWidget , static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this , &QgsMapToolOffsetCurve::placeOffsetCurveToValue );
405
- mDistanceWidget ->releaseKeyboard ();
406
- mDistanceWidget ->deleteLater ();
378
+ disconnect ( mUserInputWidget , &QgsOffsetUserWidget::offsetChanged, this , &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
379
+ disconnect ( mUserInputWidget , &QgsOffsetUserWidget::offsetEditingFinished, this , &QgsMapToolOffsetCurve::applyOffset );
380
+ disconnect ( mUserInputWidget , &QgsOffsetUserWidget::offsetEditingCanceled, this , &QgsMapToolOffsetCurve::cancel );
381
+ mUserInputWidget ->releaseKeyboard ();
382
+ mUserInputWidget ->deleteLater ();
407
383
}
408
- mDistanceWidget = nullptr ;
384
+ mUserInputWidget = nullptr ;
409
385
}
410
386
411
387
void QgsMapToolOffsetCurve::deleteRubberBandAndGeometry ()
@@ -416,15 +392,14 @@ void QgsMapToolOffsetCurve::deleteRubberBandAndGeometry()
416
392
mRubberBand = nullptr ;
417
393
}
418
394
419
- void QgsMapToolOffsetCurve::setOffsetForRubberBand ( double offset )
395
+ void QgsMapToolOffsetCurve::updateGeometryAndRubberBand ( double offset )
420
396
{
421
397
if ( !mRubberBand || mOriginalGeometry .isNull () )
422
398
{
423
399
return ;
424
400
}
425
401
426
- QgsVectorLayer *sourceLayer = dynamic_cast <QgsVectorLayer *>( QgsProject::instance ()->mapLayer ( mSourceLayerId ) );
427
- if ( !sourceLayer )
402
+ if ( !mLayer )
428
403
{
429
404
return ;
430
405
}
@@ -447,17 +422,81 @@ void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset )
447
422
if ( !offsetGeom )
448
423
{
449
424
deleteRubberBandAndGeometry ();
450
- deleteDistanceWidget ();
451
- delete mSnapVertexMarker ;
452
- mSnapVertexMarker = nullptr ;
453
- mCtrlWasHeldOnFeatureSelection = false ;
425
+ deleteUserInputWidget ();
426
+ mLayer = nullptr ;
454
427
mGeometryModified = false ;
455
- deleteDistanceWidget ();
456
428
emit messageEmitted ( tr ( " Creating offset geometry failed: %1" ).arg ( offsetGeom.lastError () ), Qgis::Critical );
457
429
}
458
430
else
459
431
{
460
432
mModifiedGeometry = offsetGeom;
461
- mRubberBand ->setToGeometry ( mModifiedGeometry , sourceLayer );
433
+ mRubberBand ->setToGeometry ( mModifiedGeometry , mLayer );
434
+ }
435
+ }
436
+
437
+
438
+ // ******************
439
+ // Offset User Widget
440
+
441
+ QgsOffsetUserWidget::QgsOffsetUserWidget ( QWidget *parent )
442
+ : QWidget( parent )
443
+ {
444
+ mLayout = new QGridLayout ( this );
445
+ mLayout ->setContentsMargins ( 0 , 0 , 0 , 0 );
446
+ // mLayout->setAlignment( Qt::AlignLeft );
447
+ setLayout ( mLayout );
448
+
449
+ QLabel *lbl = new QLabel ( tr ( " Offset" ), this );
450
+ lbl->setAlignment ( Qt::AlignRight | Qt::AlignCenter );
451
+ mLayout ->addWidget ( lbl, 0 , 0 );
452
+
453
+ mOffsetSpinBox = new QgsDoubleSpinBox ();
454
+ mOffsetSpinBox ->setMinimum ( -99999999 );
455
+ mOffsetSpinBox ->setMaximum ( 99999999 );
456
+ mOffsetSpinBox ->setDecimals ( 6 );
457
+ mOffsetSpinBox ->setClearValue ( 0.0 );
458
+ mOffsetSpinBox ->setShowClearButton ( false );
459
+ mLayout ->addWidget ( mOffsetSpinBox , 0 , 1 );
460
+
461
+ // connect signals
462
+ mOffsetSpinBox ->installEventFilter ( this );
463
+ connect ( mOffsetSpinBox , static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this , &QgsOffsetUserWidget::offsetSpinBoxValueChanged );
464
+
465
+ // config focus
466
+ setFocusProxy ( mOffsetSpinBox );
467
+ }
468
+
469
+ void QgsOffsetUserWidget::setOffset ( double offset )
470
+ {
471
+ mOffsetSpinBox ->setValue ( offset );
472
+ }
473
+
474
+ double QgsOffsetUserWidget::offset ()
475
+ {
476
+ return mOffsetSpinBox ->value ();
477
+ }
478
+
479
+ bool QgsOffsetUserWidget::eventFilter ( QObject *obj, QEvent *ev )
480
+ {
481
+ if ( obj == mOffsetSpinBox && ev->type () == QEvent::KeyPress )
482
+ {
483
+ QKeyEvent *event = static_cast <QKeyEvent *>( ev );
484
+ if ( event->key () == Qt::Key_Escape )
485
+ {
486
+ emit offsetEditingCanceled ();
487
+ return true ;
488
+ }
489
+ if ( event->key () == Qt::Key_Enter || event->key () == Qt::Key_Return )
490
+ {
491
+ emit offsetEditingFinished ( offset (), event->modifiers () );
492
+ return true ;
493
+ }
462
494
}
495
+
496
+ return false ;
497
+ }
498
+
499
+ void QgsOffsetUserWidget::offsetSpinBoxValueChanged ( double offset )
500
+ {
501
+ emit offsetChanged ( offset );
463
502
}
0 commit comments