QGIS API Documentation 4.3.0-Master (90cb4ecf9ef)
Loading...
Searching...
No Matches
qgsannotationitemwidget_impl.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsannotationitemwidget_impl.cpp
3 ------------------------
4 Date : September 2021
5 Copyright : (C) 2021 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
16
17#include "qgsannotationlayer.h"
25#include "qgsapplication.h"
27#include "qgsexpressionfinder.h"
28#include "qgsfillsymbol.h"
29#include "qgsimagecache.h"
30#include "qgslinesymbol.h"
31#include "qgsmapcanvas.h"
32#include "qgsmarkersymbol.h"
35#include "qgsstyle.h"
36#include "qgssvgcache.h"
38#include "qgstextformatwidget.h"
39
40#include <QString>
41
42#include "moc_qgsannotationitemwidget_impl.cpp"
43
44using namespace Qt::StringLiterals;
45
47
48QgsAnnotationPolygonItemWidget::QgsAnnotationPolygonItemWidget( QWidget *parent )
50{
51 setupUi( this );
52
53 mSelector = new QgsSymbolSelectorWidget( mSymbol.get(), QgsStyle::defaultStyle(), nullptr, nullptr );
54 mSelector->setDockMode( dockMode() );
55 connect( mSelector, &QgsSymbolSelectorWidget::symbolModified, this, [this] {
56 if ( !mBlockChangedSignal )
57 {
58 emit itemChanged();
59 QgsApplication::recentStyleHandler()->pushRecentSymbol( u"polygon_annotation_item"_s, qgis::down_cast<QgsFillSymbol *>( mSelector->symbol()->clone() ) );
60 }
61 } );
62 connect( mSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
63
64 QVBoxLayout *layout = new QVBoxLayout();
65 layout->setContentsMargins( 0, 0, 0, 0 );
66 layout->addWidget( mSelector );
67 mSymbolSelectorFrame->setLayout( layout );
68
69 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [this] {
70 if ( !mBlockChangedSignal )
71 emit itemChanged();
72 } );
73}
74
75QgsAnnotationItem *QgsAnnotationPolygonItemWidget::createItem()
76{
77 QgsAnnotationPolygonItem *newItem = mItem->clone();
78 newItem->setSymbol( mSymbol->clone() );
79 mPropertiesWidget->updateItem( newItem );
80 return newItem;
81}
82
83void QgsAnnotationPolygonItemWidget::updateItem( QgsAnnotationItem *item )
84{
85 if ( QgsAnnotationPolygonItem *polygonItem = dynamic_cast<QgsAnnotationPolygonItem *>( item ) )
86 {
87 polygonItem->setSymbol( mSymbol->clone() );
88 mPropertiesWidget->updateItem( polygonItem );
89 }
90}
91
92void QgsAnnotationPolygonItemWidget::setDockMode( bool dockMode )
93{
95 if ( mSelector )
96 mSelector->setDockMode( dockMode );
97}
98
99void QgsAnnotationPolygonItemWidget::setContext( const QgsSymbolWidgetContext &context )
100{
102 if ( mSelector )
103 mSelector->setContext( context );
104 mPropertiesWidget->setContext( context );
105}
106
107QgsAnnotationPolygonItemWidget::~QgsAnnotationPolygonItemWidget() = default;
108
109bool QgsAnnotationPolygonItemWidget::setNewItem( QgsAnnotationItem *item )
110{
111 QgsAnnotationPolygonItem *polygonItem = dynamic_cast<QgsAnnotationPolygonItem *>( item );
112 if ( !polygonItem )
113 return false;
114
115 mItem.reset( polygonItem->clone() );
116 if ( mItem->symbol() )
117 {
118 mSymbol.reset( mItem->symbol()->clone() );
119 }
120 else
121 {
122 mSymbol = QgsFillSymbol::createSimple( {} );
123 }
124 mBlockChangedSignal = true;
125 mSelector->loadSymbol( mSymbol.get() );
126 mSelector->updatePreview();
127 mPropertiesWidget->setItem( mItem.get() );
128 mBlockChangedSignal = false;
129
130 return true;
131}
132
133
134//
135// QgsAnnotationLineItemWidget
136//
137
138QgsAnnotationLineItemWidget::QgsAnnotationLineItemWidget( QWidget *parent )
140{
141 setupUi( this );
142
143 mSelector = new QgsSymbolSelectorWidget( mSymbol.get(), QgsStyle::defaultStyle(), nullptr, nullptr );
144 mSelector->setDockMode( dockMode() );
145 connect( mSelector, &QgsSymbolSelectorWidget::symbolModified, this, [this] {
146 if ( !mBlockChangedSignal )
147 {
148 emit itemChanged();
149 QgsApplication::recentStyleHandler()->pushRecentSymbol( u"line_annotation_item"_s, qgis::down_cast<QgsLineSymbol *>( mSelector->symbol()->clone() ) );
150 }
151 } );
152 connect( mSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
153
154 QVBoxLayout *layout = new QVBoxLayout();
155 layout->setContentsMargins( 0, 0, 0, 0 );
156 layout->addWidget( mSelector );
157 mSymbolSelectorFrame->setLayout( layout );
158
159 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [this] {
160 if ( !mBlockChangedSignal )
161 emit itemChanged();
162 } );
163}
164
165QgsAnnotationItem *QgsAnnotationLineItemWidget::createItem()
166{
167 QgsAnnotationLineItem *newItem = mItem->clone();
168 newItem->setSymbol( mSymbol->clone() );
169 mPropertiesWidget->updateItem( newItem );
170 return newItem;
171}
172
173void QgsAnnotationLineItemWidget::updateItem( QgsAnnotationItem *item )
174{
175 if ( QgsAnnotationLineItem *lineItem = dynamic_cast<QgsAnnotationLineItem *>( item ) )
176 {
177 lineItem->setSymbol( mSymbol->clone() );
178 mPropertiesWidget->updateItem( lineItem );
179 }
180}
181
182void QgsAnnotationLineItemWidget::setDockMode( bool dockMode )
183{
185 if ( mSelector )
186 mSelector->setDockMode( dockMode );
187}
188
189void QgsAnnotationLineItemWidget::setContext( const QgsSymbolWidgetContext &context )
190{
192 if ( mSelector )
193 mSelector->setContext( context );
194 mPropertiesWidget->setContext( context );
195}
196
197QgsAnnotationLineItemWidget::~QgsAnnotationLineItemWidget() = default;
198
199bool QgsAnnotationLineItemWidget::setNewItem( QgsAnnotationItem *item )
200{
201 QgsAnnotationLineItem *lineItem = dynamic_cast<QgsAnnotationLineItem *>( item );
202 if ( !lineItem )
203 return false;
204
205 mItem.reset( lineItem->clone() );
206 if ( mItem->symbol() )
207 {
208 mSymbol.reset( mItem->symbol()->clone() );
209 }
210 else
211 {
212 mSymbol = QgsLineSymbol::createSimple( {} );
213 }
214 mBlockChangedSignal = true;
215 mSelector->loadSymbol( mSymbol.get() );
216 mSelector->updatePreview();
217 mPropertiesWidget->setItem( mItem.get() );
218 mBlockChangedSignal = false;
219
220 return true;
221}
222
223
224//
225// QgsAnnotationMarkerItemWidget
226//
227
228QgsAnnotationMarkerItemWidget::QgsAnnotationMarkerItemWidget( QWidget *parent )
230{
231 setupUi( this );
232
233 mSelector = new QgsSymbolSelectorWidget( mSymbol.get(), QgsStyle::defaultStyle(), nullptr, nullptr );
234 mSelector->setDockMode( dockMode() );
235 connect( mSelector, &QgsSymbolSelectorWidget::symbolModified, this, [this] {
236 if ( !mBlockChangedSignal )
237 {
238 emit itemChanged();
239 QgsApplication::recentStyleHandler()->pushRecentSymbol( u"marker_annotation_item"_s, qgis::down_cast<QgsMarkerSymbol *>( mSelector->symbol()->clone() ) );
240 }
241 } );
242 connect( mSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
243
244 QVBoxLayout *layout = new QVBoxLayout();
245 layout->setContentsMargins( 0, 0, 0, 0 );
246 layout->addWidget( mSelector );
247 mSymbolSelectorFrame->setLayout( layout );
248
249 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [this] {
250 if ( !mBlockChangedSignal )
251 emit itemChanged();
252 } );
253}
254
255QgsAnnotationItem *QgsAnnotationMarkerItemWidget::createItem()
256{
257 QgsAnnotationMarkerItem *newItem = mItem->clone();
258 newItem->setSymbol( mSymbol->clone() );
259 mPropertiesWidget->updateItem( newItem );
260 return newItem;
261}
262
263void QgsAnnotationMarkerItemWidget::updateItem( QgsAnnotationItem *item )
264{
265 if ( QgsAnnotationMarkerItem *markerItem = dynamic_cast<QgsAnnotationMarkerItem *>( item ) )
266 {
267 markerItem->setSymbol( mSymbol->clone() );
268 mPropertiesWidget->updateItem( markerItem );
269 }
270}
271
272void QgsAnnotationMarkerItemWidget::setDockMode( bool dockMode )
273{
275 if ( mSelector )
276 mSelector->setDockMode( dockMode );
277}
278
279void QgsAnnotationMarkerItemWidget::setContext( const QgsSymbolWidgetContext &context )
280{
282 if ( mSelector )
283 mSelector->setContext( context );
284 mPropertiesWidget->setContext( context );
285}
286
287QgsAnnotationMarkerItemWidget::~QgsAnnotationMarkerItemWidget() = default;
288
289bool QgsAnnotationMarkerItemWidget::setNewItem( QgsAnnotationItem *item )
290{
291 QgsAnnotationMarkerItem *markerItem = dynamic_cast<QgsAnnotationMarkerItem *>( item );
292 if ( !markerItem )
293 return false;
294
295 mItem.reset( markerItem->clone() );
296 if ( mItem->symbol() )
297 {
298 mSymbol.reset( mItem->symbol()->clone() );
299 }
300 else
301 {
302 mSymbol = QgsMarkerSymbol::createSimple( {} );
303 }
304 mBlockChangedSignal = true;
305 mSelector->loadSymbol( mSymbol.get() );
306 mSelector->updatePreview();
307 mPropertiesWidget->setItem( mItem.get() );
308 mBlockChangedSignal = false;
309
310 return true;
311}
312
313
314//
315// QgsAnnotationPointTextItemWidget
316//
317
318QgsAnnotationPointTextItemWidget::QgsAnnotationPointTextItemWidget( QWidget *parent )
320{
321 setupUi( this );
322
323 mTextEdit->setMode( QgsRichTextEditor::Mode::QgsTextRenderer );
324
325 mTextFormatButton->setMode( QgsFontButton::ModeTextRenderer );
326
327 mSpinTextAngle->setClearValue( 0 );
328
329 populateRotationModeComboBox( mRotationModeCombo );
330
331 mAlignmentComboBox->setAvailableAlignments( Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight );
332
333 connect( mTextFormatButton, &QgsFontButton::changed, this, [this] {
334 mTextEdit->setMode( mTextFormatButton->textFormat().allowHtmlFormatting() ? QgsRichTextEditor::Mode::QgsTextRenderer : QgsRichTextEditor::Mode::PlainText );
335
336 if ( !mBlockChangedSignal )
337 emit itemChanged();
338 } );
339 connect( mTextEdit, &QgsRichTextEditor::textChanged, this, [this] {
340 if ( !mBlockChangedSignal )
341 emit itemChanged();
342 } );
343 connect( mInsertExpressionButton, &QPushButton::clicked, this, &QgsAnnotationPointTextItemWidget::mInsertExpressionButton_clicked );
344 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [this] {
345 if ( !mBlockChangedSignal )
346 emit itemChanged();
347 } );
348
349 connect( mSpinTextAngle, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this] {
350 if ( !mBlockChangedSignal )
351 emit itemChanged();
352 } );
353
354 connect( mRotationModeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
355 if ( !mBlockChangedSignal )
356 emit itemChanged();
357 } );
358
359 connect( mAlignmentComboBox, &QgsAlignmentComboBox::changed, this, [this] {
360 if ( !mBlockChangedSignal )
361 emit itemChanged();
362 } );
363}
364
365QgsAnnotationItem *QgsAnnotationPointTextItemWidget::createItem()
366{
367 QgsAnnotationPointTextItem *newItem = mItem->clone();
368 updateItem( newItem );
369 return newItem;
370}
371
372void QgsAnnotationPointTextItemWidget::updateItem( QgsAnnotationItem *item )
373{
374 if ( QgsAnnotationPointTextItem *pointTextItem = dynamic_cast<QgsAnnotationPointTextItem *>( item ) )
375 {
376 mBlockChangedSignal = true;
377 pointTextItem->setFormat( mTextFormatButton->textFormat() );
378 pointTextItem->setText( mTextFormatButton->textFormat().allowHtmlFormatting() ? mTextEdit->toHtml() : mTextEdit->toPlainText() );
379 pointTextItem->setAngle( mSpinTextAngle->value() );
380 pointTextItem->setRotationMode( mRotationModeCombo->currentData().value<Qgis::SymbolRotationMode>() );
381 pointTextItem->setAlignment( mAlignmentComboBox->currentAlignment() );
382 mBlockChangedSignal = false;
383 mPropertiesWidget->updateItem( pointTextItem );
384 }
385}
386
387void QgsAnnotationPointTextItemWidget::setContext( const QgsSymbolWidgetContext &context )
388{
390 if ( mTextFormatButton )
391 {
392 mTextFormatButton->setMapCanvas( context.mapCanvas() );
393 mTextFormatButton->setMessageBar( context.messageBar() );
394 }
395 mPropertiesWidget->setContext( context );
396}
397
398void QgsAnnotationPointTextItemWidget::focusDefaultWidget()
399{
400 mTextEdit->textEdit()->selectAll();
401 mTextEdit->setFocus();
402}
403
404QgsAnnotationPointTextItemWidget::~QgsAnnotationPointTextItemWidget() = default;
405
406bool QgsAnnotationPointTextItemWidget::setNewItem( QgsAnnotationItem *item )
407{
408 QgsAnnotationPointTextItem *textItem = dynamic_cast<QgsAnnotationPointTextItem *>( item );
409 if ( !textItem )
410 return false;
411
412 mItem.reset( textItem->clone() );
413
414 mBlockChangedSignal = true;
415 mTextFormatButton->setTextFormat( mItem->format() );
416 mTextEdit->setMode( mItem->format().allowHtmlFormatting() ? QgsRichTextEditor::Mode::QgsTextRenderer : QgsRichTextEditor::Mode::PlainText );
417 mTextEdit->setText( mItem->text() );
418 mSpinTextAngle->setValue( mItem->angle() );
419 mRotationModeCombo->setCurrentIndex( mRotationModeCombo->findData( QVariant::fromValue( mItem->rotationMode() ) ) );
420 mAlignmentComboBox->setCurrentAlignment( mItem->alignment() & Qt::AlignHorizontal_Mask );
421 mPropertiesWidget->setItem( mItem.get() );
422 mBlockChangedSignal = false;
423
424 return true;
425}
426
427void QgsAnnotationPointTextItemWidget::mInsertExpressionButton_clicked()
428{
429 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( mTextEdit->textEdit() );
430
431 QgsExpressionContext expressionContext;
432 if ( context().expressionContext() )
433 expressionContext = *( context().expressionContext() );
434 else
435 expressionContext = QgsProject::instance()->createExpressionContext();
436
437 QgsExpressionBuilderDialog exprDlg( nullptr, expression, this, u"generic"_s, expressionContext );
438
439 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
440 if ( exprDlg.exec() == QDialog::Accepted )
441 {
442 expression = exprDlg.expressionText().trimmed();
443 if ( !expression.isEmpty() )
444 {
445 mTextEdit->textEdit()->insertPlainText( "[%" + expression + "%]" );
446 }
447 }
448}
449
450
451//
452// QgsAnnotationLineTextItemWidget
453//
454
455QgsAnnotationLineTextItemWidget::QgsAnnotationLineTextItemWidget( QWidget *parent )
457{
458 setupUi( this );
459
460 mTextFormatButton->setMode( QgsFontButton::ModeTextRenderer );
461
462 mTextEdit->setMode( QgsRichTextEditor::Mode::QgsTextRenderer );
463
464 mAlignmentCombo->addItem( tr( "Start of Line" ), QVariant::fromValue( Qgis::TextAnchorPoint::StartOfText ) );
465 mAlignmentCombo->addItem( tr( "Center of Line" ), QVariant::fromValue( Qgis::TextAnchorPoint::CenterOfText ) );
466 mAlignmentCombo->addItem( tr( "End of Line" ), QVariant::fromValue( Qgis::TextAnchorPoint::EndOfText ) );
467
468 connect( mTextFormatButton, &QgsFontButton::changed, this, [this] {
469 mTextEdit->setMode( mTextFormatButton->textFormat().allowHtmlFormatting() ? QgsRichTextEditor::Mode::QgsTextRenderer : QgsRichTextEditor::Mode::PlainText );
470
471 if ( !mBlockChangedSignal )
472 emit itemChanged();
473 } );
474 connect( mTextEdit, &QgsRichTextEditor::textChanged, this, [this] {
475 if ( !mBlockChangedSignal )
476 emit itemChanged();
477 } );
478 connect( mInsertExpressionButton, &QPushButton::clicked, this, &QgsAnnotationLineTextItemWidget::mInsertExpressionButton_clicked );
479 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [this] {
480 if ( !mBlockChangedSignal )
481 emit itemChanged();
482 } );
483
484 mOffsetUnitWidget->setUnits(
486 );
487 mSpinOffset->setClearValue( 0.0 );
488 connect( mSpinOffset, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, [this] {
489 if ( !mBlockChangedSignal )
490 emit itemChanged();
491 } );
492
493 connect( mOffsetUnitWidget, &QgsUnitSelectionWidget::changed, this, [this] {
494 if ( !mBlockChangedSignal )
495 emit itemChanged();
496 } );
497
498 connect( mAlignmentCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, [this] {
499 if ( !mBlockChangedSignal )
500 emit itemChanged();
501 } );
502}
503
504QgsAnnotationLineTextItemWidget::~QgsAnnotationLineTextItemWidget() = default;
505
506QgsAnnotationItem *QgsAnnotationLineTextItemWidget::createItem()
507{
508 QgsAnnotationLineTextItem *newItem = mItem->clone();
509 updateItem( newItem );
510 return newItem;
511}
512
513void QgsAnnotationLineTextItemWidget::updateItem( QgsAnnotationItem *item )
514{
515 if ( QgsAnnotationLineTextItem *lineTextItem = dynamic_cast<QgsAnnotationLineTextItem *>( item ) )
516 {
517 mBlockChangedSignal = true;
518 lineTextItem->setFormat( mTextFormatButton->textFormat() );
519 lineTextItem->setText( mTextFormatButton->textFormat().allowHtmlFormatting() ? mTextEdit->toHtml() : mTextEdit->toPlainText() );
520
521 lineTextItem->setOffsetFromLine( mSpinOffset->value() );
522 lineTextItem->setOffsetFromLineUnit( mOffsetUnitWidget->unit() );
523 lineTextItem->setOffsetFromLineMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
524
525 lineTextItem->setTextAnchor( mAlignmentCombo->currentData().value< Qgis::TextAnchorPoint >() );
526
527 mBlockChangedSignal = false;
528 mPropertiesWidget->updateItem( lineTextItem );
529 }
530}
531
532void QgsAnnotationLineTextItemWidget::setContext( const QgsSymbolWidgetContext &context )
533{
535 if ( mTextFormatButton )
536 {
537 mTextFormatButton->setMapCanvas( context.mapCanvas() );
538 mTextFormatButton->setMessageBar( context.messageBar() );
539 }
540 mPropertiesWidget->setContext( context );
541}
542
543void QgsAnnotationLineTextItemWidget::focusDefaultWidget()
544{
545 mTextEdit->textEdit()->selectAll();
546 mTextEdit->setFocus();
547}
548
549bool QgsAnnotationLineTextItemWidget::setNewItem( QgsAnnotationItem *item )
550{
551 QgsAnnotationLineTextItem *textItem = dynamic_cast<QgsAnnotationLineTextItem *>( item );
552 if ( !textItem )
553 return false;
554
555 mItem.reset( textItem->clone() );
556
557 mBlockChangedSignal = true;
558 mTextFormatButton->setTextFormat( mItem->format() );
559 mTextEdit->setMode( mItem->format().allowHtmlFormatting() ? QgsRichTextEditor::Mode::QgsTextRenderer : QgsRichTextEditor::Mode::PlainText );
560 mTextEdit->setText( mItem->text() );
561 mPropertiesWidget->setItem( mItem.get() );
562
563 mSpinOffset->setValue( mItem->offsetFromLine() );
564 mOffsetUnitWidget->setUnit( mItem->offsetFromLineUnit() );
565 mOffsetUnitWidget->setMapUnitScale( mItem->offsetFromLineMapUnitScale() );
566
567 mAlignmentCombo->setCurrentIndex( mAlignmentCombo->findData( QVariant::fromValue( mItem->textAnchor() ) ) );
568
569 mBlockChangedSignal = false;
570
571 return true;
572}
573
574void QgsAnnotationLineTextItemWidget::mInsertExpressionButton_clicked()
575{
576 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( mTextEdit->textEdit() );
577
578 QgsExpressionContext expressionContext;
579 if ( context().expressionContext() )
580 expressionContext = *( context().expressionContext() );
581 else
582 expressionContext = QgsProject::instance()->createExpressionContext();
583
584 QgsExpressionBuilderDialog exprDlg( nullptr, expression, this, u"generic"_s, expressionContext );
585
586 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
587 if ( exprDlg.exec() == QDialog::Accepted )
588 {
589 expression = exprDlg.expressionText().trimmed();
590 if ( !expression.isEmpty() )
591 {
592 mTextEdit->textEdit()->insertPlainText( "[%" + expression + "%]" );
593 }
594 }
595}
596
597
598//
599// QgsAnnotationRectangleTextItemWidget
600//
601
602QgsAnnotationRectangleTextItemWidget::QgsAnnotationRectangleTextItemWidget( QWidget *parent )
604{
605 setupUi( this );
606
607 mSizeModeCombo->addItem( tr( "Scale Dependent Size" ), QVariant::fromValue( Qgis::AnnotationPlacementMode::SpatialBounds ) );
608 mSizeModeCombo->addItem( tr( "Fixed Size" ), QVariant::fromValue( Qgis::AnnotationPlacementMode::FixedSize ) );
609 mSizeModeCombo->addItem( tr( "Relative to Map" ), QVariant::fromValue( Qgis::AnnotationPlacementMode::RelativeToMapFrame ) );
610
611 populateRotationModeComboBox( mRotationModeCombo );
612
613 mSizeUnitWidget->setUnits(
615 );
616
617 mBackgroundSymbolButton->setSymbolType( Qgis::SymbolType::Fill );
618 mBackgroundSymbolButton->setDialogTitle( tr( "Background" ) );
619 mBackgroundSymbolButton->registerExpressionContextGenerator( this );
620 mFrameSymbolButton->setSymbolType( Qgis::SymbolType::Fill );
621 mFrameSymbolButton->setDialogTitle( tr( "Frame" ) );
622 mFrameSymbolButton->registerExpressionContextGenerator( this );
623
624 mSpinBottomMargin->setClearValue( 0 );
625 mSpinTopMargin->setClearValue( 0 );
626 mSpinRightMargin->setClearValue( 0 );
627 mSpinLeftMargin->setClearValue( 0 );
628 mMarginUnitWidget->setUnits(
630 );
631
632 mTextFormatButton->setMode( QgsFontButton::ModeTextRenderer );
633
634 mTextEdit->setMode( QgsRichTextEditor::Mode::QgsTextRenderer );
635
636 mAlignmentComboBox->setAvailableAlignments( Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight | Qt::AlignJustify );
637 mVerticalAlignmentComboBox->setAvailableAlignments( Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom );
638
639 connect( mTextFormatButton, &QgsFontButton::changed, this, [this] {
640 mTextEdit->setMode( mTextFormatButton->textFormat().allowHtmlFormatting() ? QgsRichTextEditor::Mode::QgsTextRenderer : QgsRichTextEditor::Mode::PlainText );
641
642 onWidgetChanged();
643 } );
644 connect( mTextEdit, &QgsRichTextEditor::textChanged, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
645 connect( mInsertExpressionButton, &QPushButton::clicked, this, &QgsAnnotationRectangleTextItemWidget::mInsertExpressionButton_clicked );
646 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
647 connect( mAlignmentComboBox, &QgsAlignmentComboBox::changed, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
648 connect( mVerticalAlignmentComboBox, &QgsAlignmentComboBox::changed, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
649 connect( mFrameCheckbox, &QGroupBox::toggled, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
650 connect( mBackgroundCheckbox, &QGroupBox::toggled, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
651 connect( mBackgroundSymbolButton, &QgsSymbolButton::changed, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
652 connect( mFrameSymbolButton, &QgsSymbolButton::changed, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
653 connect( mSpinTopMargin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
654 connect( mSpinRightMargin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
655 connect( mSpinLeftMargin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
656 connect( mSpinBottomMargin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
657 connect( mMarginUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
658
659 connect( mSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
660
661 connect( mWidthSpinBox, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::setWidth );
662 connect( mHeightSpinBox, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::setHeight );
663
664 connect( mSizeModeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationRectangleTextItemWidget::sizeModeChanged );
665 connect( mRotationModeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
666 mSpinAngle->setClearValue( 0 );
667 connect( mSpinAngle, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsAnnotationRectangleTextItemWidget::onWidgetChanged );
668 mWidgetFixedSize->hide();
669 sizeModeChanged();
670}
671
672QgsAnnotationItem *QgsAnnotationRectangleTextItemWidget::createItem()
673{
674 QgsAnnotationRectangleTextItem *newItem = mItem->clone();
675 updateItem( newItem );
676 return newItem;
677}
678
679void QgsAnnotationRectangleTextItemWidget::updateItem( QgsAnnotationItem *item )
680{
681 if ( QgsAnnotationRectangleTextItem *rectTextItem = dynamic_cast<QgsAnnotationRectangleTextItem *>( item ) )
682 {
683 mBlockChangedSignal = true;
684 rectTextItem->setFormat( mTextFormatButton->textFormat() );
685 rectTextItem->setText( mTextFormatButton->textFormat().allowHtmlFormatting() ? mTextEdit->toHtml() : mTextEdit->toPlainText() );
686 rectTextItem->setAlignment( mAlignmentComboBox->currentAlignment() | mVerticalAlignmentComboBox->currentAlignment() );
687
688 rectTextItem->setPlacementMode( mSizeModeCombo->currentData().value<Qgis::AnnotationPlacementMode>() );
689
690 rectTextItem->setFixedSize( QSizeF( mWidthSpinBox->value(), mHeightSpinBox->value() ) );
691 rectTextItem->setFixedSizeUnit( mSizeUnitWidget->unit() );
692
693 rectTextItem->setRotation( mSpinAngle->value() );
694 rectTextItem->setRotationMode( mRotationModeCombo->currentData().value<Qgis::SymbolRotationMode>() );
695
696 rectTextItem->setBackgroundEnabled( mBackgroundCheckbox->isChecked() );
697 rectTextItem->setFrameEnabled( mFrameCheckbox->isChecked() );
698 rectTextItem->setBackgroundSymbol( mBackgroundSymbolButton->clonedSymbol<QgsFillSymbol>() );
699 rectTextItem->setFrameSymbol( mFrameSymbolButton->clonedSymbol<QgsFillSymbol>() );
700
701 rectTextItem->setMargins( QgsMargins( mSpinLeftMargin->value(), mSpinTopMargin->value(), mSpinRightMargin->value(), mSpinBottomMargin->value() ) );
702 rectTextItem->setMarginsUnit( mMarginUnitWidget->unit() );
703
704 if ( mUpdateItemPosition )
705 {
706 rectTextItem->setBounds( mItem->bounds() );
707 mUpdateItemPosition = false;
708 }
709
710 mBlockChangedSignal = false;
711
712 mPropertiesWidget->updateItem( rectTextItem );
713 }
714}
715
716void QgsAnnotationRectangleTextItemWidget::setContext( const QgsSymbolWidgetContext &context )
717{
719 if ( mTextFormatButton )
720 {
721 mTextFormatButton->setMapCanvas( context.mapCanvas() );
722 mTextFormatButton->setMessageBar( context.messageBar() );
723 }
724 mBackgroundSymbolButton->setMapCanvas( context.mapCanvas() );
725 mBackgroundSymbolButton->setMessageBar( context.messageBar() );
726 mFrameSymbolButton->setMapCanvas( context.mapCanvas() );
727 mFrameSymbolButton->setMessageBar( context.messageBar() );
728 mPropertiesWidget->setContext( context );
729}
730
731QgsExpressionContext QgsAnnotationRectangleTextItemWidget::createExpressionContext() const
732{
733 QgsExpressionContext expressionContext;
734 if ( context().expressionContext() )
735 expressionContext = *( context().expressionContext() );
736 else
737 expressionContext = QgsProject::instance()->createExpressionContext();
738 return expressionContext;
739}
740
741void QgsAnnotationRectangleTextItemWidget::focusDefaultWidget()
742{
743 mTextEdit->textEdit()->selectAll();
744 mTextEdit->setFocus();
745}
746
747QgsAnnotationRectangleTextItemWidget::~QgsAnnotationRectangleTextItemWidget() = default;
748
749bool QgsAnnotationRectangleTextItemWidget::setNewItem( QgsAnnotationItem *item )
750{
751 QgsAnnotationRectangleTextItem *textItem = dynamic_cast<QgsAnnotationRectangleTextItem *>( item );
752 if ( !textItem )
753 return false;
754
755 mItem.reset( textItem->clone() );
756
757 mBlockChangedSignal = true;
758 mTextFormatButton->setTextFormat( mItem->format() );
759 mTextEdit->setMode( mItem->format().allowHtmlFormatting() ? QgsRichTextEditor::Mode::QgsTextRenderer : QgsRichTextEditor::Mode::PlainText );
760 mTextEdit->setText( mItem->text() );
761 mAlignmentComboBox->setCurrentAlignment( mItem->alignment() & Qt::AlignHorizontal_Mask );
762 mVerticalAlignmentComboBox->setCurrentAlignment( mItem->alignment() & Qt::AlignVertical_Mask );
763 mPropertiesWidget->setItem( mItem.get() );
764
765 mBackgroundCheckbox->setChecked( textItem->backgroundEnabled() );
766 if ( const QgsSymbol *symbol = textItem->backgroundSymbol() )
767 mBackgroundSymbolButton->setSymbol( symbol->clone() );
768
769 mFrameCheckbox->setChecked( textItem->frameEnabled() );
770 if ( const QgsSymbol *symbol = textItem->frameSymbol() )
771 mFrameSymbolButton->setSymbol( symbol->clone() );
772
773 mMarginUnitWidget->setUnit( textItem->marginsUnit() );
774 mSpinLeftMargin->setValue( textItem->margins().left() );
775 mSpinTopMargin->setValue( textItem->margins().top() );
776 mSpinRightMargin->setValue( textItem->margins().right() );
777 mSpinBottomMargin->setValue( textItem->margins().bottom() );
778
779 mWidthSpinBox->setValue( textItem->fixedSize().width() );
780 mHeightSpinBox->setValue( textItem->fixedSize().height() );
781 mSizeUnitWidget->setUnit( textItem->fixedSizeUnit() );
782 mSizeModeCombo->setCurrentIndex( mSizeModeCombo->findData( QVariant::fromValue( textItem->placementMode() ) ) );
783 mRotationModeCombo->setCurrentIndex( mRotationModeCombo->findData( QVariant::fromValue( textItem->rotationMode() ) ) );
784 mSpinAngle->setValue( textItem->rotation() );
785
786 mBlockChangedSignal = false;
787
788 return true;
789}
790
791void QgsAnnotationRectangleTextItemWidget::onWidgetChanged()
792{
793 if ( !mBlockChangedSignal )
794 emit itemChanged();
795}
796
797void QgsAnnotationRectangleTextItemWidget::sizeModeChanged()
798{
799 const Qgis::AnnotationPlacementMode mode = mSizeModeCombo->currentData().value<Qgis::AnnotationPlacementMode>();
800 switch ( mode )
801 {
803 mWidgetFixedSize->hide();
804 break;
805
807 if ( const QgsRenderedAnnotationItemDetails *details = renderedItemDetails() )
808 {
809 const QgsRectangle itemBoundsMapUnits = details->boundingBox();
810 if ( QgsMapCanvas *canvas = context().mapCanvas() )
811 {
812 const QgsPointXY topLeftPixels = canvas->mapSettings().mapToPixel().transform( itemBoundsMapUnits.xMinimum(), itemBoundsMapUnits.yMinimum() );
813 const QgsPointXY bottomRightPixels = canvas->mapSettings().mapToPixel().transform( itemBoundsMapUnits.xMaximum(), itemBoundsMapUnits.yMaximum() );
814 const double widthPixels = std::abs( bottomRightPixels.x() - topLeftPixels.x() );
815 const double heightPixels = std::abs( bottomRightPixels.y() - topLeftPixels.y() );
816
817 // convert width/height in pixels to mm
818 const double pixelsPerMm = canvas->mapSettings().outputDpi() / 25.4;
819 mItem->setFixedSizeUnit( Qgis::RenderUnit::Millimeters );
820 mItem->setFixedSize( QSizeF( widthPixels / pixelsPerMm, heightPixels / pixelsPerMm ) );
821
822 // and update widget UI accordingly
823 whileBlocking( mWidthSpinBox )->setValue( mItem->fixedSize().width() );
824 whileBlocking( mHeightSpinBox )->setValue( mItem->fixedSize().height() );
825 whileBlocking( mSizeUnitWidget )->setUnit( mItem->fixedSizeUnit() );
826
827 mUpdateItemPosition = true;
828 }
829 }
830
831 mWidgetFixedSize->show();
832 break;
833
835 {
836 if ( const QgsRenderedAnnotationItemDetails *details = renderedItemDetails() )
837 {
838 // convert item bounds to relative position
839 const QgsRectangle itemBounds = details->boundingBox();
840 if ( QgsMapCanvas *canvas = context().mapCanvas() )
841 {
842 const double centerX = ( itemBounds.center().x() - canvas->extent().xMinimum() ) / canvas->extent().width();
843 const double centerY = ( canvas->extent().yMaximum() - itemBounds.center().y() ) / canvas->extent().height();
844 mItem->setBounds( QgsRectangle::fromCenterAndSize( QgsPointXY( centerX, centerY ), 0.5, 0.5 ) );
845 mUpdateItemPosition = true;
846 }
847 }
848
849 mWidgetFixedSize->hide();
850 break;
851 }
852 }
853
854 onWidgetChanged();
855}
856
857void QgsAnnotationRectangleTextItemWidget::setWidth()
858{
859 onWidgetChanged();
860}
861
862void QgsAnnotationRectangleTextItemWidget::setHeight()
863{
864 onWidgetChanged();
865}
866
867void QgsAnnotationRectangleTextItemWidget::mInsertExpressionButton_clicked()
868{
869 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( mTextEdit->textEdit() );
870
871 QgsExpressionBuilderDialog exprDlg( nullptr, expression, this, u"generic"_s, createExpressionContext() );
872
873 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
874 if ( exprDlg.exec() == QDialog::Accepted )
875 {
876 expression = exprDlg.expressionText().trimmed();
877 if ( !expression.isEmpty() )
878 {
879 mTextEdit->textEdit()->insertPlainText( "[%" + expression + "%]" );
880 }
881 }
882}
883
884
885//
886// QgsAnnotationPictureItemWidget
887//
888
889QgsAnnotationPictureItemWidget::QgsAnnotationPictureItemWidget( QWidget *parent )
891{
892 setupUi( this );
893
894 mSizeStackedWidget->setSizeMode( QgsStackedWidget::SizeMode::CurrentPageOnly );
895
896 mSizeModeCombo->addItem( tr( "Scale Dependent Size" ), QVariant::fromValue( Qgis::AnnotationPlacementMode::SpatialBounds ) );
897 mSizeModeCombo->addItem( tr( "Fixed Size" ), QVariant::fromValue( Qgis::AnnotationPlacementMode::FixedSize ) );
898 mSizeModeCombo->addItem( tr( "Relative to Map" ), QVariant::fromValue( Qgis::AnnotationPlacementMode::RelativeToMapFrame ) );
899
900 m3DSizeModeCombo->addItem( tr( "View Independent" ), QVariant::fromValue( Qgis::BillboardScaleMode::ViewIndependent ) );
901 m3DSizeModeCombo->addItem( tr( "Perspective Scaled" ), QVariant::fromValue( Qgis::BillboardScaleMode::Perspective ) );
902
903 populateRotationModeComboBox( mRotationModeCombo );
904
905 mSizeUnitWidget->setUnits(
907 );
908
909 mBackgroundSymbolButton->setSymbolType( Qgis::SymbolType::Fill );
910 mBackgroundSymbolButton->setDialogTitle( tr( "Background" ) );
911 mBackgroundSymbolButton->registerExpressionContextGenerator( this );
912 mFrameSymbolButton->setSymbolType( Qgis::SymbolType::Fill );
913 mFrameSymbolButton->setDialogTitle( tr( "Frame" ) );
914 mFrameSymbolButton->registerExpressionContextGenerator( this );
915
916 mHeight3DSpinBox->setClearValue( 0, tr( "Default" ) );
917 mWidth3DSpinBox->setClearValue( 0, tr( "Default" ) );
918
919 connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [this] {
920 if ( !mBlockChangedSignal )
921 emit itemChanged();
922 } );
923
924 connect( mSizeModeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationPictureItemWidget::sizeModeChanged );
925
926 connect( m3DSizeModeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
927
928 connect( mRotationModeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
929
930 mSpinAngle->setClearValue( 0 );
931 connect( mSpinAngle, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
932
933 connect( mHeight3DSpinBox, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
934 connect( mWidth3DSpinBox, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
935
936 connect( mRadioSVG, &QRadioButton::toggled, this, &QgsAnnotationPictureItemWidget::modeChanged );
937 connect( mRadioRaster, &QRadioButton::toggled, this, &QgsAnnotationPictureItemWidget::modeChanged );
938 connect( mSourceLineEdit, &QgsPictureSourceLineEditBase::sourceChanged, this, [this]( const QString &source ) {
939 if ( !mRadioSVG->isChecked() && QFileInfo( source ).suffix().compare( "svg"_L1, Qt::CaseInsensitive ) == 0 )
940 {
941 mRadioSVG->setChecked( true );
942 }
943
944 onWidgetChanged();
945 } );
946
947 connect( mLockAspectRatioCheck, &QCheckBox::toggled, this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
948 connect( mFrameCheckbox, &QGroupBox::toggled, this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
949 connect( mBackgroundCheckbox, &QGroupBox::toggled, this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
950 connect( mBackgroundSymbolButton, &QgsSymbolButton::changed, this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
951 connect( mFrameSymbolButton, &QgsSymbolButton::changed, this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
952 connect( mSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsAnnotationPictureItemWidget::onWidgetChanged );
953
954 connect( mWidthSpinBox, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationPictureItemWidget::setWidth );
955 connect( mHeightSpinBox, qOverload<double>( &QDoubleSpinBox::valueChanged ), this, &QgsAnnotationPictureItemWidget::setHeight );
956 connect( mLockAspectRatio, &QgsRatioLockButton::lockChanged, this, &QgsAnnotationPictureItemWidget::setLockAspectRatio );
957}
958
959QgsAnnotationPictureItemWidget::~QgsAnnotationPictureItemWidget() = default;
960
961QgsAnnotationItem *QgsAnnotationPictureItemWidget::createItem()
962{
963 QgsAnnotationPictureItem *newItem = mItem->clone();
964 updateItem( newItem );
965 return newItem;
966}
967
968void QgsAnnotationPictureItemWidget::updateItem( QgsAnnotationItem *item )
969{
970 if ( QgsAnnotationPictureItem *pictureItem = dynamic_cast<QgsAnnotationPictureItem *>( item ) )
971 {
972 const bool svg = mRadioSVG->isChecked();
974 const QString path = mSourceLineEdit->source();
975 pictureItem->setPath( newFormat, path );
976
977 pictureItem->setPlacementMode( mSizeModeCombo->currentData().value<Qgis::AnnotationPlacementMode>() );
978 switch ( pictureItem->placementMode() )
979 {
981 pictureItem->setLockAspectRatio( mLockAspectRatioCheck->isChecked() );
982 break;
985 pictureItem->setLockAspectRatio( mLockAspectRatio->isChecked() );
986 break;
987 }
988
989 pictureItem->setFixedSize( QSizeF( mWidthSpinBox->value(), mHeightSpinBox->value() ) );
990 pictureItem->setFixedSizeUnit( mSizeUnitWidget->unit() );
991
992 pictureItem->setRotation( mSpinAngle->value() );
993 pictureItem->setRotationMode( mRotationModeCombo->currentData().value<Qgis::SymbolRotationMode>() );
994
995 pictureItem->setBackgroundEnabled( mBackgroundCheckbox->isChecked() );
996 pictureItem->setFrameEnabled( mFrameCheckbox->isChecked() );
997 pictureItem->setBackgroundSymbol( mBackgroundSymbolButton->clonedSymbol<QgsFillSymbol>() );
998 pictureItem->setFrameSymbol( mFrameSymbolButton->clonedSymbol<QgsFillSymbol>() );
999
1000 pictureItem->setBillboard3DScaleMode( m3DSizeModeCombo->currentData().value< Qgis::BillboardScaleMode >() );
1001 if ( mWidth3DSpinBox->value() > 0 && mHeight3DSpinBox->value() > 0 )
1002 {
1003 pictureItem->setBillboard3DSize( QSizeF( mWidth3DSpinBox->value(), mHeight3DSpinBox->value() ) );
1004 }
1005 else
1006 {
1007 pictureItem->setBillboard3DSize( QSizeF() );
1008 }
1009
1010 if ( mUpdateItemPosition )
1011 {
1012 pictureItem->setBounds( mItem->bounds() );
1013 mUpdateItemPosition = false;
1014 }
1015
1016 mPropertiesWidget->updateItem( pictureItem );
1017 }
1018}
1019
1020void QgsAnnotationPictureItemWidget::setDockMode( bool dockMode )
1021{
1023}
1024
1025void QgsAnnotationPictureItemWidget::setContext( const QgsSymbolWidgetContext &context )
1026{
1028 mPropertiesWidget->setContext( context );
1029 mBackgroundSymbolButton->setMapCanvas( context.mapCanvas() );
1030 mBackgroundSymbolButton->setMessageBar( context.messageBar() );
1031 mFrameSymbolButton->setMapCanvas( context.mapCanvas() );
1032 mFrameSymbolButton->setMessageBar( context.messageBar() );
1033}
1034
1035QgsExpressionContext QgsAnnotationPictureItemWidget::createExpressionContext() const
1036{
1037 QgsExpressionContext expressionContext;
1038 if ( context().expressionContext() )
1039 expressionContext = *( context().expressionContext() );
1040 else
1041 expressionContext = QgsProject::instance()->createExpressionContext();
1042 return expressionContext;
1043}
1044
1045void QgsAnnotationPictureItemWidget::focusDefaultWidget()
1046{
1047 mSourceLineEdit->setFocus();
1048}
1049
1050bool QgsAnnotationPictureItemWidget::setNewItem( QgsAnnotationItem *item )
1051{
1052 QgsAnnotationPictureItem *pictureItem = dynamic_cast<QgsAnnotationPictureItem *>( item );
1053 if ( !pictureItem )
1054 return false;
1055
1056 mItem.reset( pictureItem->clone() );
1057
1058 mBlockChangedSignal = true;
1059 mPropertiesWidget->setItem( mItem.get() );
1060
1061 mLockAspectRatioCheck->setChecked( mItem->lockAspectRatio() );
1062 mLockAspectRatio->setLocked( mItem->lockAspectRatio() );
1063 switch ( pictureItem->format() )
1064 {
1066 mRadioSVG->setChecked( true );
1067 break;
1069 mRadioRaster->setChecked( true );
1070 break;
1072 break;
1073 }
1074
1075 mSourceLineEdit->setSource( pictureItem->path() );
1076
1077 mBackgroundCheckbox->setChecked( pictureItem->backgroundEnabled() );
1078 if ( const QgsSymbol *symbol = pictureItem->backgroundSymbol() )
1079 mBackgroundSymbolButton->setSymbol( symbol->clone() );
1080
1081 mFrameCheckbox->setChecked( pictureItem->frameEnabled() );
1082 if ( const QgsSymbol *symbol = pictureItem->frameSymbol() )
1083 mFrameSymbolButton->setSymbol( symbol->clone() );
1084
1085 mWidthSpinBox->setValue( pictureItem->fixedSize().width() );
1086 mHeightSpinBox->setValue( pictureItem->fixedSize().height() );
1087 mSizeModeCombo->setCurrentIndex( mSizeModeCombo->findData( QVariant::fromValue( pictureItem->placementMode() ) ) );
1088 mSpinAngle->setValue( pictureItem->rotation() );
1089 mRotationModeCombo->setCurrentIndex( mRotationModeCombo->findData( QVariant::fromValue( pictureItem->rotationMode() ) ) );
1090
1091 mWidth3DSpinBox->setValue( pictureItem->billboard3DSize().width() );
1092 mHeight3DSpinBox->setValue( pictureItem->billboard3DSize().height() );
1093
1094 m3DSizeModeCombo->setCurrentIndex( m3DSizeModeCombo->findData( QVariant::fromValue( pictureItem->billboard3DScaleMode() ) ) );
1095 sizeModeChanged();
1096
1097 mBlockChangedSignal = false;
1098
1099 return true;
1100}
1101
1102void QgsAnnotationPictureItemWidget::showEvent( QShowEvent *event )
1103{
1104 QgsAnnotationLayer *layer = QgsAnnotationPictureItemWidget::layer();
1105 // show 3d settings only when layer has a 3d renderer
1106 mGroupAppearance3D->setVisible( !layer || layer->renderer3D() );
1107
1108 QgsAnnotationItemBaseWidget::showEvent( event );
1109}
1110
1111void QgsAnnotationPictureItemWidget::onWidgetChanged()
1112{
1113 if ( !mBlockChangedSignal )
1114 emit itemChanged();
1115}
1116
1117void QgsAnnotationPictureItemWidget::modeChanged( bool checked )
1118{
1119 if ( !checked )
1120 return;
1121
1122 const bool svg = mRadioSVG->isChecked();
1123
1124 if ( svg )
1125 mSourceLineEdit->setMode( QgsPictureSourceLineEditBase::Svg );
1126 else
1127 mSourceLineEdit->setMode( QgsPictureSourceLineEditBase::Image );
1128
1129 onWidgetChanged();
1130}
1131
1132void QgsAnnotationPictureItemWidget::sizeModeChanged()
1133{
1134 const Qgis::AnnotationPlacementMode mode = mSizeModeCombo->currentData().value<Qgis::AnnotationPlacementMode>();
1135 switch ( mode )
1136 {
1138 mSizeStackedWidget->setCurrentWidget( mPageSpatialBounds );
1139 break;
1140
1142 mSizeStackedWidget->setCurrentWidget( mPageFixedSize );
1143 break;
1144
1146 {
1147 if ( const QgsRenderedAnnotationItemDetails *details = renderedItemDetails() )
1148 {
1149 // convert item bounds to relative position
1150 const QgsRectangle itemBounds = details->boundingBox();
1151 if ( QgsMapCanvas *canvas = context().mapCanvas() )
1152 {
1153 const double centerX = ( itemBounds.center().x() - canvas->extent().xMinimum() ) / canvas->extent().width();
1154 const double centerY = ( canvas->extent().yMaximum() - itemBounds.center().y() ) / canvas->extent().height();
1155 mItem->setBounds( QgsRectangle::fromCenterAndSize( QgsPointXY( centerX, centerY ), 0.5, 0.5 ) );
1156 mUpdateItemPosition = true;
1157 }
1158 }
1159
1160 mSizeStackedWidget->setCurrentWidget( mPageFixedSize );
1161 break;
1162 }
1163 }
1164
1165 onWidgetChanged();
1166}
1167
1168void QgsAnnotationPictureItemWidget::setWidth()
1169{
1170 if ( mLockAspectRatio->locked() )
1171 {
1172 const double ratio = pictureAspectRatio();
1173 if ( ratio > 0 )
1174 whileBlocking( mHeightSpinBox )->setValue( mWidthSpinBox->value() * ratio );
1175 }
1176
1177 onWidgetChanged();
1178}
1179
1180void QgsAnnotationPictureItemWidget::setHeight()
1181{
1182 if ( mLockAspectRatio->locked() )
1183 {
1184 const double ratio = pictureAspectRatio();
1185 if ( ratio > 0 )
1186 whileBlocking( mWidthSpinBox )->setValue( mHeightSpinBox->value() / ratio );
1187 }
1188
1189 onWidgetChanged();
1190}
1191
1192void QgsAnnotationPictureItemWidget::setLockAspectRatio( bool locked )
1193{
1194 if ( locked && !mBlockChangedSignal )
1195 {
1196 const double ratio = pictureAspectRatio();
1197 if ( ratio > 0 )
1198 whileBlocking( mHeightSpinBox )->setValue( mWidthSpinBox->value() * ratio );
1199 }
1200
1201 onWidgetChanged();
1202}
1203
1204double QgsAnnotationPictureItemWidget::pictureAspectRatio() const
1205{
1206 const bool svg = mRadioSVG->isChecked();
1207 const QString path = mSourceLineEdit->source();
1208 QSizeF size;
1209 if ( svg )
1210 {
1211 size = QgsApplication::svgCache()->svgViewboxSize( path, 100, QColor(), QColor(), 1, 1 );
1212 }
1213 else
1214 {
1215 size = QgsApplication::imageCache()->originalSize( path );
1216 }
1217 if ( size.isValid() && size.width() > 0 )
1218 return size.height() / size.width();
1219
1220 return 0;
1221}
1222
SymbolRotationMode
Modes for handling how symbol and text entity rotation is handled when maps are rotated.
Definition qgis.h:848
TextAnchorPoint
Anchor point of label text.
Definition qgis.h:1475
@ EndOfText
Anchor using end of text.
Definition qgis.h:1478
@ StartOfText
Anchor using start of text.
Definition qgis.h:1476
@ CenterOfText
Anchor using center of text.
Definition qgis.h:1477
PictureFormat
Picture formats.
Definition qgis.h:5776
@ Raster
Raster image.
Definition qgis.h:5778
@ Unknown
Invalid or unknown image type.
Definition qgis.h:5779
@ SVG
SVG image.
Definition qgis.h:5777
BillboardScaleMode
3D billboard scaling modes.
Definition qgis.h:4441
@ ViewIndependent
Billboard has a fixed pixel size on the screen, regardless of the camera distance.
Definition qgis.h:4442
@ Perspective
Billboard size is scaled with perspective distance from camera, using world units.
Definition qgis.h:4443
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5686
@ Millimeters
Millimeters.
Definition qgis.h:5683
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5687
@ MapUnits
Map units.
Definition qgis.h:5684
@ Pixels
Pixels.
Definition qgis.h:5685
@ Inches
Inches.
Definition qgis.h:5688
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5690
@ Fill
Fill symbol.
Definition qgis.h:654
AnnotationPlacementMode
Annotation item placement modes.
Definition qgis.h:2667
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
Definition qgis.h:2668
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2669
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2670
void sourceChanged(const QString &source)
Emitted whenever the file source is changed in the widget.
void changed()
Emitted when the alignment is changed.
A base class for property widgets for annotation items.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the widget is shown, e.g., the associated map canvas and expression context...
void itemChanged()
Emitted when the annotation item definition in the widget is changed by the user.
Abstract base class for annotation items which are drawn with QgsAnnotationLayers.
Represents a map layer containing a set of georeferenced annotations, e.g.
An annotation item which renders a line symbol along a line geometry.
void setSymbol(QgsLineSymbol *symbol)
Sets the symbol used to render the marker item.
QgsAnnotationLineItem * clone() const override
Returns a clone of the item.
An annotation item which renders text along a line geometry.
QgsAnnotationLineTextItem * clone() const override
Returns a clone of the item.
void setText(const QString &text)
Sets the text rendered by the item.
An annotation item which renders a marker symbol at a point location.
void setSymbol(QgsMarkerSymbol *symbol)
Sets the symbol used to render the marker item.
QgsAnnotationMarkerItem * clone() const override
Returns a clone of the item.
An annotation item which renders a picture.
Qgis::PictureFormat format() const
Returns the picture format.
QString path() const
Returns the path of the image used to render the item.
QSizeF billboard3DSize() const
Returns the 3D billboard size for the picture.
QgsAnnotationPictureItem * clone() const override
Returns a clone of the item.
Qgis::BillboardScaleMode billboard3DScaleMode() const
Returns the 3D billboard scale mode for the picture.
An annotation item which renders a text string at a point location.
void setText(const QString &text)
Sets the text rendered by the item.
QgsAnnotationPointTextItem * clone() const override
Returns a clone of the item.
An annotation item which renders a fill symbol for a polygon geometry.
void setSymbol(QgsFillSymbol *symbol)
Sets the symbol used to render the polygon item.
QgsAnnotationPolygonItem * clone() const override
Returns a clone of the item.
double rotation() const
Returns the rotation of the item, in degrees clockwise.
Qgis::SymbolRotationMode rotationMode() const
Returns the method used to render the item with respect to the rotation of the map.
Qgis::RenderUnit fixedSizeUnit() const
Returns the units to use for fixed item sizes, when the placementMode() is Qgis::AnnotationPlacementM...
QSizeF fixedSize() const
Returns the fixed size to use for the item, when the placementMode() is Qgis::AnnotationPlacementMode...
bool frameEnabled() const
Returns true if the item's frame should be rendered.
const QgsFillSymbol * frameSymbol() const
Returns the symbol used to render the item's frame.
Qgis::AnnotationPlacementMode placementMode() const
Returns the placement mode for the item.
bool backgroundEnabled() const
Returns true if the item's background should be rendered.
const QgsFillSymbol * backgroundSymbol() const
Returns the symbol used to render the item's background.
An annotation item which renders paragraphs of text within a rectangle.
QgsAnnotationRectangleTextItem * clone() const override
Returns a clone of the item.
const QgsMargins & margins() const
Returns the margins between the outside of the item's frame and the interior text.
void setText(const QString &text)
Sets the text rendered by the item.
Qgis::RenderUnit marginsUnit() const
Returns the units for the margins between the item's frame and the interior text.
static QgsRecentStyleHandler * recentStyleHandler()
Returns the handler for recently used style items.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
A generic dialog for building expression strings.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QString findAndSelectActiveExpression(QgsCodeEditor *editor, const QString &pattern=QString())
Find the expression under the cursor in the given editor and select it.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static std::unique_ptr< QgsFillSymbol > createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
@ ModeTextRenderer
Configure font settings for use with QgsTextRenderer.
void changed()
Emitted when the widget's text format settings are changed.
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
static std::unique_ptr< QgsLineSymbol > createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
Defines the four margins of a rectangle.
Definition qgsmargins.h:40
double top() const
Returns the top margin.
Definition qgsmargins.h:76
double right() const
Returns the right margin.
Definition qgsmargins.h:82
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:88
double left() const
Returns the left margin.
Definition qgsmargins.h:70
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void lockChanged(bool locked)
Emitted whenever the lock state changes.
void pushRecentSymbol(const QString &identifier, QgsSymbol *symbol)
Pushes a recently used symbol with the specified identifier.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
static QgsRectangle fromCenterAndSize(const QgsPointXY &center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
QgsPointXY center
Contains information about a rendered annotation item.
@ PlainText
Plain text mode.
@ QgsTextRenderer
QGIS text renderer mode, exposes the HTML/CSS subset supported by the QgsTextRenderer class.
void textChanged()
Emitted when the text contents are changed.
@ CurrentPageOnly
Only the size of the current page is considered when calculating the stacked widget size.
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
void changed()
Emitted when the symbol's settings are changed.
Symbol selector widget that can be used to select and build a symbol.
void symbolModified()
Emitted when a symbol is modified in the widget.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QgsExpressionContext * expressionContext() const
Returns the expression context used for the widget, if set.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
void changed()
Emitted when the selected unit is changed, or the definition of the map unit scale is changed.
QList< Qgis::RenderUnit > RenderUnitList
List of render units.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7451