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 ) );
 
   63   mLineExtensionConstraint.reset( 
new CadConstraint( 
new QLineEdit(), 
new QToolButton() ) );
 
   64   mXyVertexConstraint.reset( 
new CadConstraint( 
new QLineEdit(), 
new QToolButton() ) );
 
   68   mMapCanvas->installEventFilter( 
this );
 
   69   mAngleLineEdit->installEventFilter( 
this );
 
   70   mDistanceLineEdit->installEventFilter( 
this );
 
   71   mXLineEdit->installEventFilter( 
this );
 
   72   mYLineEdit->installEventFilter( 
this );
 
   73   mZLineEdit->installEventFilter( 
this );
 
   74   mMLineEdit->installEventFilter( 
this );
 
   77   connect( mEnableAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::activateCad );
 
   78   connect( mConstructionModeAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
 
   79   connect( mParallelAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
 
   80   connect( mPerpendicularAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
 
   81   connect( mLockAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   82   connect( mLockDistanceButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   83   connect( mLockXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   84   connect( mLockYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   85   connect( mLockZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   86   connect( mLockMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
   87   connect( mRelativeAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   88   connect( mRelativeXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   89   connect( mRelativeYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   90   connect( mRelativeZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   91   connect( mRelativeMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
   92   connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   93   connect( mRepeatingLockAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   94   connect( mRepeatingLockXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   95   connect( mRepeatingLockYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   96   connect( mRepeatingLockZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   97   connect( mRepeatingLockMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
   98   connect( mAngleLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
   99   connect( mDistanceLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
  100   connect( mXLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
  101   connect( mYLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
  102   connect( mZLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
  103   connect( mMLineEdit, &QLineEdit::returnPressed, 
this, [ = ]() { lockConstraint(); } );
 
  104   connect( mAngleLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  105   connect( mDistanceLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  106   connect( mXLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  107   connect( mYLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  108   connect( mZLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  109   connect( mMLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  125   QMenu *menu = 
new QMenu( 
this );
 
  127   QActionGroup *angleButtonGroup = 
new QActionGroup( menu ); 
 
  128   mCommonAngleActions = QMap<QAction *, double>();
 
  129   QList< QPair< double, QString > > commonAngles;
 
  131   const QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
 
  132   for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
 
  135       menuText = tr( 
"Do Not Snap to Common Angles" );
 
  137       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 );
 
  138     commonAngles << QPair<double, QString>( *it, menuText );
 
  140   for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
 
  142     QAction *action = 
new QAction( it->second, menu );
 
  143     action->setCheckable( 
true );
 
  144     action->setChecked( it->first == mCommonAngleConstraint );
 
  145     menu->addAction( action );
 
  146     angleButtonGroup->addAction( action );
 
  147     mCommonAngleActions.insert( action, it->first );
 
  150   qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
 
  151   mSettingsAction->setMenu( menu );
 
  152   mSettingsAction->setCheckable( 
true );
 
  153   mSettingsAction->setToolTip( tr( 
"Snap to common angles" ) );
 
  154   mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
 
  155   connect( menu, &QMenu::triggered, 
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
 
  158   QMenu *constructionMenu = 
new QMenu( 
this );
 
  160   mLineExtensionAction = 
new QAction( 
"Line Extension", constructionMenu );
 
  161   mLineExtensionAction->setCheckable( 
true );
 
  162   constructionMenu->addAction( mLineExtensionAction );
 
  163   connect( mLineExtensionAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
 
  165   mXyVertexAction = 
new QAction( 
"X/Y Point", constructionMenu );
 
  166   mXyVertexAction->setCheckable( 
true );
 
  167   constructionMenu->addAction( mXyVertexAction );
 
  168   connect( mXyVertexAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
 
  170   auto constructionToolBar = qobject_cast< QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
 
  171   constructionToolBar->setPopupMode( QToolButton::InstantPopup );
 
  172   constructionToolBar->setMenu( constructionMenu );
 
  173   constructionToolBar->setObjectName( QStringLiteral( 
"ConstructionButton" ) );
 
  175   mConstructionAction->setMenu( menu );
 
  176   mConstructionAction->setCheckable( 
true );
 
  177   mConstructionAction->setToolTip( tr( 
"Construction Tools" ) );
 
  181   mConstructionModeAction->setToolTip( 
"<b>" + tr( 
"Construction mode" ) + 
"</b><br>(" + tr( 
"press c to toggle on/off" ) + 
")" );
 
  182   mDistanceLineEdit->setToolTip( 
"<b>" + tr( 
"Distance" ) + 
"</b><br>(" + tr( 
"press d for quick access" ) + 
")" );
 
  183   mLockDistanceButton->setToolTip( 
"<b>" + tr( 
"Lock distance" ) + 
"</b><br>(" + tr( 
"press Ctrl + d for quick access" ) + 
")" );
 
  184   mRepeatingLockDistanceButton->setToolTip( 
"<b>" + tr( 
"Continuously lock distance" ) + 
"</b>" );
 
  186   mRelativeAngleButton->setToolTip( 
"<b>" + tr( 
"Toggles relative angle to previous segment" ) + 
"</b><br>(" + tr( 
"press Shift + a for quick access" ) + 
")" );
 
  187   mAngleLineEdit->setToolTip( 
"<b>" + tr( 
"Angle" ) + 
"</b><br>(" + tr( 
"press a for quick access" ) + 
")" );
 
  188   mLockAngleButton->setToolTip( 
"<b>" + tr( 
"Lock angle" ) + 
"</b><br>(" + tr( 
"press Ctrl + a for quick access" ) + 
")" );
 
  189   mRepeatingLockAngleButton->setToolTip( 
"<b>" + tr( 
"Continuously lock angle" ) + 
"</b>" );
 
  191   mRelativeXButton->setToolTip( 
"<b>" + tr( 
"Toggles relative x to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + x for quick access" ) + 
")" );
 
  192   mXLineEdit->setToolTip( 
"<b>" + tr( 
"X coordinate" ) + 
"</b><br>(" + tr( 
"press x for quick access" ) + 
")" );
 
  193   mLockXButton->setToolTip( 
"<b>" + tr( 
"Lock x coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + x for quick access" ) + 
")" );
 
  194   mRepeatingLockXButton->setToolTip( 
"<b>" + tr( 
"Continuously lock x coordinate" ) + 
"</b>" );
 
  196   mRelativeYButton->setToolTip( 
"<b>" + tr( 
"Toggles relative y to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + y for quick access" ) + 
")" );
 
  197   mYLineEdit->setToolTip( 
"<b>" + tr( 
"Y coordinate" ) + 
"</b><br>(" + tr( 
"press y for quick access" ) + 
")" );
 
  198   mLockYButton->setToolTip( 
"<b>" + tr( 
"Lock y coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + y for quick access" ) + 
")" );
 
  199   mRepeatingLockYButton->setToolTip( 
"<b>" + tr( 
"Continuously lock y coordinate" ) + 
"</b>" );
 
  201   mRelativeZButton->setToolTip( 
"<b>" + tr( 
"Toggles relative z to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + z for quick access" ) + 
")" );
 
  202   mZLineEdit->setToolTip( 
"<b>" + tr( 
"Z coordinate" ) + 
"</b><br>(" + tr( 
"press z for quick access" ) + 
")" );
 
  203   mLockZButton->setToolTip( 
"<b>" + tr( 
"Lock z coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + z for quick access" ) + 
")" );
 
  204   mRepeatingLockZButton->setToolTip( 
"<b>" + tr( 
"Continuously lock z coordinate" ) + 
"</b>" );
 
  206   mRelativeMButton->setToolTip( 
"<b>" + tr( 
"Toggles relative m to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + m for quick access" ) + 
")" );
 
  207   mMLineEdit->setToolTip( 
"<b>" + tr( 
"M coordinate" ) + 
"</b><br>(" + tr( 
"press m for quick access" ) + 
")" );
 
  208   mLockMButton->setToolTip( 
"<b>" + tr( 
"Lock m coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + m for quick access" ) + 
")" );
 
  209   mRepeatingLockMButton->setToolTip( 
"<b>" + tr( 
"Continuously lock m coordinate" ) + 
"</b>" );
 
  222   mToggleFloaterAction->setChecked( mFloater->
active() );
 
  224   updateCapacity( 
true );
 
  232   mXLineEdit->setText( value );
 
  233   if ( mode == WidgetSetMode::ReturnPressed )
 
  235     emit mXLineEdit->returnPressed();
 
  237   else if ( mode == WidgetSetMode::FocusOut )
 
  239     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  240     QCoreApplication::postEvent( mXLineEdit, e );
 
  242   else if ( mode == WidgetSetMode::TextEdited )
 
  244     emit mXLineEdit->textEdited( value );
 
  249   mYLineEdit->setText( value );
 
  250   if ( mode == WidgetSetMode::ReturnPressed )
 
  252     emit mYLineEdit->returnPressed();
 
  254   else if ( mode == WidgetSetMode::FocusOut )
 
  256     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  257     QCoreApplication::postEvent( mYLineEdit, e );
 
  259   else if ( mode == WidgetSetMode::TextEdited )
 
  261     emit mYLineEdit->textEdited( value );
 
  266   mZLineEdit->setText( value );
 
  267   if ( mode == WidgetSetMode::ReturnPressed )
 
  269     emit mZLineEdit->returnPressed();
 
  271   else if ( mode == WidgetSetMode::FocusOut )
 
  273     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  274     QCoreApplication::postEvent( mZLineEdit, e );
 
  276   else if ( mode == WidgetSetMode::TextEdited )
 
  278     emit mZLineEdit->textEdited( value );
 
  283   mMLineEdit->setText( value );
 
  284   if ( mode == WidgetSetMode::ReturnPressed )
 
  286     emit mMLineEdit->returnPressed();
 
  288   else if ( mode == WidgetSetMode::FocusOut )
 
  290     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  291     QCoreApplication::postEvent( mMLineEdit, e );
 
  293   else if ( mode == WidgetSetMode::TextEdited )
 
  295     emit mMLineEdit->textEdited( value );
 
  300   mAngleLineEdit->setText( value );
 
  301   if ( mode == WidgetSetMode::ReturnPressed )
 
  303     emit mAngleLineEdit->returnPressed();
 
  305   else if ( mode == WidgetSetMode::FocusOut )
 
  307     emit mAngleLineEdit->textEdited( value );
 
  312   mDistanceLineEdit->setText( value );
 
  313   if ( mode == WidgetSetMode::ReturnPressed )
 
  315     emit mDistanceLineEdit->returnPressed();
 
  317   else if ( mode == WidgetSetMode::FocusOut )
 
  319     QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  320     QCoreApplication::postEvent( mDistanceLineEdit, e );
 
  322   else if ( mode == WidgetSetMode::TextEdited )
 
  324     emit mDistanceLineEdit->textEdited( value );
 
  329 void QgsAdvancedDigitizingDockWidget::setCadEnabled( 
bool enabled )
 
  331   mCadEnabled = enabled;
 
  332   mEnableAction->setChecked( enabled );
 
  333   mConstructionModeAction->setEnabled( enabled );
 
  334   mSettingsAction->setEnabled( enabled );
 
  335   mInputWidgets->setEnabled( enabled );
 
  336   mToggleFloaterAction->setEnabled( enabled );
 
  337   mConstructionAction->setEnabled( enabled );
 
  342     mLineExtensionAction->setChecked( 
false );
 
  343     mXyVertexAction->setChecked( 
false );
 
  345     mParallelAction->setEnabled( 
false );
 
  346     mPerpendicularAction->setEnabled( 
false );
 
  352   setConstructionMode( 
false );
 
  363   bool enableZ = 
false;
 
  364   bool enableM = 
false;
 
  368     switch ( layer->type() )
 
  381         QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
 
  402   mRelativeZButton->setEnabled( 
enable );
 
  403   mZLabel->setEnabled( 
enable );
 
  404   mZLineEdit->setEnabled( 
enable );
 
  405   if ( mZLineEdit->isEnabled() )
 
  409   mLockZButton->setEnabled( 
enable );
 
  415   mRelativeMButton->setEnabled( 
enable );
 
  416   mMLabel->setEnabled( 
enable );
 
  417   mMLineEdit->setEnabled( 
enable );
 
  418   if ( mMLineEdit->isEnabled() )
 
  422   mLockMButton->setEnabled( 
enable );
 
  426 void QgsAdvancedDigitizingDockWidget::activateCad( 
bool enabled )
 
  428   enabled &= mCurrentMapToolSupportsCad;
 
  430   mSessionActive = enabled;
 
  432   if ( enabled && !isVisible() )
 
  437   setCadEnabled( enabled );
 
  440 void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked( 
bool activated )
 
  446   else if ( sender() == mParallelAction )
 
  450   else if ( sender() == mPerpendicularAction )
 
  456 void QgsAdvancedDigitizingDockWidget::setConstraintRelative( 
bool activate )
 
  458   if ( sender() == mRelativeAngleButton )
 
  460     mAngleConstraint->setRelative( activate );
 
  463   else if ( sender() == mRelativeXButton )
 
  465     mXConstraint->setRelative( activate );
 
  468   else if ( sender() == mRelativeYButton )
 
  470     mYConstraint->setRelative( activate );
 
  473   else if ( sender() == mRelativeZButton )
 
  475     mZConstraint->setRelative( activate );
 
  478   else if ( sender() == mRelativeMButton )
 
  480     mMConstraint->setRelative( activate );
 
  485 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( 
bool activate )
 
  487   if ( sender() == mRepeatingLockDistanceButton )
 
  489     mDistanceConstraint->setRepeatingLock( activate );
 
  491   else if ( sender() == mRepeatingLockAngleButton )
 
  493     mAngleConstraint->setRepeatingLock( activate );
 
  495   else if ( sender() == mRepeatingLockXButton )
 
  497     mXConstraint->setRepeatingLock( activate );
 
  499   else if ( sender() == mRepeatingLockYButton )
 
  501     mYConstraint->setRepeatingLock( activate );
 
  503   else if ( sender() == mRepeatingLockZButton )
 
  505     mZConstraint->setRepeatingLock( activate );
 
  507   else if ( sender() == mRepeatingLockMButton )
 
  509     mMConstraint->setRepeatingLock( activate );
 
  513 void QgsAdvancedDigitizingDockWidget::setConstructionMode( 
bool enabled )
 
  515   mConstructionMode = enabled;
 
  516   mConstructionModeAction->setChecked( enabled );
 
  519 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
 
  522   const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
 
  523   if ( ica != mCommonAngleActions.constEnd() )
 
  525     ica.key()->setChecked( 
true );
 
  526     mCommonAngleConstraint = ica.value();
 
  528     mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
 
  533 QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
 const 
  537     return advancedTool->layer();
 
  551   if ( releaseRepeatingLocks )
 
  553     mXyVertexAction->setChecked( 
false );
 
  557     mLineExtensionAction->setChecked( 
false );
 
  564   if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
 
  569   if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
 
  574   if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
 
  579   if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
 
  584   if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
 
  589   if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
 
  595   if ( !mCadPointList.empty() )
 
  597     if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
 
  599       mXConstraint->setValue( mCadPointList.constLast().x(), 
true );
 
  601     if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
 
  603       mYConstraint->setValue( mCadPointList.constLast().y(), 
true );
 
  605     if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
 
  607       mZConstraint->setValue( mCadPointList.constLast().z(), 
true );
 
  609     if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
 
  611       mMConstraint->setValue( mCadPointList.constLast().m(), 
true );
 
  618 void QgsAdvancedDigitizingDockWidget::emit pointChanged()
 
  621   QPoint globalPos = mMapCanvas->cursor().pos();
 
  622   QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
 
  623   QMouseEvent *e = 
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
 
  624   mCurrentMapTool->canvasMoveEvent( e );
 
  630   CadConstraint *constraint = 
nullptr;
 
  631   if ( obj == mAngleLineEdit || obj == mLockAngleButton )
 
  633     constraint = mAngleConstraint.get();
 
  635   else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
 
  637     constraint = mDistanceConstraint.get();
 
  639   else if ( obj == mXLineEdit  || obj == mLockXButton )
 
  641     constraint = mXConstraint.get();
 
  643   else if ( obj == mYLineEdit  || obj == mLockYButton )
 
  645     constraint = mYConstraint.get();
 
  647   else if ( obj == mZLineEdit  || obj == mLockZButton )
 
  649     constraint = mZConstraint.get();
 
  651   else if ( obj == mMLineEdit  || obj == mLockMButton )
 
  653     constraint = mMConstraint.get();
 
  655   else if ( obj == mLineExtensionAction )
 
  657     constraint = mLineExtensionConstraint.get();
 
  659   else if ( obj == mXyVertexAction )
 
  661     constraint = mXyVertexConstraint.get();
 
  666 double QgsAdvancedDigitizingDockWidget::parseUserInput( 
const QString &inputValue, 
bool &ok )
 const 
  678     const QVariant result = expr.evaluate();
 
  679     if ( expr.hasEvalError() )
 
  682       QString inputValueC { inputValue };
 
  685       if ( inputValue.contains( QLocale().groupSeparator() ) )
 
  687         inputValueC.remove( QLocale().groupSeparator() );
 
  689         const QVariant resultC = exprC.evaluate();
 
  690         if ( ! exprC.hasEvalError() )
 
  692           value = resultC.toDouble( &ok );
 
  697       if ( !ok && QLocale().decimalPoint() != QChar( 
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
 
  699         QgsExpression exprC( inputValueC .replace( QLocale().decimalPoint(), QChar( 
'.' ) ) );
 
  700         const QVariant resultC = exprC.evaluate();
 
  701         if ( ! exprC.hasEvalError() )
 
  703           value = resultC.toDouble( &ok );
 
  709       value = result.toDouble( &ok );
 
  715 void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, 
const QString &textValue, 
bool convertExpression )
 
  717   if ( !constraint || textValue.isEmpty() )
 
  726   const double value = parseUserInput( textValue, ok );
 
  730   constraint->setValue( value, convertExpression );
 
  735 void QgsAdvancedDigitizingDockWidget::lockConstraint( 
bool activate  )
 
  737   CadConstraint *constraint = objectToConstraint( sender() );
 
  745     const QString textValue = constraint->lineEdit()->text();
 
  746     if ( !textValue.isEmpty() )
 
  749       const double value = parseUserInput( textValue, ok );
 
  752         constraint->setValue( value );
 
  766   if ( constraint == mXConstraint.get() )
 
  770   else if ( constraint == mYConstraint.get() )
 
  774   else if ( constraint == mZConstraint.get() )
 
  778   else if ( constraint == mMConstraint.get() )
 
  782   else if ( constraint == mDistanceConstraint.get() )
 
  786   else if ( constraint == mAngleConstraint.get() )
 
  794     if ( constraint == mAngleConstraint.get() )
 
  804 void QgsAdvancedDigitizingDockWidget::constraintTextEdited( 
const QString &textValue )
 
  806   CadConstraint *constraint = objectToConstraint( sender() );
 
  812   updateConstraintValue( constraint, textValue, 
false );
 
  815 void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
 
  817   QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
 
  821   CadConstraint *constraint = objectToConstraint( lineEdit );
 
  827   updateConstraintValue( constraint, lineEdit->text(), 
true );
 
  832   mBetweenLineConstraint = constraint;
 
  837 void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint( 
bool activate  )
 
  839   CadConstraint *constraint = objectToConstraint( sender() );
 
  847   if ( constraint == mXyVertexConstraint.get() )
 
  851   else if ( constraint == mLineExtensionConstraint.get() )
 
  865 void QgsAdvancedDigitizingDockWidget::updateCapacity( 
bool updateUIwithoutChange )
 
  867   CadCapacities newCapacities = CadCapacities();
 
  871   if ( mCadPointList.count() > 1 )
 
  880   if ( mCadPointList.count() > 2 )
 
  885   if ( !updateUIwithoutChange && newCapacities == mCapacities )
 
  895   const bool distance =  mCadEnabled && newCapacities.testFlag( 
Distance );
 
  896   const bool relativeAngle = mCadEnabled && newCapacities.testFlag( 
RelativeAngle );
 
  897   const bool absoluteAngle = mCadEnabled && newCapacities.testFlag( 
AbsoluteAngle );
 
  898   const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( 
RelativeCoordinates );
 
  900   mPerpendicularAction->setEnabled( distance && snappingEnabled );
 
  901   mParallelAction->setEnabled( distance && snappingEnabled );
 
  903   mLineExtensionAction->setEnabled( snappingEnabled );
 
  904   mXyVertexAction->setEnabled( snappingEnabled );
 
  908   if ( !snappingEnabled )
 
  910     mPerpendicularAction->setToolTip( tr( 
"Snapping must be enabled to utilize perpendicular mode." ) );
 
  911     mParallelAction->setToolTip( tr( 
"Snapping must be enabled to utilize parallel mode." ) );
 
  912     mLineExtensionAction->setToolTip( tr( 
"Snapping must be enabled to utilize line extension mode." ) );
 
  913     mXyVertexAction->setToolTip( tr( 
"Snapping must be enabled to utilize xy point mode." ) );
 
  915   else if ( mCadPointList.count() <= 1 )
 
  917     mPerpendicularAction->setToolTip( tr( 
"A first vertex should be drawn to utilize perpendicular mode." ) );
 
  918     mParallelAction->setToolTip( tr( 
"A first vertex should be drawn to utilize parallel mode." ) );
 
  920   else if ( isGeographic )
 
  922     mPerpendicularAction->setToolTip( tr( 
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
  923     mParallelAction->setToolTip( tr( 
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
  927     mPerpendicularAction->setToolTip( 
"<b>" + tr( 
"Perpendicular" ) + 
"</b><br>(" + tr( 
"press p to switch between perpendicular, parallel and normal mode" ) + 
")" );
 
  928     mParallelAction->setToolTip( 
"<b>" + tr( 
"Parallel" ) + 
"</b><br>(" + tr( 
"press p to switch between perpendicular, parallel and normal mode" ) + 
")" );
 
  932   if ( !absoluteAngle )
 
  938   mLockAngleButton->setEnabled( absoluteAngle );
 
  939   mRelativeAngleButton->setEnabled( relativeAngle );
 
  940   mAngleLineEdit->setEnabled( absoluteAngle );
 
  942   if ( !absoluteAngle )
 
  946   if ( !relativeAngle )
 
  948     mAngleConstraint->setRelative( 
false );
 
  951   else if ( relativeAngle && !mCapacities.testFlag( 
RelativeAngle ) )
 
  954     mAngleConstraint->setRelative( 
true );
 
  959   mLockDistanceButton->setEnabled( distance && relativeCoordinates );
 
  960   mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
 
  962   if ( !( distance && relativeCoordinates ) )
 
  967   mRelativeXButton->setEnabled( relativeCoordinates );
 
  968   mRelativeYButton->setEnabled( relativeCoordinates );
 
  969   mRelativeZButton->setEnabled( relativeCoordinates );
 
  970   mRelativeMButton->setEnabled( relativeCoordinates );
 
  973   mCapacities = newCapacities;
 
  983   constr.
value = 
c->value();
 
  990   if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
 
  996   const int lastIndex = mLockedSnapVertices.length() - 1;
 
  997   for ( 
int i = lastIndex ; i >= 0; --i )
 
  999     if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
 
 1001       if ( snapMatch.
point() != previouslySnap.
point() )
 
 1003         mLockedSnapVertices.removeAt( i );
 
 1009   if ( snapMatch.
point() != previouslySnap.
point() )
 
 1011     mLockedSnapVertices.enqueue( snapMatch );
 
 1014   if ( mLockedSnapVertices.count() > 3 )
 
 1016     mLockedSnapVertices.dequeue();
 
 1025   context.
xConstraint = _constraint( mXConstraint.get() );
 
 1026   context.
yConstraint = _constraint( mYConstraint.get() );
 
 1027   context.
zConstraint = _constraint( mZConstraint.get() );
 
 1028   context.
mConstraint = _constraint( mMConstraint.get() );
 
 1044   const bool res = output.
valid;
 
 1046   mSnappedSegment.clear();
 
 1051     mSnappedSegment << edgePt0 << edgePt1;
 
 1072     mSnapIndicator->setMatch( output.
snapMatch );
 
 1073     mSnapIndicator->setVisible( 
true );
 
 1077     mSnapIndicator->setVisible( 
false );
 
 1098   if ( mSnapMatch.
layer() )
 
 1110     toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
 
 1111     mLastSnapMatch = mSnapMatch;
 
 1121   if ( mLockZButton->isChecked() )
 
 1123     point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
 
 1125   if ( mLockMButton->isChecked() )
 
 1127     point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
 
 1131   updateCurrentPoint( point );
 
 1133   updateUnlockedConstraintValues( point );
 
 1141     emit 
pushWarning( tr( 
"Some constraints are incompatible. Resulting point might be incorrect." ) );
 
 1148 void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( 
const QgsPoint &point )
 
 1150   bool previousPointExist, penulPointExist;
 
 1155   if ( !mAngleConstraint->isLocked() && previousPointExist )
 
 1158     if ( penulPointExist && mAngleConstraint->relative() )
 
 1161       angle = std::atan2( previousPt.
y() - penultimatePt.
y(),
 
 1162                           previousPt.
x() - penultimatePt.
x() );
 
 1164     angle = ( std::atan2( point.
y() - previousPt.
y(),
 
 1165                           point.
x() - previousPt.
x()
 
 1166                         ) - 
angle ) * 180 / M_PI;
 
 1169     mAngleConstraint->setValue( 
angle );
 
 1172   if ( !mDistanceConstraint->isLocked() && previousPointExist )
 
 1174     mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
 
 1177   if ( !mXConstraint->isLocked() )
 
 1179     if ( previousPointExist && mXConstraint->relative() )
 
 1181       mXConstraint->setValue( point.
x() - previousPt.
x() );
 
 1185       mXConstraint->setValue( point.
x() );
 
 1189   if ( !mYConstraint->isLocked() )
 
 1191     if ( previousPointExist && mYConstraint->relative() )
 
 1193       mYConstraint->setValue( point.
y() - previousPt.
y() );
 
 1197       mYConstraint->setValue( point.
y() );
 
 1201   if ( !mZConstraint->isLocked() )
 
 1203     if ( previousPointExist && mZConstraint->relative() )
 
 1205       mZConstraint->setValue( point.
z() - previousPt.
z() );
 
 1209       mZConstraint->setValue( point.
z() );
 
 1213   if ( !mMConstraint->isLocked() )
 
 1215     if ( previousPointExist && mMConstraint->relative() )
 
 1217       mMConstraint->setValue( point.
m() - previousPt.
m() );
 
 1221       mMConstraint->setValue( point.
m() );
 
 1227 QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( 
const QgsPointXY &originalMapPoint, 
bool *snapped )
 const 
 1239   localConfig.
setTypeFlag( Qgis::SnappingType::Segment );
 
 1240   snappingUtils->
setConfig( localConfig );
 
 1242   match = snappingUtils->
snapToMap( originalMapPoint, 
nullptr, 
true );
 
 1244   snappingUtils->
setConfig( canvasConfig );
 
 1254     *snapped = 
segment.count() == 2;
 
 1267   bool previousPointExist, penulPointExist, snappedSegmentExist;
 
 1270   mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
 
 1272   if ( !previousPointExist || !snappedSegmentExist )
 
 1277   double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
 
 1279   if ( mAngleConstraint->relative() && penulPointExist )
 
 1281     angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
 
 1289   angle *= 180 / M_PI;
 
 1291   mAngleConstraint->setValue( 
angle );
 
 1292   mAngleConstraint->setLockMode( lockMode );
 
 1310     case Qt::Key_Backspace:
 
 1311     case Qt::Key_Delete:
 
 1317     case Qt::Key_Escape:
 
 1347     case Qt::Key_Backspace:
 
 1348     case Qt::Key_Delete:
 
 1354     case Qt::Key_Escape:
 
 1361       filterKeyPress( e );
 
 1370   const auto constPoints = points;
 
 1377 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
 
 1381     return QgsDockWidget::eventFilter( obj, event );
 
 1389   if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
 
 1391     if ( QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( event ) )
 
 1393       return filterKeyPress( keyEvent );
 
 1396   return QgsDockWidget::eventFilter( obj, event );
 
 1399 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
 
 1405   const QEvent::Type type = e->type();
 
 1411       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1413         mXConstraint->toggleLocked();
 
 1418       else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1422           mXConstraint->toggleRelative();
 
 1429       else if ( type == QEvent::KeyPress )
 
 1431         mXLineEdit->setFocus();
 
 1432         mXLineEdit->selectAll();
 
 1441       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1443         mYConstraint->toggleLocked();
 
 1448       else if ( type == QEvent::ShortcutOverride &&  e->modifiers() == Qt::ShiftModifier )
 
 1452           mYConstraint->toggleRelative();
 
 1459       else if ( type == QEvent::KeyPress )
 
 1461         mYLineEdit->setFocus();
 
 1462         mYLineEdit->selectAll();
 
 1471       if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
 
 1473         mZConstraint->toggleLocked();
 
 1478       else if ( type == QEvent::ShortcutOverride &&  e->modifiers() == Qt::ShiftModifier )
 
 1482           mZConstraint->toggleRelative();
 
 1489       else if ( type == QEvent::KeyPress )
 
 1491         mZLineEdit->setFocus();
 
 1492         mZLineEdit->selectAll();
 
 1501       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1503         mMConstraint->toggleLocked();
 
 1508       else if ( type == QEvent::ShortcutOverride &&  e->modifiers() == Qt::ShiftModifier )
 
 1512           mMConstraint->toggleRelative();
 
 1519       else if ( type == QEvent::KeyPress )
 
 1521         mMLineEdit->setFocus();
 
 1522         mMLineEdit->selectAll();
 
 1531       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1535           mAngleConstraint->toggleLocked();
 
 1541       else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1545           mAngleConstraint->toggleRelative();
 
 1552       else if ( type == QEvent::KeyPress )
 
 1554         mAngleLineEdit->setFocus();
 
 1555         mAngleLineEdit->selectAll();
 
 1564       if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1568           mDistanceConstraint->toggleLocked();
 
 1575       else if ( type == QEvent::KeyPress )
 
 1577         mDistanceLineEdit->setFocus();
 
 1578         mDistanceLineEdit->selectAll();
 
 1586       if ( type == QEvent::KeyPress )
 
 1588         setConstructionMode( !mConstructionMode );
 
 1595       if ( type == QEvent::KeyPress )
 
 1597         const bool parallel = mParallelAction->isChecked();
 
 1598         const bool perpendicular = mPerpendicularAction->isChecked();
 
 1600         if ( !parallel && !perpendicular )
 
 1604         else if ( perpendicular )
 
 1624   return e->isAccepted();
 
 1633     mAngleLineEdit->setToolTip( tr( 
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 1634     mDistanceLineEdit->setToolTip( tr( 
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 1636     mLabelX->setText( tr( 
"Long" ) );
 
 1637     mLabelY->setText( tr( 
"Lat" ) );
 
 1639     mXConstraint->setPrecision( 8 );
 
 1640     mYConstraint->setPrecision( 8 );
 
 1644     mAngleLineEdit->setToolTip( 
"<b>" + tr( 
"Angle" ) + 
"</b><br>(" + tr( 
"press a for quick access" ) + 
")" );
 
 1645     mAngleLineEdit->setToolTip( QString() );
 
 1647     mDistanceLineEdit->setToolTip( 
"<b>" + tr( 
"Distance" ) + 
"</b><br>(" + tr( 
"press d for quick access" ) + 
")" );
 
 1649     mLabelX->setText( tr( 
"x" ) );
 
 1650     mLabelY->setText( tr( 
"y" ) );
 
 1652     mXConstraint->setPrecision( 6 );
 
 1653     mYConstraint->setPrecision( 6 );
 
 1658   mEnableAction->setEnabled( 
true );
 
 1659   mErrorLabel->hide();
 
 1662   mCurrentMapToolSupportsCad = 
true;
 
 1664   if ( mSessionActive && !isVisible() )
 
 1668   setCadEnabled( mSessionActive );
 
 1675   mEnableAction->setEnabled( 
false );
 
 1676   mErrorLabel->setText( tr( 
"CAD tools are not enabled for the current map tool" ) );
 
 1677   mErrorLabel->show();
 
 1680   mCurrentMapToolSupportsCad = 
false;
 
 1682   mSnapIndicator->setVisible( 
false );
 
 1684   setCadEnabled( 
false );
 
 1689   mCadPaintItem->update();
 
 1694   if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
 
 1699   mLockedSnapVertices.clear();
 
 1705   QgsPoint pt = pointXYToPoint( point );
 
 1708     mCadPointList << pt;
 
 1712     mCadPointList.insert( 0, pt );
 
 1725   mCadPointList.removeAt( i );
 
 1732   mCadPointList.clear();
 
 1733   mSnappedSegment.clear();
 
 1739 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint( 
const QgsPoint &point )
 
 1743     mCadPointList << point;
 
 1748     mCadPointList[0] = point;
 
 1757   mLockerButton->setChecked( mode == 
HardLock );
 
 1758   if ( mRepeatingLockButton )
 
 1762       mRepeatingLockButton->setEnabled( 
true );
 
 1766       mRepeatingLockButton->setChecked( 
false );
 
 1767       mRepeatingLockButton->setEnabled( 
false );
 
 1780   mRepeatingLock = repeating;
 
 1781   if ( mRepeatingLockButton )
 
 1782     mRepeatingLockButton->setChecked( repeating );
 
 1787   mRelative = relative;
 
 1788   if ( mRelativeButton )
 
 1790     mRelativeButton->setChecked( relative );
 
 1797   if ( updateWidget && mLineEdit->isEnabled() )
 
 1798     mLineEdit->setText( QLocale().toString( value, 
'f', mPrecision ) );
 
 1803   setLockMode( mLockMode == HardLock ? NoLock : HardLock );
 
 1808   setRelative( !mRelative );
 
 1814   if ( mLineEdit->isEnabled() )
 
 1815     mLineEdit->setText( QLocale().toString( mValue, 
'f', mPrecision ) );
 
 1823     return mCadPointList.value( 0 );
 
 1832     QgsPoint res = mCadPointList.value( 0 );
 
 1834     res.
setX( layerCoordinates.
x() );
 
 1835     res.
setY( layerCoordinates.
y() );
 
 1846     return mCadPointList.value( 1 );
 
 1856     return mCadPointList.value( 2 );
 
 1861 QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint( 
const QgsPointXY &point )
 const 
 1868   return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
 
 1873   return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();