18 #include <QCoreApplication> 
   43 #include <QActionGroup> 
   48   , mMapCanvas( canvas )
 
   50   , mCommonAngleConstraint( 
QgsSettings().value( QStringLiteral( 
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
 
   56   mAngleConstraint.reset( 
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
 
   57   mDistanceConstraint.reset( 
new CadConstraint( mDistanceLineEdit, mLockDistanceButton, 
nullptr, mRepeatingLockDistanceButton ) );
 
   58   mXConstraint.reset( 
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
 
   59   mYConstraint.reset( 
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
 
   60   mZConstraint.reset( 
new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
 
   61   mMConstraint.reset( 
new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
 
   64   mMapCanvas->installEventFilter( 
this );
 
   65   mAngleLineEdit->installEventFilter( 
this );
 
   66   mDistanceLineEdit->installEventFilter( 
this );
 
   67   mXLineEdit->installEventFilter( 
this );
 
   68   mYLineEdit->installEventFilter( 
this );
 
   69   mZLineEdit->installEventFilter( 
this );
 
   70   mMLineEdit->installEventFilter( 
this );
 
   73   connect( mEnableAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::activateCad );
 
   74   connect( mConstructionModeAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
 
   75   connect( mParallelAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::additionalConstraintClicked );
 
   76   connect( mPerpendicularAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::additionalConstraintClicked );
 
   77   connect( mLockAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   78   connect( mLockDistanceButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   79   connect( mLockXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   80   connect( mLockYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   81   connect( mLockZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   82   connect( mLockMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   83   connect( mRelativeAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   84   connect( mRelativeXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   85   connect( mRelativeYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   86   connect( mRelativeZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   87   connect( mRelativeMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   88   connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   89   connect( mRepeatingLockAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   90   connect( mRepeatingLockXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   91   connect( mRepeatingLockYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   92   connect( mRepeatingLockZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   93   connect( mRepeatingLockMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   94   connect( mAngleLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
   95   connect( mDistanceLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
   96   connect( mXLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
   97   connect( mYLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
   98   connect( mZLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
   99   connect( mMLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
  100   connect( mAngleLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  101   connect( mDistanceLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  102   connect( mXLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  103   connect( mYLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  104   connect( mZLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  105   connect( mMLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  121   QMenu *menu = 
new QMenu( 
this );
 
  123   QActionGroup *angleButtonGroup = 
new QActionGroup( menu ); 
 
  124   mCommonAngleActions = QMap<QAction *, double>();
 
  125   QList< QPair< double, QString > > commonAngles;
 
  127   const QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
 
  128   for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
 
  131       menuText = tr( 
"Do Not Snap to Common Angles" );
 
  133       menuText = QString( tr( 
"%1, %2, %3, %4°…" ) ).arg( *it, 0, 
'f', 1 ).arg( *it * 2, 0, 
'f', 1 ).arg( *it * 3, 0, 
'f', 1 ).arg( *it * 4, 0, 
'f', 1 );
 
  134     commonAngles << QPair<double, QString>( *it, menuText );
 
  136   for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
 
  138     QAction *action = 
new QAction( it->second, menu );
 
  139     action->setCheckable( 
true );
 
  140     action->setChecked( it->first == mCommonAngleConstraint );
 
  141     menu->addAction( action );
 
  142     angleButtonGroup->addAction( action );
 
  143     mCommonAngleActions.insert( action, it->first );
 
  146   qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
 
  147   mSettingsAction->setMenu( menu );
 
  148   mSettingsAction->setCheckable( 
true );
 
  149   mSettingsAction->setToolTip( tr( 
"Snap to common angles" ) );
 
  150   mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
 
  151   connect( menu, &QMenu::triggered, 
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
 
  154   mConstructionModeAction->setToolTip( 
"<b>" + tr( 
"Construction mode" ) + 
"</b><br>(" + tr( 
"press c to toggle on/off" ) + 
")" );
 
  155   mDistanceLineEdit->setToolTip( 
"<b>" + tr( 
"Distance" ) + 
"</b><br>(" + tr( 
"press d for quick access" ) + 
")" );
 
  156   mLockDistanceButton->setToolTip( 
"<b>" + tr( 
"Lock distance" ) + 
"</b><br>(" + tr( 
"press Ctrl + d for quick access" ) + 
")" );
 
  157   mRepeatingLockDistanceButton->setToolTip( 
"<b>" + tr( 
"Continuously lock distance" ) + 
"</b>" );
 
  159   mRelativeAngleButton->setToolTip( 
"<b>" + tr( 
"Toggles relative angle to previous segment" ) + 
"</b><br>(" + tr( 
"press Shift + a for quick access" ) + 
")" );
 
  160   mAngleLineEdit->setToolTip( 
"<b>" + tr( 
"Angle" ) + 
"</b><br>(" + tr( 
"press a for quick access" ) + 
")" );
 
  161   mLockAngleButton->setToolTip( 
"<b>" + tr( 
"Lock angle" ) + 
"</b><br>(" + tr( 
"press Ctrl + a for quick access" ) + 
")" );
 
  162   mRepeatingLockAngleButton->setToolTip( 
"<b>" + tr( 
"Continuously lock angle" ) + 
"</b>" );
 
  164   mRelativeXButton->setToolTip( 
"<b>" + tr( 
"Toggles relative x to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + x for quick access" ) + 
")" );
 
  165   mXLineEdit->setToolTip( 
"<b>" + tr( 
"X coordinate" ) + 
"</b><br>(" + tr( 
"press x for quick access" ) + 
")" );
 
  166   mLockXButton->setToolTip( 
"<b>" + tr( 
"Lock x coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + x for quick access" ) + 
")" );
 
  167   mRepeatingLockXButton->setToolTip( 
"<b>" + tr( 
"Continuously lock x coordinate" ) + 
"</b>" );
 
  169   mRelativeYButton->setToolTip( 
"<b>" + tr( 
"Toggles relative y to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + y for quick access" ) + 
")" );
 
  170   mYLineEdit->setToolTip( 
"<b>" + tr( 
"Y coordinate" ) + 
"</b><br>(" + tr( 
"press y for quick access" ) + 
")" );
 
  171   mLockYButton->setToolTip( 
"<b>" + tr( 
"Lock y coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + y for quick access" ) + 
")" );
 
  172   mRepeatingLockYButton->setToolTip( 
"<b>" + tr( 
"Continuously lock y coordinate" ) + 
"</b>" );
 
  174   mRelativeZButton->setToolTip( 
"<b>" + tr( 
"Toggles relative z to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + z for quick access" ) + 
")" );
 
  175   mZLineEdit->setToolTip( 
"<b>" + tr( 
"Z coordinate" ) + 
"</b><br>(" + tr( 
"press z for quick access" ) + 
")" );
 
  176   mLockZButton->setToolTip( 
"<b>" + tr( 
"Lock z coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + z for quick access" ) + 
")" );
 
  177   mRepeatingLockZButton->setToolTip( 
"<b>" + tr( 
"Continuously lock z coordinate" ) + 
"</b>" );
 
  179   mRelativeMButton->setToolTip( 
"<b>" + tr( 
"Toggles relative m to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + m for quick access" ) + 
")" );
 
  180   mMLineEdit->setToolTip( 
"<b>" + tr( 
"M coordinate" ) + 
"</b><br>(" + tr( 
"press m for quick access" ) + 
")" );
 
  181   mLockMButton->setToolTip( 
"<b>" + tr( 
"Lock m coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + m for quick access" ) + 
")" );
 
  182   mRepeatingLockMButton->setToolTip( 
"<b>" + tr( 
"Continuously lock m coordinate" ) + 
"</b>" );
 
  195   mToggleFloaterAction->setChecked( mFloater->
active() );
 
  197   updateCapacity( 
true );
 
  205   mXLineEdit->setText( value );
 
  206   if ( mode == WidgetSetMode::ReturnPressed )
 
  208     mXLineEdit->returnPressed();
 
  210   else if ( mode == WidgetSetMode::FocusOut )
 
  212     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  213     QCoreApplication::postEvent( mXLineEdit, e );
 
  215   else if ( mode == WidgetSetMode::TextEdited )
 
  217     mXLineEdit->textEdited( value );
 
  222   mYLineEdit->setText( value );
 
  223   if ( mode == WidgetSetMode::ReturnPressed )
 
  225     mYLineEdit->returnPressed();
 
  227   else if ( mode == WidgetSetMode::FocusOut )
 
  229     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  230     QCoreApplication::postEvent( mYLineEdit, e );
 
  232   else if ( mode == WidgetSetMode::TextEdited )
 
  234     mYLineEdit->textEdited( value );
 
  239   mZLineEdit->setText( value );
 
  240   if ( mode == WidgetSetMode::ReturnPressed )
 
  242     mZLineEdit->returnPressed();
 
  244   else if ( mode == WidgetSetMode::FocusOut )
 
  246     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  247     QCoreApplication::postEvent( mZLineEdit, e );
 
  249   else if ( mode == WidgetSetMode::TextEdited )
 
  251     mZLineEdit->textEdited( value );
 
  256   mMLineEdit->setText( value );
 
  257   if ( mode == WidgetSetMode::ReturnPressed )
 
  259     mMLineEdit->returnPressed();
 
  261   else if ( mode == WidgetSetMode::FocusOut )
 
  263     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  264     QCoreApplication::postEvent( mMLineEdit, e );
 
  266   else if ( mode == WidgetSetMode::TextEdited )
 
  268     mMLineEdit->textEdited( value );
 
  273   mAngleLineEdit->setText( value );
 
  274   if ( mode == WidgetSetMode::ReturnPressed )
 
  276     mAngleLineEdit->returnPressed();
 
  278   else if ( mode == WidgetSetMode::FocusOut )
 
  280     mAngleLineEdit->textEdited( value );
 
  285   mDistanceLineEdit->setText( value );
 
  286   if ( mode == WidgetSetMode::ReturnPressed )
 
  288     mDistanceLineEdit->returnPressed();
 
  290   else if ( mode == WidgetSetMode::FocusOut )
 
  292     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  293     QCoreApplication::postEvent( mDistanceLineEdit, e );
 
  295   else if ( mode == WidgetSetMode::TextEdited )
 
  297     mDistanceLineEdit->textEdited( value );
 
  302 void QgsAdvancedDigitizingDockWidget::setCadEnabled( 
bool enabled )
 
  304   mCadEnabled = enabled;
 
  305   mEnableAction->setChecked( enabled );
 
  306   mConstructionModeAction->setEnabled( enabled );
 
  307   mParallelAction->setEnabled( enabled );
 
  308   mPerpendicularAction->setEnabled( enabled );
 
  309   mSettingsAction->setEnabled( enabled );
 
  310   mInputWidgets->setEnabled( enabled );
 
  311   mToggleFloaterAction->setEnabled( enabled );
 
  314   setConstructionMode( 
false );
 
  323   bool enableZ = 
false;
 
  324   bool enableM = 
false;
 
  328     switch ( layer->type() )
 
  341         QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
 
  361   mRelativeZButton->setEnabled( 
enable );
 
  362   mZLabel->setEnabled( 
enable );
 
  363   mZLineEdit->setEnabled( 
enable );
 
  364   if ( mZLineEdit->isEnabled() )
 
  365     mZLineEdit->setText( QLocale().toString( 
QgsMapToolEdit( mMapCanvas ).defaultZValue(), 
'f', 6 ) );
 
  368   mLockZButton->setEnabled( 
enable );
 
  374   mRelativeMButton->setEnabled( 
enable );
 
  375   mMLabel->setEnabled( 
enable );
 
  376   mMLineEdit->setEnabled( 
enable );
 
  377   if ( mMLineEdit->isEnabled() )
 
  378     mMLineEdit->setText( QLocale().toString( 
QgsMapToolEdit( mMapCanvas ).defaultMValue(), 
'f', 6 ) );
 
  381   mLockMButton->setEnabled( 
enable );
 
  385 void QgsAdvancedDigitizingDockWidget::activateCad( 
bool enabled )
 
  387   enabled &= mCurrentMapToolSupportsCad;
 
  389   mSessionActive = enabled;
 
  391   if ( enabled && !isVisible() )
 
  396   setCadEnabled( enabled );
 
  399 void QgsAdvancedDigitizingDockWidget::additionalConstraintClicked( 
bool activated )
 
  405   else if ( sender() == mParallelAction )
 
  409   else if ( sender() == mPerpendicularAction )
 
  415 void QgsAdvancedDigitizingDockWidget::setConstraintRelative( 
bool activate )
 
  417   if ( sender() == mRelativeAngleButton )
 
  419     mAngleConstraint->setRelative( activate );
 
  422   else if ( sender() == mRelativeXButton )
 
  424     mXConstraint->setRelative( activate );
 
  427   else if ( sender() == mRelativeYButton )
 
  429     mYConstraint->setRelative( activate );
 
  432   else if ( sender() == mRelativeZButton )
 
  434     mZConstraint->setRelative( activate );
 
  437   else if ( sender() == mRelativeMButton )
 
  439     mMConstraint->setRelative( activate );
 
  444 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( 
bool activate )
 
  446   if ( sender() == mRepeatingLockDistanceButton )
 
  448     mDistanceConstraint->setRepeatingLock( activate );
 
  450   else if ( sender() == mRepeatingLockAngleButton )
 
  452     mAngleConstraint->setRepeatingLock( activate );
 
  454   else if ( sender() == mRepeatingLockXButton )
 
  456     mXConstraint->setRepeatingLock( activate );
 
  458   else if ( sender() == mRepeatingLockYButton )
 
  460     mYConstraint->setRepeatingLock( activate );
 
  462   else if ( sender() == mRepeatingLockZButton )
 
  464     mZConstraint->setRepeatingLock( activate );
 
  466   else if ( sender() == mRepeatingLockMButton )
 
  468     mMConstraint->setRepeatingLock( activate );
 
  472 void QgsAdvancedDigitizingDockWidget::setConstructionMode( 
bool enabled )
 
  474   mConstructionMode = enabled;
 
  475   mConstructionModeAction->setChecked( enabled );
 
  478 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
 
  481   const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
 
  482   if ( ica != mCommonAngleActions.constEnd() )
 
  484     ica.key()->setChecked( 
true );
 
  485     mCommonAngleConstraint = ica.value();
 
  487     mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
 
  492 QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
 
  496     return advancedTool->layer();
 
  510   if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
 
  515   if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
 
  520   if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
 
  525   if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
 
  530   if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
 
  535   if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
 
  541   if ( !mCadPointList.empty() )
 
  543     if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
 
  545       mXConstraint->setValue( mCadPointList.constLast().x(), 
true );
 
  547     if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
 
  549       mYConstraint->setValue( mCadPointList.constLast().y(), 
true );
 
  551     if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
 
  553       mZConstraint->setValue( mCadPointList.constLast().z(), 
true );
 
  555     if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
 
  557       mMConstraint->setValue( mCadPointList.constLast().m(), 
true );
 
  564 void QgsAdvancedDigitizingDockWidget::emit pointChanged()
 
  567   QPoint globalPos = mMapCanvas->cursor().pos();
 
  568   QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
 
  569   QMouseEvent *e = 
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
 
  570   mCurrentMapTool->canvasMoveEvent( e );
 
  576   CadConstraint *constraint = 
nullptr;
 
  577   if ( obj == mAngleLineEdit || obj == mLockAngleButton )
 
  579     constraint = mAngleConstraint.get();
 
  581   else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
 
  583     constraint = mDistanceConstraint.get();
 
  585   else if ( obj == mXLineEdit  || obj == mLockXButton )
 
  587     constraint = mXConstraint.get();
 
  589   else if ( obj == mYLineEdit  || obj == mLockYButton )
 
  591     constraint = mYConstraint.get();
 
  593   else if ( obj == mZLineEdit  || obj == mLockZButton )
 
  595     constraint = mZConstraint.get();
 
  597   else if ( obj == mMLineEdit  || obj == mLockMButton )
 
  599     constraint = mMConstraint.get();
 
  604 double QgsAdvancedDigitizingDockWidget::parseUserInput( 
const QString &inputValue, 
bool &ok )
 const 
  616     const QVariant result = expr.evaluate();
 
  617     if ( expr.hasEvalError() )
 
  620       value = result.toDouble( &ok );
 
  625 void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, 
const QString &textValue, 
bool convertExpression )
 
  627   if ( !constraint || textValue.isEmpty() )
 
  636   const double value = parseUserInput( textValue, ok );
 
  640   constraint->setValue( value, convertExpression );
 
  645 void QgsAdvancedDigitizingDockWidget::lockConstraint( 
bool activate  )
 
  647   CadConstraint *constraint = objectToConstraint( sender() );
 
  655     const QString textValue = constraint->lineEdit()->text();
 
  656     if ( !textValue.isEmpty() )
 
  659       const double value = parseUserInput( textValue, ok );
 
  662         constraint->setValue( value );
 
  676   if ( constraint == mXConstraint.get() )
 
  680   else if ( constraint == mYConstraint.get() )
 
  684   else if ( constraint == mZConstraint.get() )
 
  688   else if ( constraint == mMConstraint.get() )
 
  692   else if ( constraint == mDistanceConstraint.get() )
 
  696   else if ( constraint == mAngleConstraint.get() )
 
  704     if ( constraint == mAngleConstraint.get() )
 
  714 void QgsAdvancedDigitizingDockWidget::constraintTextEdited( 
const QString &textValue )
 
  716   CadConstraint *constraint = objectToConstraint( sender() );
 
  722   updateConstraintValue( constraint, textValue, 
false );
 
  725 void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
 
  727   QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
 
  731   CadConstraint *constraint = objectToConstraint( lineEdit );
 
  737   updateConstraintValue( constraint, lineEdit->text(), 
true );
 
  740 void QgsAdvancedDigitizingDockWidget::lockAdditionalConstraint( AdditionalConstraint constraint )
 
  742   mAdditionalConstraint = constraint;
 
  747 void QgsAdvancedDigitizingDockWidget::updateCapacity( 
bool updateUIwithoutChange )
 
  749   CadCapacities newCapacities = CadCapacities();
 
  755   if ( mCadPointList.count() > 1 )
 
  761   if ( mCadPointList.count() > 2 )
 
  766   if ( !updateUIwithoutChange && newCapacities == mCapacities )
 
  776   const bool distance =  mCadEnabled && newCapacities.testFlag( 
Distance );
 
  777   const bool relativeAngle = mCadEnabled && newCapacities.testFlag( 
RelativeAngle );
 
  778   const bool absoluteAngle = mCadEnabled && newCapacities.testFlag( 
AbsoluteAngle );
 
  779   const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( 
RelativeCoordinates );
 
  781   mPerpendicularAction->setEnabled( distance && absoluteAngle && snappingEnabled );
 
  782   mParallelAction->setEnabled( distance && absoluteAngle && snappingEnabled );
 
  785   if ( !snappingEnabled )
 
  787     mPerpendicularAction->setToolTip( tr( 
"Snapping must be enabled to utilize perpendicular mode" ) );
 
  788     mParallelAction->setToolTip( tr( 
"Snapping must be enabled to utilize parallel mode" ) );
 
  792     mPerpendicularAction->setToolTip( 
"<b>" + tr( 
"Perpendicular" ) + 
"</b><br>(" + tr( 
"press p to switch between perpendicular, parallel and normal mode" ) + 
")" );
 
  793     mParallelAction->setToolTip( 
"<b>" + tr( 
"Parallel" ) + 
"</b><br>(" + tr( 
"press p to switch between perpendicular, parallel and normal mode" ) + 
")" );
 
  797   if ( !absoluteAngle )
 
  803   mLockAngleButton->setEnabled( absoluteAngle );
 
  804   mRelativeAngleButton->setEnabled( relativeAngle );
 
  805   mAngleLineEdit->setEnabled( absoluteAngle );
 
  807   if ( !absoluteAngle )
 
  811   if ( !relativeAngle )
 
  813     mAngleConstraint->setRelative( 
false );
 
  816   else if ( relativeAngle && !mCapacities.testFlag( 
RelativeAngle ) )
 
  819     mAngleConstraint->setRelative( 
true );
 
  824   mLockDistanceButton->setEnabled( distance && relativeCoordinates );
 
  825   mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
 
  827   if ( !( distance && relativeCoordinates ) )
 
  832   mRelativeXButton->setEnabled( relativeCoordinates );
 
  833   mRelativeYButton->setEnabled( relativeCoordinates );
 
  834   mRelativeZButton->setEnabled( relativeCoordinates );
 
  835   mRelativeMButton->setEnabled( relativeCoordinates );
 
  838   mCapacities = newCapacities;
 
  847   constr.
value = 
c->value();
 
  856   context.
xConstraint = _constraint( mXConstraint.get() );
 
  857   context.
yConstraint = _constraint( mYConstraint.get() );
 
  858   context.
zConstraint = _constraint( mZConstraint.get() );
 
  859   context.
mConstraint = _constraint( mMConstraint.get() );
 
  870   const bool res = output.
valid;
 
  872   mSnappedSegment.clear();
 
  877     mSnappedSegment << edgePt0 << edgePt1;
 
  894     mSnapIndicator->setMatch( output.
snapMatch );
 
  895     mSnapIndicator->setVisible( 
true );
 
  899     mSnapIndicator->setVisible( 
false );
 
  928   if ( mLockZButton->isChecked() )
 
  930     point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
 
  932   if ( mLockMButton->isChecked() )
 
  934     point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
 
  938   updateCurrentPoint( point );
 
  940   updateUnlockedConstraintValues( point );
 
  948     emit 
pushWarning( tr( 
"Some constraints are incompatible. Resulting point might be incorrect." ) );
 
  955 void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( 
const QgsPoint &point )
 
  957   bool previousPointExist, penulPointExist;
 
  962   if ( !mAngleConstraint->isLocked() && previousPointExist )
 
  965     if ( penulPointExist && mAngleConstraint->relative() )
 
  968       angle = std::atan2( previousPt.
y() - penultimatePt.
y(),
 
  969                           previousPt.
x() - penultimatePt.
x() );
 
  971     angle = ( std::atan2( point.
y() - previousPt.
y(),
 
  972                           point.
x() - previousPt.
x()
 
  973                         ) - 
angle ) * 180 / M_PI;
 
  976     mAngleConstraint->setValue( 
angle );
 
  979   if ( !mDistanceConstraint->isLocked() && previousPointExist )
 
  981     mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
 
  984   if ( !mXConstraint->isLocked() )
 
  986     if ( previousPointExist && mXConstraint->relative() )
 
  988       mXConstraint->setValue( point.
x() - previousPt.
x() );
 
  992       mXConstraint->setValue( point.
x() );
 
  996   if ( !mYConstraint->isLocked() )
 
  998     if ( previousPointExist && mYConstraint->relative() )
 
 1000       mYConstraint->setValue( point.
y() - previousPt.
y() );
 
 1004       mYConstraint->setValue( point.
y() );
 
 1008   if ( !mZConstraint->isLocked() )
 
 1010     if ( previousPointExist && mZConstraint->relative() )
 
 1012       mZConstraint->setValue( point.
z() - previousPt.
z() );
 
 1016       mZConstraint->setValue( point.
z() );
 
 1020   if ( !mMConstraint->isLocked() )
 
 1022     if ( previousPointExist && mMConstraint->relative() )
 
 1024       mMConstraint->setValue( point.
m() - previousPt.
m() );
 
 1028       mMConstraint->setValue( point.
m() );
 
 1034 QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( 
const QgsPointXY &originalMapPoint, 
bool *snapped )
 const 
 1047   snappingUtils->
setConfig( localConfig );
 
 1049   match = snappingUtils->
snapToMap( originalMapPoint, 
nullptr, 
true );
 
 1051   snappingUtils->
setConfig( canvasConfig );
 
 1061     *snapped = 
segment.count() == 2;
 
 1074   bool previousPointExist, penulPointExist, snappedSegmentExist;
 
 1077   mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
 
 1079   if ( !previousPointExist || !snappedSegmentExist )
 
 1084   double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
 
 1086   if ( mAngleConstraint->relative() && penulPointExist )
 
 1088     angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
 
 1096   angle *= 180 / M_PI;
 
 1098   mAngleConstraint->setValue( 
angle );
 
 1099   mAngleConstraint->setLockMode( lockMode );
 
 1117     case Qt::Key_Backspace:
 
 1118     case Qt::Key_Delete:
 
 1124     case Qt::Key_Escape:
 
 1154     case Qt::Key_Backspace:
 
 1155     case Qt::Key_Delete:
 
 1161     case Qt::Key_Escape:
 
 1168       filterKeyPress( e );
 
 1177   const auto constPoints = points;
 
 1184 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
 
 1188     return QgsDockWidget::eventFilter( obj, event );
 
 1196   if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
 
 1198     if ( QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( event ) )
 
 1200       return filterKeyPress( keyEvent );
 
 1203   return QgsDockWidget::eventFilter( obj, event );
 
 1206 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
 
 1212   const QEvent::Type type = e->type();
 
 1218       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1220         mXConstraint->toggleLocked();
 
 1225       else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1229           mXConstraint->toggleRelative();
 
 1236       else if ( type == QEvent::KeyPress )
 
 1238         mXLineEdit->setFocus();
 
 1239         mXLineEdit->selectAll();
 
 1248       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1250         mYConstraint->toggleLocked();
 
 1255       else if ( type == QEvent::ShortcutOverride &&  e->modifiers() == Qt::ShiftModifier )
 
 1259           mYConstraint->toggleRelative();
 
 1266       else if ( type == QEvent::KeyPress )
 
 1268         mYLineEdit->setFocus();
 
 1269         mYLineEdit->selectAll();
 
 1278       if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
 
 1280         mZConstraint->toggleLocked();
 
 1285       else if ( type == QEvent::ShortcutOverride &&  e->modifiers() == Qt::ShiftModifier )
 
 1289           mZConstraint->toggleRelative();
 
 1296       else if ( type == QEvent::KeyPress )
 
 1298         mZLineEdit->setFocus();
 
 1299         mZLineEdit->selectAll();
 
 1308       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1310         mMConstraint->toggleLocked();
 
 1315       else if ( type == QEvent::ShortcutOverride &&  e->modifiers() == Qt::ShiftModifier )
 
 1319           mMConstraint->toggleRelative();
 
 1326       else if ( type == QEvent::KeyPress )
 
 1328         mMLineEdit->setFocus();
 
 1329         mMLineEdit->selectAll();
 
 1338       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1342           mAngleConstraint->toggleLocked();
 
 1348       else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1352           mAngleConstraint->toggleRelative();
 
 1359       else if ( type == QEvent::KeyPress )
 
 1361         mAngleLineEdit->setFocus();
 
 1362         mAngleLineEdit->selectAll();
 
 1371       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1375           mDistanceConstraint->toggleLocked();
 
 1382       else if ( type == QEvent::KeyPress )
 
 1384         mDistanceLineEdit->setFocus();
 
 1385         mDistanceLineEdit->selectAll();
 
 1393       if ( type == QEvent::KeyPress )
 
 1395         setConstructionMode( !mConstructionMode );
 
 1402       if ( type == QEvent::KeyPress )
 
 1404         const bool parallel = mParallelAction->isChecked();
 
 1405         const bool perpendicular = mPerpendicularAction->isChecked();
 
 1407         if ( !parallel && !perpendicular )
 
 1411         else if ( perpendicular )
 
 1431   return e->isAccepted();
 
 1439     mAngleLineEdit->setEnabled( 
false );
 
 1440     mAngleLineEdit->setToolTip( tr( 
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 1442     mDistanceLineEdit->setEnabled( 
false );
 
 1443     mDistanceLineEdit->setToolTip( tr( 
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 1445     mLabelX->setText( tr( 
"Long" ) );
 
 1446     mLabelY->setText( tr( 
"Lat" ) );
 
 1448     mXConstraint->setPrecision( 8 );
 
 1449     mYConstraint->setPrecision( 8 );
 
 1453     mAngleLineEdit->setToolTip( 
"<b>" + tr( 
"Angle" ) + 
"</b><br>(" + tr( 
"press a for quick access" ) + 
")" );
 
 1454     mAngleLineEdit->setToolTip( QString() );
 
 1456     mDistanceLineEdit->setEnabled( 
true );
 
 1457     mDistanceLineEdit->setToolTip( 
"<b>" + tr( 
"Distance" ) + 
"</b><br>(" + tr( 
"press d for quick access" ) + 
")" );
 
 1459     mLabelX->setText( tr( 
"x" ) );
 
 1460     mLabelY->setText( tr( 
"y" ) );
 
 1462     mXConstraint->setPrecision( 6 );
 
 1463     mYConstraint->setPrecision( 6 );
 
 1466   mEnableAction->setEnabled( 
true );
 
 1467   mErrorLabel->hide();
 
 1470   mCurrentMapToolSupportsCad = 
true;
 
 1472   if ( mSessionActive && !isVisible() )
 
 1476   setCadEnabled( mSessionActive );
 
 1483   mEnableAction->setEnabled( 
false );
 
 1484   mErrorLabel->setText( tr( 
"CAD tools are not enabled for the current map tool" ) );
 
 1485   mErrorLabel->show();
 
 1488   mCurrentMapToolSupportsCad = 
false;
 
 1490   setCadEnabled( 
false );
 
 1495   mCadPaintItem->update();
 
 1500   QgsPoint pt = pointXYToPoint( point );
 
 1503     mCadPointList << pt;
 
 1507     mCadPointList.insert( 0, pt );
 
 1520   mCadPointList.removeAt( i );
 
 1527   mCadPointList.clear();
 
 1528   mSnappedSegment.clear();
 
 1534 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint( 
const QgsPoint &point )
 
 1538     mCadPointList << point;
 
 1543     mCadPointList[0] = point;
 
 1552   mLockerButton->setChecked( mode == 
HardLock );
 
 1553   if ( mRepeatingLockButton )
 
 1557       mRepeatingLockButton->setEnabled( 
true );
 
 1561       mRepeatingLockButton->setChecked( 
false );
 
 1562       mRepeatingLockButton->setEnabled( 
false );
 
 1575   mRepeatingLock = repeating;
 
 1576   if ( mRepeatingLockButton )
 
 1577     mRepeatingLockButton->setChecked( repeating );
 
 1582   mRelative = relative;
 
 1583   if ( mRelativeButton )
 
 1585     mRelativeButton->setChecked( relative );
 
 1592   if ( updateWidget && mLineEdit->isEnabled() )
 
 1593     mLineEdit->setText( QLocale().toString( value, 
'f', mPrecision ) );
 
 1598   setLockMode( mLockMode == HardLock ? NoLock : HardLock );
 
 1603   setRelative( !mRelative );
 
 1609   if ( mLineEdit->isEnabled() )
 
 1610     mLineEdit->setText( QLocale().toString( mValue, 
'f', mPrecision ) );
 
 1618     return mCadPointList.value( 0 );
 
 1627     QgsPoint res = mCadPointList.value( 0 );
 
 1629     res.
setX( layerCoordinates.
x() );
 
 1630     res.
setY( layerCoordinates.
y() );
 
 1641     return mCadPointList.value( 1 );
 
 1651     return mCadPointList.value( 2 );
 
 1656 QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint( 
const QgsPointXY &point )
 const 
 1663   return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
 
 1668   return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
 
The QgsAdvancedDigitizingCanvasItem class draws the graphical elements of the CAD tools (.
The QgsAdvancedDigitizingFloater class is widget that floats next to the mouse pointer,...
void setActive(bool active)
Set whether the floater should be active or not.
bool active()
Whether the floater is active or not.
Structure with details of one constraint.
bool locked
Whether the constraint is active, i.e. should be considered.
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
bool relative
Whether the value is relative to previous value.
Defines constraints for the QgsCadUtils::alignMapPoint() method.
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
void setCadPoints(const QList< QgsPoint > &points)
Sets the list of recent CAD points (in map coordinates).
QgsCadUtils::AlignMapPointConstraint mConstraint
Constraint for M coordinate.
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
QgsCadUtils::AlignMapPointConstraint zConstraint
Constraint for Z coordinate.
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be nullptr.
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Structure returned from alignMapPoint() method.
QgsPointXY finalMapPoint
map point aligned according to the constraints
bool valid
Whether the combination of constraints is actually valid.
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
static QgsCadUtils::AlignMapPointOutput alignMapPoint(const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx)
Applies X/Y/angle/distance constraints from the given context to a map point.
Class for parsing and evaluation of expressions (formerly called "search strings").
A event filter for watching for focus events on a parent object.
void focusOut()
Emitted when parent object loses focus.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
void destinationCrsChanged()
Emitted when map CRS has changed.
QgsMapTool * mapTool()
Returns the currently active tool.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY originalMapPoint() const
Returns the original, unmodified map point of the mouse cursor.
void setMapPoint(const QgsPointXY &point)
Set the (snapped) point this event points to in map coordinates.
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
bool isEditable() const override
Returns true if the layer can be edited.
A class to represent a 2D point.
Point geometry type, with support for z-dimension and m-values.
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
double distanceSquared(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsSnappingConfig snappingConfig
This class is a composition of two QSettings instances:
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Class that shows snapping marker on map canvas for the current snapping match.
This is a container for configuration of the snapping of the project.
@ AllLayers
On all vector layers.
@ SegmentFlag
On segments.
void setTypeFlag(QgsSnappingConfig::SnappingTypeFlag type)
define the type of snapping
void setMode(SnappingMode mode)
define the mode of snapping
bool enabled() const
Returns if snapping is enabled.
This class has all the configuration of snapping and can return answers to snapping queries.
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Type
The WKB type describes the number of dimensions a geometry has.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
@ PointCloudLayer
Added in 3.18.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
double qgsPermissiveToDouble(QString string, bool &ok)
Converts a string to a double in a permissive way, e.g., allowing for incorrect numbers of digits bet...
QLineF segment(int index, QRectF rect, double radius)
QgsPoint interpolatedPoint(const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem()) const
Convenient method to return a point on an edge with linear interpolation of the Z value.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
bool hasEdge() const
Returns true if the Match is an edge.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
bool hasLineEndpoint() const
Returns true if the Match is a line endpoint (start or end vertex).
bool hasVertex() const
Returns true if the Match is a vertex.