QGIS API Documentation  3.27.0-Master (597e8eebd4)
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 
18 #include "qgsstyle.h"
19 #include "qgsfillsymbol.h"
20 #include "qgslinesymbol.h"
21 #include "qgsmarkersymbol.h"
23 #include "qgsannotationlineitem.h"
27 #include "qgstextformatwidget.h"
28 #include "qgsapplication.h"
29 #include "qgsrecentstylehandler.h"
30 
32 
33 QgsAnnotationPolygonItemWidget::QgsAnnotationPolygonItemWidget( QWidget *parent )
34  : QgsAnnotationItemBaseWidget( parent )
35 {
36  setupUi( this );
37 
38  mSelector = new QgsSymbolSelectorWidget( mSymbol.get(), QgsStyle::defaultStyle(), nullptr, nullptr );
39  mSelector->setDockMode( dockMode() );
40  connect( mSelector, &QgsSymbolSelectorWidget::symbolModified, this, [ = ]
41  {
42  if ( !mBlockChangedSignal )
43  {
44  emit itemChanged();
45  QgsApplication::recentStyleHandler()->pushRecentSymbol( QStringLiteral( "polygon_annotation_item" ), qgis::down_cast< QgsFillSymbol * >( mSelector->symbol()->clone() ) );
46  }
47  } );
48  connect( mSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
49 
50  QVBoxLayout *layout = new QVBoxLayout();
51  layout->setContentsMargins( 0, 0, 0, 0 );
52  layout->addWidget( mSelector );
53  mSymbolSelectorFrame->setLayout( layout );
54 
55  connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [ = ]
56  {
57  if ( !mBlockChangedSignal )
58  emit itemChanged();
59  } );
60 }
61 
62 QgsAnnotationItem *QgsAnnotationPolygonItemWidget::createItem()
63 {
64  QgsAnnotationPolygonItem *newItem = mItem->clone();
65  newItem->setSymbol( mSymbol->clone() );
66  mPropertiesWidget->updateItem( newItem );
67  return newItem;
68 }
69 
70 void QgsAnnotationPolygonItemWidget::updateItem( QgsAnnotationItem *item )
71 {
72  if ( QgsAnnotationPolygonItem *polygonItem = dynamic_cast< QgsAnnotationPolygonItem * >( item ) )
73  {
74  polygonItem->setSymbol( mSymbol->clone() );
75  mPropertiesWidget->updateItem( polygonItem );
76  }
77 }
78 
79 void QgsAnnotationPolygonItemWidget::setDockMode( bool dockMode )
80 {
82  if ( mSelector )
83  mSelector->setDockMode( dockMode );
84 }
85 
86 void QgsAnnotationPolygonItemWidget::setContext( const QgsSymbolWidgetContext &context )
87 {
89  if ( mSelector )
90  mSelector->setContext( context );
91  mPropertiesWidget->setContext( context );
92 }
93 
94 QgsAnnotationPolygonItemWidget::~QgsAnnotationPolygonItemWidget() = default;
95 
96 bool QgsAnnotationPolygonItemWidget::setNewItem( QgsAnnotationItem *item )
97 {
98  QgsAnnotationPolygonItem *polygonItem = dynamic_cast< QgsAnnotationPolygonItem * >( item );
99  if ( !polygonItem )
100  return false;
101 
102  mItem.reset( polygonItem->clone() );
103  if ( mItem->symbol() )
104  {
105  mSymbol.reset( mItem->symbol()->clone() );
106  }
107  else
108  {
109  mSymbol.reset( QgsFillSymbol::createSimple( {} ) );
110  }
111  mBlockChangedSignal = true;
112  mSelector->loadSymbol( mSymbol.get() );
113  mSelector->updatePreview();
114  mPropertiesWidget->setItem( mItem.get() );
115  mBlockChangedSignal = false;
116 
117  return true;
118 }
119 
120 
121 //
122 // QgsAnnotationLineItemWidget
123 //
124 
125 QgsAnnotationLineItemWidget::QgsAnnotationLineItemWidget( QWidget *parent )
126  : QgsAnnotationItemBaseWidget( parent )
127 {
128  setupUi( this );
129 
130  mSelector = new QgsSymbolSelectorWidget( mSymbol.get(), QgsStyle::defaultStyle(), nullptr, nullptr );
131  mSelector->setDockMode( dockMode() );
132  connect( mSelector, &QgsSymbolSelectorWidget::symbolModified, this, [ = ]
133  {
134  if ( !mBlockChangedSignal )
135  {
136  emit itemChanged();
137  QgsApplication::recentStyleHandler()->pushRecentSymbol( QStringLiteral( "line_annotation_item" ), qgis::down_cast< QgsLineSymbol * >( mSelector->symbol()->clone() ) );
138  }
139  } );
140  connect( mSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
141 
142  QVBoxLayout *layout = new QVBoxLayout();
143  layout->setContentsMargins( 0, 0, 0, 0 );
144  layout->addWidget( mSelector );
145  mSymbolSelectorFrame->setLayout( layout );
146 
147  connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [ = ]
148  {
149  if ( !mBlockChangedSignal )
150  emit itemChanged();
151  } );
152 }
153 
154 QgsAnnotationItem *QgsAnnotationLineItemWidget::createItem()
155 {
156  QgsAnnotationLineItem *newItem = mItem->clone();
157  newItem->setSymbol( mSymbol->clone() );
158  mPropertiesWidget->updateItem( newItem );
159  return newItem;
160 }
161 
162 void QgsAnnotationLineItemWidget::updateItem( QgsAnnotationItem *item )
163 {
164  if ( QgsAnnotationLineItem *lineItem = dynamic_cast< QgsAnnotationLineItem * >( item ) )
165  {
166  lineItem->setSymbol( mSymbol->clone() );
167  mPropertiesWidget->updateItem( lineItem );
168  }
169 }
170 
171 void QgsAnnotationLineItemWidget::setDockMode( bool dockMode )
172 {
174  if ( mSelector )
175  mSelector->setDockMode( dockMode );
176 }
177 
178 void QgsAnnotationLineItemWidget::setContext( const QgsSymbolWidgetContext &context )
179 {
181  if ( mSelector )
182  mSelector->setContext( context );
183  mPropertiesWidget->setContext( context );
184 }
185 
186 QgsAnnotationLineItemWidget::~QgsAnnotationLineItemWidget() = default;
187 
188 bool QgsAnnotationLineItemWidget::setNewItem( QgsAnnotationItem *item )
189 {
190  QgsAnnotationLineItem *lineItem = dynamic_cast< QgsAnnotationLineItem * >( item );
191  if ( !lineItem )
192  return false;
193 
194  mItem.reset( lineItem->clone() );
195  if ( mItem->symbol() )
196  {
197  mSymbol.reset( mItem->symbol()->clone() );
198  }
199  else
200  {
201  mSymbol.reset( QgsLineSymbol::createSimple( {} ) );
202  }
203  mBlockChangedSignal = true;
204  mSelector->loadSymbol( mSymbol.get() );
205  mSelector->updatePreview();
206  mPropertiesWidget->setItem( mItem.get() );
207  mBlockChangedSignal = false;
208 
209  return true;
210 }
211 
212 
213 //
214 // QgsAnnotationMarkerItemWidget
215 //
216 
217 QgsAnnotationMarkerItemWidget::QgsAnnotationMarkerItemWidget( QWidget *parent )
218  : QgsAnnotationItemBaseWidget( parent )
219 {
220  setupUi( this );
221 
222  mSelector = new QgsSymbolSelectorWidget( mSymbol.get(), QgsStyle::defaultStyle(), nullptr, nullptr );
223  mSelector->setDockMode( dockMode() );
224  connect( mSelector, &QgsSymbolSelectorWidget::symbolModified, this, [ = ]
225  {
226  if ( !mBlockChangedSignal )
227  {
228  emit itemChanged();
229  QgsApplication::recentStyleHandler()->pushRecentSymbol( QStringLiteral( "marker_annotation_item" ), qgis::down_cast< QgsMarkerSymbol * >( mSelector->symbol()->clone() ) );
230  }
231  } );
232  connect( mSelector, &QgsPanelWidget::showPanel, this, &QgsPanelWidget::openPanel );
233 
234  QVBoxLayout *layout = new QVBoxLayout();
235  layout->setContentsMargins( 0, 0, 0, 0 );
236  layout->addWidget( mSelector );
237  mSymbolSelectorFrame->setLayout( layout );
238 
239  connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [ = ]
240  {
241  if ( !mBlockChangedSignal )
242  emit itemChanged();
243  } );
244 }
245 
246 QgsAnnotationItem *QgsAnnotationMarkerItemWidget::createItem()
247 {
248  QgsAnnotationMarkerItem *newItem = mItem->clone();
249  newItem->setSymbol( mSymbol->clone() );
250  mPropertiesWidget->updateItem( newItem );
251  return newItem;
252 }
253 
254 void QgsAnnotationMarkerItemWidget::updateItem( QgsAnnotationItem *item )
255 {
256  if ( QgsAnnotationMarkerItem *markerItem = dynamic_cast< QgsAnnotationMarkerItem * >( item ) )
257  {
258  markerItem->setSymbol( mSymbol->clone() );
259  mPropertiesWidget->updateItem( markerItem );
260  }
261 }
262 
263 void QgsAnnotationMarkerItemWidget::setDockMode( bool dockMode )
264 {
266  if ( mSelector )
267  mSelector->setDockMode( dockMode );
268 }
269 
270 void QgsAnnotationMarkerItemWidget::setContext( const QgsSymbolWidgetContext &context )
271 {
273  if ( mSelector )
274  mSelector->setContext( context );
275  mPropertiesWidget->setContext( context );
276 }
277 
278 QgsAnnotationMarkerItemWidget::~QgsAnnotationMarkerItemWidget() = default;
279 
280 bool QgsAnnotationMarkerItemWidget::setNewItem( QgsAnnotationItem *item )
281 {
282  QgsAnnotationMarkerItem *markerItem = dynamic_cast< QgsAnnotationMarkerItem * >( item );
283  if ( !markerItem )
284  return false;
285 
286  mItem.reset( markerItem->clone() );
287  if ( mItem->symbol() )
288  {
289  mSymbol.reset( mItem->symbol()->clone() );
290  }
291  else
292  {
293  mSymbol.reset( QgsMarkerSymbol::createSimple( {} ) );
294  }
295  mBlockChangedSignal = true;
296  mSelector->loadSymbol( mSymbol.get() );
297  mSelector->updatePreview();
298  mPropertiesWidget->setItem( mItem.get() );
299  mBlockChangedSignal = false;
300 
301  return true;
302 }
303 
304 
305 
306 //
307 // QgsAnnotationPointTextItemWidget
308 //
309 
310 QgsAnnotationPointTextItemWidget::QgsAnnotationPointTextItemWidget( QWidget *parent )
311  : QgsAnnotationItemBaseWidget( parent )
312 {
313  setupUi( this );
314 
315  mTextFormatWidget = new QgsTextFormatWidget();
316  QVBoxLayout *vLayout = new QVBoxLayout();
317  vLayout->setContentsMargins( 0, 0, 0, 0 );
318  vLayout->addWidget( mTextFormatWidget );
319  mTextFormatWidgetContainer->setLayout( vLayout );
320 
321  mTextEdit->setMaximumHeight( mTextEdit->fontMetrics().height() * 10 );
322 
323  mTextFormatWidget->setDockMode( dockMode() );
324  connect( mTextFormatWidget, &QgsTextFormatWidget::widgetChanged, this, [ = ]
325  {
326  if ( !mBlockChangedSignal )
327  emit itemChanged();
328  } );
329  connect( mTextEdit, &QPlainTextEdit::textChanged, this, [ = ]
330  {
331  if ( !mBlockChangedSignal )
332  emit itemChanged();
333  } );
334  connect( mInsertExpressionButton, &QPushButton::clicked, this, &QgsAnnotationPointTextItemWidget::mInsertExpressionButton_clicked );
335  connect( mPropertiesWidget, &QgsAnnotationItemCommonPropertiesWidget::itemChanged, this, [ = ]
336  {
337  if ( !mBlockChangedSignal )
338  emit itemChanged();
339  } );
340 }
341 
342 QgsAnnotationItem *QgsAnnotationPointTextItemWidget::createItem()
343 {
344  QgsAnnotationPointTextItem *newItem = mItem->clone();
345  newItem->setFormat( mTextFormatWidget->format() );
346  newItem->setText( mTextEdit->toPlainText() );
347  mPropertiesWidget->updateItem( newItem );
348  return newItem;
349 }
350 
351 void QgsAnnotationPointTextItemWidget::updateItem( QgsAnnotationItem *item )
352 {
353  if ( QgsAnnotationPointTextItem *pointTextItem = dynamic_cast< QgsAnnotationPointTextItem * >( item ) )
354  {
355  pointTextItem->setFormat( mTextFormatWidget->format() );
356  pointTextItem->setText( mTextEdit->toPlainText() );
357  mPropertiesWidget->updateItem( pointTextItem );
358  }
359 }
360 
361 void QgsAnnotationPointTextItemWidget::setDockMode( bool dockMode )
362 {
364  if ( mTextFormatWidget )
365  mTextFormatWidget->setDockMode( dockMode );
366 }
367 
368 void QgsAnnotationPointTextItemWidget::setContext( const QgsSymbolWidgetContext &context )
369 {
371  if ( mTextFormatWidget )
372  mTextFormatWidget->setContext( context );
373  mPropertiesWidget->setContext( context );
374 }
375 
376 void QgsAnnotationPointTextItemWidget::focusDefaultWidget()
377 {
378  mTextEdit->selectAll();
379  mTextEdit->setFocus();
380 }
381 
382 QgsAnnotationPointTextItemWidget::~QgsAnnotationPointTextItemWidget() = default;
383 
384 bool QgsAnnotationPointTextItemWidget::setNewItem( QgsAnnotationItem *item )
385 {
386  QgsAnnotationPointTextItem *textItem = dynamic_cast< QgsAnnotationPointTextItem * >( item );
387  if ( !textItem )
388  return false;
389 
390  mItem.reset( textItem->clone() );
391 
392  mBlockChangedSignal = true;
393  mTextFormatWidget->setFormat( mItem->format() );
394  mTextEdit->setPlainText( mItem->text() );
395  mPropertiesWidget->setItem( mItem.get() );
396  mBlockChangedSignal = false;
397 
398  return true;
399 }
400 
401 void QgsAnnotationPointTextItemWidget::mInsertExpressionButton_clicked()
402 {
403  QString selText = mTextEdit->textCursor().selectedText();
404 
405  // html editor replaces newlines with Paragraph Separator characters - see https://github.com/qgis/QGIS/issues/27568
406  selText = selText.replace( QChar( 0x2029 ), QChar( '\n' ) );
407 
408  // edit the selected expression if there's one
409  if ( selText.startsWith( QLatin1String( "[%" ) ) && selText.endsWith( QLatin1String( "%]" ) ) )
410  selText = selText.mid( 2, selText.size() - 4 );
411 
412  QgsExpressionContext expressionContext;
413  if ( context().expressionContext() )
414  expressionContext = *( context().expressionContext() );
415  else
416  expressionContext = QgsProject::instance()->createExpressionContext();
417 
418  QgsExpressionBuilderDialog exprDlg( nullptr, selText, this, QStringLiteral( "generic" ), expressionContext );
419 
420  exprDlg.setWindowTitle( tr( "Insert Expression" ) );
421  if ( exprDlg.exec() == QDialog::Accepted )
422  {
423  QString expression = exprDlg.expressionText();
424  if ( !expression.isEmpty() )
425  {
426  mTextEdit->insertPlainText( "[%" + expression + "%]" );
427  }
428  }
429 }
430 
432 
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.
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() override
Returns a clone of the item.
An annotation item which renders a marker symbol at a point location.
QgsAnnotationMarkerItem * clone() override
Returns a clone of the item.
void setSymbol(QgsMarkerSymbol *symbol)
Sets the symbol used to render the marker item.
An annotation item which renders a text string at a point location.
void setText(const QString &text)
Sets the text rendered by the item.
void setFormat(const QgsTextFormat &format)
Sets the text format used to render the text.
QgsAnnotationPointTextItem * clone() 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() override
Returns a clone of the item.
static QgsRecentStyleHandler * recentStyleHandler()
Returns the handler for recently used style items.
A generic dialog for building expression strings.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
static QgsLineSymbol * createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
static 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.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:479
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void pushRecentSymbol(const QString &identifier, QgsSymbol *symbol)
Pushes a recently used symbol with the specified identifier.
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
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.
A widget for customizing text formatting settings.
void widgetChanged()
Emitted when the text format defined by the widget changes.