Skip to content

Commit a9e9a6e

Browse files
authoredOct 6, 2016
Merge pull request #3579 from nyalldawson/composer_dock
Port composer item configuration widgets to inline dock
2 parents e5f7cdc + 521cc3b commit a9e9a6e

20 files changed

+239
-94
lines changed
 

‎python/core/composer/qgscomposeritemcommand.sip

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,14 @@ class QgsComposerMergeCommand : QgsComposerItemCommand
4949
//composer label
5050
ComposerLabelSetText,
5151
ComposerLabelSetId,
52+
ComposerLabelFontColor,
5253
//composer map
5354
ComposerMapRotation,
5455
ComposerMapAnnotationDistance,
56+
ComposerMapGridFramePenColor,
57+
ComposerMapGridFrameFill1Color,
58+
ComposerMapGridFrameFill2Color,
59+
ComposerMapGridAnnotationFontColor,
5560
//composer legend
5661
ComposerLegendText,
5762
LegendColumnCount,
@@ -69,8 +74,12 @@ class QgsComposerMergeCommand : QgsComposerItemCommand
6974
LegendBoxSpace,
7075
LegendColumnSpace,
7176
LegendRasterBorderWidth,
77+
LegendFontColor,
78+
LegendRasterBorderColor,
7279
//composer picture
7380
ComposerPictureRotation,
81+
ComposerPictureFillColor,
82+
ComposerPictureOutlineColor,
7483
// composer scalebar
7584
ScaleBarLineWidth,
7685
ScaleBarHeight,
@@ -81,6 +90,10 @@ class QgsComposerMergeCommand : QgsComposerItemCommand
8190
ScaleBarMapUnitsSegment,
8291
ScaleBarLabelBarSize,
8392
ScaleBarBoxContentSpace,
93+
ScaleBarFontColor,
94+
ScaleBarFillColor,
95+
ScaleBarFill2Color,
96+
ScaleBarStrokeColor,
8497
// composer table
8598
TableMaximumFeatures,
8699
TableMargin,
@@ -90,9 +103,13 @@ class QgsComposerMergeCommand : QgsComposerItemCommand
90103
ShapeOutlineWidth,
91104
//composer arrow
92105
ArrowOutlineWidth,
106+
ArrowHeadFillColor,
107+
ArrowHeadOutlineColor,
93108
ArrowHeadWidth,
94109
//item
95110
ItemOutlineWidth,
111+
ItemOutlineColor,
112+
ItemBackgroundColor,
96113
ItemMove,
97114
ItemRotation,
98115
ItemTransparency,

‎python/core/composer/qgscomposermultiframecommand.sip

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ class QgsComposerMultiFrameMergeCommand: QgsComposerMultiFrameCommand
4949
TableMaximumFeatures,
5050
TableMargin,
5151
TableGridStrokeWidth,
52-
TableCellStyle
52+
TableCellStyle,
53+
TableHeaderFontColor,
54+
TableContentFontColor,
55+
TableGridColor,
56+
TableBackgroundColor,
5357
};
5458

5559
QgsComposerMultiFrameMergeCommand( Context c, QgsComposerMultiFrame* multiFrame, const QString& text );

‎src/app/composer/qgscomposer.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include "qgsvectorlayer.h"
6868
#include "qgscomposerimageexportoptionsdialog.h"
6969
#include "ui_qgssvgexportoptions.h"
70+
#include "qgspanelwidgetstack.h"
7071

7172
#include <QCloseEvent>
7273
#include <QCheckBox>
@@ -578,6 +579,8 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
578579
mItemDock = new QgsDockWidget( tr( "Item properties" ), this );
579580
mItemDock->setObjectName( "ItemDock" );
580581
mItemDock->setMinimumWidth( minDockWidth );
582+
mItemPropertiesStack = new QgsPanelWidgetStack();
583+
mItemDock->setWidget( mItemPropertiesStack );
581584
mPanelMenu->addAction( mItemDock->toggleViewAction() );
582585
mUndoDock = new QgsDockWidget( tr( "Command history" ), this );
583586
mUndoDock->setObjectName( "CommandDock" );
@@ -996,28 +999,27 @@ void QgsComposer::updateStatusAtlasMsg( const QString& message )
996999

9971000
void QgsComposer::showItemOptions( QgsComposerItem* item )
9981001
{
999-
QWidget* currentWidget = mItemDock->widget();
1000-
10011002
if ( !item )
10021003
{
1003-
mItemDock->setWidget( nullptr );
1004+
mItemPropertiesStack->takeMainPanel();
10041005
return;
10051006
}
10061007

1007-
QMap<QgsComposerItem*, QWidget*>::const_iterator it = mItemWidgetMap.constFind( item );
1008+
QMap<QgsComposerItem*, QgsPanelWidget*>::const_iterator it = mItemWidgetMap.constFind( item );
10081009
if ( it == mItemWidgetMap.constEnd() )
10091010
{
10101011
return;
10111012
}
10121013

1013-
QWidget* newWidget = it.value();
1014-
1015-
if ( !newWidget || newWidget == currentWidget ) //bail out if new widget does not exist or is already there
1014+
QgsPanelWidget* newWidget = it.value();
1015+
if ( !newWidget || newWidget == mItemPropertiesStack->mainPanel() ) //bail out if new widget does not exist or is already there
10161016
{
10171017
return;
10181018
}
10191019

1020-
mItemDock->setWidget( newWidget );
1020+
( void ) mItemPropertiesStack->takeMainPanel();
1021+
newWidget->setDockMode( true );
1022+
mItemPropertiesStack->setMainPanel( newWidget );
10211023
}
10221024

10231025
void QgsComposer::on_mActionOptions_triggered()
@@ -3774,7 +3776,7 @@ void QgsComposer::addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame*
37743776

37753777
void QgsComposer::deleteItem( QgsComposerItem* item )
37763778
{
3777-
QMap<QgsComposerItem*, QWidget*>::const_iterator it = mItemWidgetMap.constFind( item );
3779+
QMap<QgsComposerItem*, QgsPanelWidget*>::const_iterator it = mItemWidgetMap.constFind( item );
37783780

37793781
if ( it == mItemWidgetMap.constEnd() )
37803782
{
@@ -3800,7 +3802,7 @@ void QgsComposer::setSelectionTool()
38003802

38013803
bool QgsComposer::containsWmsLayer() const
38023804
{
3803-
QMap<QgsComposerItem*, QWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
3805+
QMap<QgsComposerItem*, QgsPanelWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
38043806
QgsComposerItem* currentItem = nullptr;
38053807
QgsComposerMap* currentMap = nullptr;
38063808

@@ -3822,7 +3824,7 @@ bool QgsComposer::containsWmsLayer() const
38223824
bool QgsComposer::containsAdvancedEffects() const
38233825
{
38243826
// Check if composer contains any blend modes or flattened layers for transparency
3825-
QMap<QgsComposerItem*, QWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
3827+
QMap<QgsComposerItem*, QgsPanelWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
38263828
QgsComposerItem* currentItem = nullptr;
38273829
QgsComposerMap* currentMap = nullptr;
38283830

@@ -3893,7 +3895,7 @@ void QgsComposer::showAdvancedEffectsWarning()
38933895

38943896
void QgsComposer::cleanupAfterTemplateRead()
38953897
{
3896-
QMap<QgsComposerItem*, QWidget*>::const_iterator itemIt = mItemWidgetMap.constBegin();
3898+
QMap<QgsComposerItem*, QgsPanelWidget*>::const_iterator itemIt = mItemWidgetMap.constBegin();
38973899
for ( ; itemIt != mItemWidgetMap.constEnd(); ++itemIt )
38983900
{
38993901
//update all legends completely

‎src/app/composer/qgscomposer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define QGSCOMPOSER_H
1919
#include "ui_qgscomposerbase.h"
2020

21+
#include "qgspanelwidget.h"
2122
class QgisApp;
2223
class QgsComposerArrow;
2324
class QgsComposerPolygon;
@@ -44,6 +45,7 @@ class QgsDockWidget;
4445
class QgsMapLayer;
4546
class QgsFeature;
4647
class QgsVectorLayer;
48+
class QgsPanelWidgetStack;
4749

4850
class QGridLayout;
4951
class QDomNode;
@@ -568,7 +570,7 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
568570
QSizeGrip *mSizeGrip;
569571

570572
//! To know which item to show if selection changes
571-
QMap<QgsComposerItem*, QWidget*> mItemWidgetMap;
573+
QMap<QgsComposerItem*, QgsPanelWidget*> mItemWidgetMap;
572574

573575
//! Window menu action to select this window
574576
QAction *mWindowAction;
@@ -597,6 +599,7 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
597599
QMap< QgsComposerMap*, int > mMapsToRestore;
598600

599601
QgsDockWidget* mItemDock;
602+
QgsPanelWidgetStack* mItemPropertiesStack;
600603
QgsDockWidget* mUndoDock;
601604
QgsDockWidget* mGeneralDock;
602605
QgsDockWidget* mAtlasDock;

‎src/app/composer/qgscomposerarrowwidget.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
QgsComposerArrowWidget::QgsComposerArrowWidget( QgsComposerArrow* arrow ): QgsComposerItemBaseWidget( nullptr, arrow ), mArrow( arrow )
2929
{
3030
setupUi( this );
31+
setPanelTitle( tr( "Arrow properties" ) );
3132
mRadioButtonGroup = new QButtonGroup( this );
3233
mRadioButtonGroup->addButton( mDefaultMarkerRadioButton );
3334
mRadioButtonGroup->addButton( mNoMarkerRadioButton );
@@ -98,7 +99,7 @@ void QgsComposerArrowWidget::on_mArrowHeadFillColorButton_colorChanged( const QC
9899
return;
99100
}
100101

101-
mArrow->beginCommand( tr( "Arrow head fill color" ) );
102+
mArrow->beginCommand( tr( "Arrow head fill color" ), QgsComposerMergeCommand::ArrowHeadFillColor );
102103
mArrow->setArrowHeadFillColor( newColor );
103104
mArrow->update();
104105
mArrow->endCommand();
@@ -111,7 +112,7 @@ void QgsComposerArrowWidget::on_mArrowHeadOutlineColorButton_colorChanged( const
111112
return;
112113
}
113114

114-
mArrow->beginCommand( tr( "Arrow head outline color" ) );
115+
mArrow->beginCommand( tr( "Arrow head outline color" ), QgsComposerMergeCommand::ArrowHeadOutlineColor );
115116
mArrow->setArrowHeadOutlineColor( newColor );
116117
mArrow->update();
117118
mArrow->endCommand();

‎src/app/composer/qgscomposerattributetablewidget.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAtt
3939
, mFrame( frame )
4040
{
4141
setupUi( this );
42+
setPanelTitle( tr( "Table properties" ) );
4243

4344
blockAllSignals( true );
4445

@@ -268,7 +269,7 @@ void QgsComposerAttributeTableWidget::on_mHeaderFontColorButton_colorChanged( co
268269
QgsComposition* composition = mComposerTable->composition();
269270
if ( composition )
270271
{
271-
composition->beginMultiFrameCommand( mComposerTable, tr( "Table header font color" ) );
272+
composition->beginMultiFrameCommand( mComposerTable, tr( "Table header font color" ), QgsComposerMultiFrameMergeCommand::TableHeaderFontColor );
272273
}
273274
mComposerTable->setHeaderFontColor( newColor );
274275
if ( composition )
@@ -309,7 +310,7 @@ void QgsComposerAttributeTableWidget::on_mContentFontColorButton_colorChanged( c
309310
QgsComposition* composition = mComposerTable->composition();
310311
if ( composition )
311312
{
312-
composition->beginMultiFrameCommand( mComposerTable, tr( "Table content font color" ) );
313+
composition->beginMultiFrameCommand( mComposerTable, tr( "Table content font color" ), QgsComposerMultiFrameMergeCommand::TableContentFontColor );
313314
}
314315
mComposerTable->setContentFontColor( newColor );
315316
if ( composition )
@@ -347,7 +348,7 @@ void QgsComposerAttributeTableWidget::on_mGridColorButton_colorChanged( const QC
347348
QgsComposition* composition = mComposerTable->composition();
348349
if ( composition )
349350
{
350-
composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid color" ) );
351+
composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid color" ), QgsComposerMultiFrameMergeCommand::TableGridColor );
351352
}
352353
mComposerTable->setGridColor( newColor );
353354
if ( composition )
@@ -385,7 +386,7 @@ void QgsComposerAttributeTableWidget::on_mBackgroundColorButton_colorChanged( co
385386
QgsComposition* composition = mComposerTable->composition();
386387
if ( composition )
387388
{
388-
composition->beginMultiFrameCommand( mComposerTable, tr( "Table background color" ) );
389+
composition->beginMultiFrameCommand( mComposerTable, tr( "Table background color" ), QgsComposerMultiFrameMergeCommand::TableBackgroundColor );
389390
}
390391
mComposerTable->setBackgroundColor( newColor );
391392
if ( composition )

‎src/app/composer/qgscomposerhtmlwidget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ QgsComposerHtmlWidget::QgsComposerHtmlWidget( QgsComposerHtml* html, QgsComposer
3131
, mFrame( frame )
3232
{
3333
setupUi( this );
34+
setPanelTitle( tr( "HTML properties" ) );
3435

3536
//setup html editor
3637
mHtmlEditor = new QgsCodeEditorHTML( this );

‎src/app/composer/qgscomposeritemwidget.cpp

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,20 @@
3030

3131
//QgsComposerItemBaseWidget
3232

33-
QgsComposerItemBaseWidget::QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject *composerObject ): QWidget( parent ), mComposerObject( composerObject )
33+
QgsComposerConfigObject::QgsComposerConfigObject( QWidget* parent, QgsComposerObject *composerObject )
34+
: QObject( parent )
35+
, mComposerObject( composerObject )
3436
{
3537
connect( atlasComposition(), SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
3638
this, SLOT( updateDataDefinedButtons() ) );
3739
connect( atlasComposition(), SIGNAL( toggled( bool ) ), this, SLOT( updateDataDefinedButtons() ) );
3840
}
3941

40-
QgsComposerItemBaseWidget::~QgsComposerItemBaseWidget()
42+
QgsComposerConfigObject::~QgsComposerConfigObject()
4143
{
4244
}
4345

44-
void QgsComposerItemBaseWidget::updateDataDefinedProperty()
46+
void QgsComposerConfigObject::updateDataDefinedProperty()
4547
{
4648
//match data defined button to item's data defined property
4749
QgsDataDefinedButton* ddButton = dynamic_cast<QgsDataDefinedButton*>( sender() );
@@ -64,15 +66,15 @@ void QgsComposerItemBaseWidget::updateDataDefinedProperty()
6466
mComposerObject->refreshDataDefinedProperty( property );
6567
}
6668

67-
void QgsComposerItemBaseWidget::updateDataDefinedButtons()
69+
void QgsComposerConfigObject::updateDataDefinedButtons()
6870
{
6971
Q_FOREACH ( QgsDataDefinedButton* button, findChildren< QgsDataDefinedButton* >() )
7072
{
7173
button->setVectorLayer( atlasCoverageLayer() );
7274
}
7375
}
7476

75-
void QgsComposerItemBaseWidget::setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p )
77+
void QgsComposerConfigObject::setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p )
7678
{
7779
if ( !mComposerObject )
7880
{
@@ -83,7 +85,7 @@ void QgsComposerItemBaseWidget::setDataDefinedProperty( const QgsDataDefinedButt
8385
mComposerObject->setDataDefinedProperty( p, map.value( "active" ).toInt(), map.value( "useexpr" ).toInt(), map.value( "expression" ), map.value( "field" ) );
8486
}
8587

86-
void QgsComposerItemBaseWidget::registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property,
88+
void QgsComposerConfigObject::registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property,
8789
QgsDataDefinedButton::DataType type, const QString& description )
8890
{
8991
button->blockSignals( true );
@@ -98,7 +100,7 @@ void QgsComposerItemBaseWidget::registerDataDefinedButton( QgsDataDefinedButton*
98100
button->blockSignals( false );
99101
}
100102

101-
QgsAtlasComposition* QgsComposerItemBaseWidget::atlasComposition() const
103+
QgsAtlasComposition* QgsComposerConfigObject::atlasComposition() const
102104
{
103105
if ( !mComposerObject )
104106
{
@@ -115,7 +117,7 @@ QgsAtlasComposition* QgsComposerItemBaseWidget::atlasComposition() const
115117
return &composition->atlasComposition();
116118
}
117119

118-
QgsVectorLayer* QgsComposerItemBaseWidget::atlasCoverageLayer() const
120+
QgsVectorLayer* QgsComposerConfigObject::atlasCoverageLayer() const
119121
{
120122
QgsAtlasComposition* atlasMap = atlasComposition();
121123

@@ -140,8 +142,9 @@ void QgsComposerItemWidget::updateVariables()
140142
}
141143

142144
QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* item )
143-
: QgsComposerItemBaseWidget( parent, item )
145+
: QWidget( parent )
144146
, mItem( item )
147+
, mConfigObject( new QgsComposerConfigObject( this, item ) )
145148
, mFreezeXPosSpin( false )
146149
, mFreezeYPosSpin( false )
147150
, mFreezeWidthSpin( false )
@@ -184,18 +187,6 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem*
184187
connect( mItem->composition(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) );
185188
}
186189

187-
QgsComposerItemWidget::QgsComposerItemWidget()
188-
: QgsComposerItemBaseWidget( nullptr, nullptr )
189-
, mItem( nullptr )
190-
, mFreezeXPosSpin( false )
191-
, mFreezeYPosSpin( false )
192-
, mFreezeWidthSpin( false )
193-
, mFreezeHeightSpin( false )
194-
, mFreezePageSpin( false )
195-
{
196-
197-
}
198-
199190
QgsComposerItemWidget::~QgsComposerItemWidget()
200191
{
201192

@@ -219,7 +210,7 @@ void QgsComposerItemWidget::on_mFrameColorButton_colorChanged( const QColor& new
219210
{
220211
return;
221212
}
222-
mItem->beginCommand( tr( "Frame color changed" ) );
213+
mItem->beginCommand( tr( "Frame color changed" ), QgsComposerMergeCommand::ItemOutlineColor );
223214
mItem->setFrameOutlineColor( newFrameColor );
224215
mItem->update();
225216
mItem->endCommand();
@@ -239,7 +230,7 @@ void QgsComposerItemWidget::on_mBackgroundColorButton_colorChanged( const QColor
239230
{
240231
return;
241232
}
242-
mItem->beginCommand( tr( "Background color changed" ) );
233+
mItem->beginCommand( tr( "Background color changed" ), QgsComposerMergeCommand::ItemBackgroundColor );
243234
mItem->setBackgroundColor( newBackgroundColor );
244235

245236
//if the item is a composer map, we need to regenerate the map image
@@ -552,22 +543,22 @@ void QgsComposerItemWidget::setValuesForGuiNonPositionElements()
552543

553544
void QgsComposerItemWidget::populateDataDefinedButtons()
554545
{
555-
registerDataDefinedButton( mXPositionDDBtn, QgsComposerObject::PositionX,
556-
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
557-
registerDataDefinedButton( mYPositionDDBtn, QgsComposerObject::PositionY,
558-
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
559-
registerDataDefinedButton( mWidthDDBtn, QgsComposerObject::ItemWidth,
560-
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
561-
registerDataDefinedButton( mHeightDDBtn, QgsComposerObject::ItemHeight,
562-
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
563-
registerDataDefinedButton( mItemRotationDDBtn, QgsComposerObject::ItemRotation,
564-
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
565-
registerDataDefinedButton( mTransparencyDDBtn, QgsComposerObject::Transparency,
566-
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::intTranspDesc() );
567-
registerDataDefinedButton( mBlendModeDDBtn, QgsComposerObject::BlendMode,
568-
QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() );
569-
registerDataDefinedButton( mExcludePrintsDDBtn, QgsComposerObject::ExcludeFromExports,
570-
QgsDataDefinedButton::String, QgsDataDefinedButton::boolDesc() );
546+
mConfigObject->registerDataDefinedButton( mXPositionDDBtn, QgsComposerObject::PositionX,
547+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
548+
mConfigObject->registerDataDefinedButton( mYPositionDDBtn, QgsComposerObject::PositionY,
549+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
550+
mConfigObject->registerDataDefinedButton( mWidthDDBtn, QgsComposerObject::ItemWidth,
551+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
552+
mConfigObject->registerDataDefinedButton( mHeightDDBtn, QgsComposerObject::ItemHeight,
553+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
554+
mConfigObject->registerDataDefinedButton( mItemRotationDDBtn, QgsComposerObject::ItemRotation,
555+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
556+
mConfigObject->registerDataDefinedButton( mTransparencyDDBtn, QgsComposerObject::Transparency,
557+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::intTranspDesc() );
558+
mConfigObject->registerDataDefinedButton( mBlendModeDDBtn, QgsComposerObject::BlendMode,
559+
QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() );
560+
mConfigObject->registerDataDefinedButton( mExcludePrintsDDBtn, QgsComposerObject::ExcludeFromExports,
561+
QgsDataDefinedButton::String, QgsDataDefinedButton::boolDesc() );
571562
}
572563

573564
void QgsComposerItemWidget::setValuesForGuiElements()
@@ -786,3 +777,25 @@ void QgsComposerItemWidget::on_mExcludeFromPrintsCheckBox_toggled( bool checked
786777
mItem->endCommand();
787778
}
788779
}
780+
781+
QgsComposerItemBaseWidget::QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject* composerObject )
782+
: QgsPanelWidget( parent )
783+
, mConfigObject( new QgsComposerConfigObject( this, composerObject ) )
784+
{
785+
786+
}
787+
788+
void QgsComposerItemBaseWidget::registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property, QgsDataDefinedButton::DataType type, const QString& description )
789+
{
790+
mConfigObject->registerDataDefinedButton( button, property, type, description );
791+
}
792+
793+
QgsVectorLayer* QgsComposerItemBaseWidget::atlasCoverageLayer() const
794+
{
795+
return mConfigObject->atlasCoverageLayer();
796+
}
797+
798+
QgsAtlasComposition* QgsComposerItemBaseWidget::atlasComposition() const
799+
{
800+
return mConfigObject->atlasComposition();
801+
}

‎src/app/composer/qgscomposeritemwidget.h

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,84 @@
2020

2121
#include "ui_qgscomposeritemwidgetbase.h"
2222
#include "qgscomposeritem.h"
23+
#include "qgspanelwidget.h"
2324

2425
class QgsComposerItem;
2526
class QgsAtlasComposition;
2627
class QgsDataDefinedButton;
2728

28-
/** A base class for property widgets for composer items. All composer item widgets should inherit from
29-
* this base class.
29+
30+
// NOTE - the inheritance here is tricky, as we need to avoid the multiple inheritance
31+
// diamond problem and the ideal base object (QgsComposerConfigObject) MUST be a QObject
32+
// because of its slots.
33+
34+
// So here we go:
35+
// QgsComposerItemWidget is just a QWidget which is embedded inside specific item property
36+
// widgets and contains common settings like position and rotation of the items. While the
37+
// actual individual item type widgets MUST be QgsPanelWidgets unfortunately QgsComposerItemWidget
38+
// CANNOT be a QgsPanelWidget and must instead be a generic QWidget (otherwise a QgsPanelWidget
39+
// contains a child QgsPanelWidget, which breaks lots of assumptions made in QgsPanelWidget
40+
// and related classes).
41+
// So QgsComposerItemWidget HAS a QgsComposerConfigObject to handle these common tasks.
42+
// Specific item property widgets (eg QgsComposerMapWidget) should inherit from QgsComposerItemBaseWidget
43+
// (which is a QgsPanelWidget) and also HAS a QgsComposerConfigObject, with protected methods
44+
// which are just proxied through to the QgsComposerConfigObject.
45+
// phew!
46+
// long story short - don't change this without good reason. If you add a new item type, inherit
47+
// from QgsComposerItemWidget and trust that everything else has been done for you.
48+
49+
/** An object for property widgets for composer items. All composer config type widgets should contain
50+
* this object.
3051
*/
31-
class QgsComposerItemBaseWidget: public QWidget
52+
class QgsComposerConfigObject: public QObject
3253
{
3354
Q_OBJECT
3455
public:
35-
QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject* composerObject );
36-
~QgsComposerItemBaseWidget();
56+
QgsComposerConfigObject( QWidget* parent, QgsComposerObject* composerObject );
57+
~QgsComposerConfigObject();
3758

38-
protected slots:
59+
/** Sets a data defined property for the item from its current data defined button settings*/
60+
void setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p );
61+
62+
/** Registers a data defined button, setting up its initial value, connections and description.
63+
* @param button button to register
64+
* @param property correponding data defined property
65+
* @param type valid data types for button
66+
* @param description user visible description for data defined property
67+
*/
68+
void registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property,
69+
QgsDataDefinedButton::DataType type, const QString& description );
70+
71+
/** Returns the current atlas coverage layer (if set)*/
72+
QgsVectorLayer* atlasCoverageLayer() const;
73+
74+
/** Returns the atlas for the composition*/
75+
QgsAtlasComposition *atlasComposition() const;
76+
77+
private slots:
3978
/** Must be called when a data defined button changes*/
4079
void updateDataDefinedProperty();
4180

4281
//! Updates data defined buttons to reflect current state of atlas (eg coverage layer)
4382
void updateDataDefinedButtons();
4483

84+
private:
85+
86+
QgsComposerObject* mComposerObject;
87+
};
88+
89+
/**
90+
* A base class for property widgets for composer items. All composer item widgets should inherit from
91+
* this base class.
92+
*/
93+
class QgsComposerItemBaseWidget: public QgsPanelWidget
94+
{
95+
Q_OBJECT
96+
97+
public:
98+
QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject* composerObject );
99+
45100
protected:
46-
/** Sets a data defined property for the item from its current data defined button settings*/
47-
void setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p );
48101

49102
/** Registers a data defined button, setting up its initial value, connections and description.
50103
* @param button button to register
@@ -61,12 +114,14 @@ class QgsComposerItemBaseWidget: public QWidget
61114
/** Returns the atlas for the composition*/
62115
QgsAtlasComposition *atlasComposition() const;
63116

64-
QgsComposerObject* mComposerObject;
117+
private:
118+
119+
QgsComposerConfigObject* mConfigObject;
65120
};
66121

67122
/** A class to enter generic properties for composer items (e.g. background, outline, frame).
68123
This widget can be embedded into other item widgets*/
69-
class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsComposerItemWidgetBase
124+
class QgsComposerItemWidget: public QWidget, private Ui::QgsComposerItemWidgetBase
70125
{
71126
Q_OBJECT
72127
public:
@@ -132,9 +187,9 @@ class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo
132187
void populateDataDefinedButtons();
133188

134189
private:
135-
QgsComposerItemWidget();
136190

137191
QgsComposerItem* mItem;
192+
QgsComposerConfigObject* mConfigObject;
138193

139194
bool mFreezeXPosSpin;
140195
bool mFreezeYPosSpin;

‎src/app/composer/qgscomposerlabelwidget.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
QgsComposerLabelWidget::QgsComposerLabelWidget( QgsComposerLabel* label ): QgsComposerItemBaseWidget( nullptr, label ), mComposerLabel( label )
3030
{
3131
setupUi( this );
32+
setPanelTitle( tr( "Label properties" ) );
3233

3334
//add widget for general composer item properties
3435
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, label );
@@ -124,7 +125,7 @@ void QgsComposerLabelWidget::on_mFontColorButton_colorChanged( const QColor &new
124125
return;
125126
}
126127

127-
mComposerLabel->beginCommand( tr( "Label color changed" ) );
128+
mComposerLabel->beginCommand( tr( "Label color changed" ), QgsComposerMergeCommand::ComposerLabelFontColor );
128129
mComposerLabel->setFontColor( newLabelColor );
129130
mComposerLabel->update();
130131
mComposerLabel->endCommand();

‎src/app/composer/qgscomposerlegendwidget.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ QgsComposerLegendWidget::QgsComposerLegendWidget( QgsComposerLegend* legend )
4747
, mLegend( legend )
4848
{
4949
setupUi( this );
50+
setPanelTitle( tr( "Legend properties" ) );
5051

5152
// setup icons
5253
mAddToolButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
@@ -405,7 +406,7 @@ void QgsComposerLegendWidget::on_mFontColorButton_colorChanged( const QColor& ne
405406
return;
406407
}
407408

408-
mLegend->beginCommand( tr( "Legend font color changed" ) );
409+
mLegend->beginCommand( tr( "Legend font color changed" ), QgsComposerMergeCommand::LegendFontColor );
409410
mLegend->setFontColor( newFontColor );
410411
mLegend->update();
411412
mLegend->endCommand();
@@ -623,7 +624,7 @@ void QgsComposerLegendWidget::on_mRasterBorderColorButton_colorChanged( const QC
623624
return;
624625
}
625626

626-
mLegend->beginCommand( tr( "Legend raster border color" ) );
627+
mLegend->beginCommand( tr( "Legend raster border color" ), QgsComposerMergeCommand::LegendRasterBorderColor );
627628
mLegend->setRasterBorderColor( newColor );
628629
mLegend->update();
629630
mLegend->endCommand();

‎src/app/composer/qgscomposermapwidget.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap )
4848
, mComposerMap( composerMap )
4949
{
5050
setupUi( this );
51+
setPanelTitle( tr( "Map properties" ) );
5152

5253
//add widget for general composer item properties
5354
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerMap );
@@ -1729,7 +1730,7 @@ void QgsComposerMapWidget::on_mGridFramePenColorButton_colorChanged( const QColo
17291730
return;
17301731
}
17311732

1732-
mComposerMap->beginCommand( tr( "Grid frame color changed" ) );
1733+
mComposerMap->beginCommand( tr( "Grid frame color changed" ), QgsComposerMergeCommand::ComposerMapGridFramePenColor );
17331734
grid->setFramePenColor( newColor );
17341735
mComposerMap->update();
17351736
mComposerMap->endCommand();
@@ -1743,7 +1744,7 @@ void QgsComposerMapWidget::on_mGridFrameFill1ColorButton_colorChanged( const QCo
17431744
return;
17441745
}
17451746

1746-
mComposerMap->beginCommand( tr( "Grid frame first fill color changed" ) );
1747+
mComposerMap->beginCommand( tr( "Grid frame first fill color changed" ), QgsComposerMergeCommand::ComposerMapGridFrameFill1Color );
17471748
grid->setFrameFillColor1( newColor );
17481749
mComposerMap->update();
17491750
mComposerMap->endCommand();
@@ -1757,7 +1758,7 @@ void QgsComposerMapWidget::on_mGridFrameFill2ColorButton_colorChanged( const QCo
17571758
return;
17581759
}
17591760

1760-
mComposerMap->beginCommand( tr( "Grid frame second fill color changed" ) );
1761+
mComposerMap->beginCommand( tr( "Grid frame second fill color changed" ), QgsComposerMergeCommand::ComposerMapGridFrameFill2Color );
17611762
grid->setFrameFillColor2( newColor );
17621763
mComposerMap->update();
17631764
mComposerMap->endCommand();
@@ -2076,7 +2077,7 @@ void QgsComposerMapWidget::on_mAnnotationFontColorButton_colorChanged( const QCo
20762077
return;
20772078
}
20782079

2079-
mComposerMap->beginCommand( tr( "Annotation color changed" ) );
2080+
mComposerMap->beginCommand( tr( "Annotation color changed" ), QgsComposerMergeCommand::ComposerMapGridAnnotationFontColor );
20802081
grid->setAnnotationFontColor( color );
20812082
mComposerMap->update();
20822083
mComposerMap->endCommand();

‎src/app/composer/qgscomposerpicturewidget.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
QgsComposerPictureWidget::QgsComposerPictureWidget( QgsComposerPicture* picture ): QgsComposerItemBaseWidget( nullptr, picture ), mPicture( picture ), mPreviewsLoaded( false )
3737
{
3838
setupUi( this );
39+
setPanelTitle( tr( "Picture properties" ) );
3940

4041
mFillColorButton->setAllowAlpha( true );
4142
mFillColorButton->setColorDialogTitle( tr( "Select fill color" ) );
@@ -632,15 +633,15 @@ void QgsComposerPictureWidget::loadPicturePreviews( bool collapsed )
632633

633634
void QgsComposerPictureWidget::on_mFillColorButton_colorChanged( const QColor& color )
634635
{
635-
mPicture->beginCommand( tr( "Picture fill color changed" ) );
636+
mPicture->beginCommand( tr( "Picture fill color changed" ), QgsComposerMergeCommand::ComposerPictureFillColor );
636637
mPicture->setSvgFillColor( color );
637638
mPicture->endCommand();
638639
mPicture->update();
639640
}
640641

641642
void QgsComposerPictureWidget::on_mOutlineColorButton_colorChanged( const QColor& color )
642643
{
643-
mPicture->beginCommand( tr( "Picture border color changed" ) );
644+
mPicture->beginCommand( tr( "Picture border color changed" ), QgsComposerMergeCommand::ComposerPictureOutlineColor );
644645
mPicture->setSvgBorderColor( color );
645646
mPicture->endCommand();
646647
mPicture->update();

‎src/app/composer/qgscomposerpolygonwidget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ QgsComposerPolygonWidget::QgsComposerPolygonWidget( QgsComposerPolygon* composer
2626
, mComposerPolygon( composerPolygon )
2727
{
2828
setupUi( this );
29+
setPanelTitle( tr( "Polygon properties" ) );
2930

3031
//add widget for general composer item properties
3132
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerPolygon );

‎src/app/composer/qgscomposerpolylinewidget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ QgsComposerPolylineWidget::QgsComposerPolylineWidget( QgsComposerPolyline* compo
2626
, mComposerPolyline( composerPolyline )
2727
{
2828
setupUi( this );
29+
setPanelTitle( tr( "Polyline properties" ) );
2930

3031
//add widget for general composer item properties
3132
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerPolyline );

‎src/app/composer/qgscomposerscalebarwidget.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
QgsComposerScaleBarWidget::QgsComposerScaleBarWidget( QgsComposerScaleBar* scaleBar ): QgsComposerItemBaseWidget( nullptr, scaleBar ), mComposerScaleBar( scaleBar )
2828
{
2929
setupUi( this );
30+
setPanelTitle( tr( "Scalebar properties" ) );
31+
3032
connectUpdateSignal();
3133

3234
//add widget for general composer item properties
@@ -261,7 +263,7 @@ void QgsComposerScaleBarWidget::on_mFontColorButton_colorChanged( const QColor&
261263
return;
262264
}
263265

264-
mComposerScaleBar->beginCommand( tr( "Scalebar font color changed" ) );
266+
mComposerScaleBar->beginCommand( tr( "Scalebar font color changed" ), QgsComposerMergeCommand::ScaleBarFontColor );
265267
disconnectUpdateSignal();
266268
mComposerScaleBar->setFontColor( newColor );
267269
mComposerScaleBar->update();
@@ -276,7 +278,7 @@ void QgsComposerScaleBarWidget::on_mFillColorButton_colorChanged( const QColor&
276278
return;
277279
}
278280

279-
mComposerScaleBar->beginCommand( tr( "Scalebar color changed" ) );
281+
mComposerScaleBar->beginCommand( tr( "Scalebar color changed" ), QgsComposerMergeCommand::ScaleBarFillColor );
280282
disconnectUpdateSignal();
281283
QBrush newBrush = mComposerScaleBar->brush();
282284
newBrush.setColor( newColor );
@@ -293,7 +295,7 @@ void QgsComposerScaleBarWidget::on_mFillColor2Button_colorChanged( const QColor
293295
return;
294296
}
295297

296-
mComposerScaleBar->beginCommand( tr( "Scalebar secondary color changed" ) );
298+
mComposerScaleBar->beginCommand( tr( "Scalebar secondary color changed" ), QgsComposerMergeCommand::ScaleBarFill2Color );
297299
disconnectUpdateSignal();
298300
QBrush newBrush = mComposerScaleBar->brush2();
299301
newBrush.setColor( newColor );
@@ -310,7 +312,7 @@ void QgsComposerScaleBarWidget::on_mStrokeColorButton_colorChanged( const QColor
310312
return;
311313
}
312314

313-
mComposerScaleBar->beginCommand( tr( "Scalebar line color changed" ) );
315+
mComposerScaleBar->beginCommand( tr( "Scalebar line color changed" ), QgsComposerMergeCommand::ScaleBarStrokeColor );
314316
disconnectUpdateSignal();
315317
QPen newPen = mComposerScaleBar->pen();
316318
newPen.setColor( newColor );

‎src/app/composer/qgscomposershapewidget.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape* composerShape ): QgsComposerItemBaseWidget( nullptr, composerShape ), mComposerShape( composerShape )
2828
{
2929
setupUi( this );
30+
setPanelTitle( tr( "Shape properties" ) );
3031

3132
//add widget for general composer item properties
3233
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerShape );
@@ -109,19 +110,18 @@ void QgsComposerShapeWidget::on_mShapeStyleButton_clicked()
109110

110111
QgsFillSymbol* newSymbol = mComposerShape->shapeStyleSymbol()->clone();
111112
QgsExpressionContext context = mComposerShape->createExpressionContext();
112-
QgsSymbolSelectorDialog d( newSymbol, QgsStyle::defaultStyle(), coverageLayer, this );
113+
114+
115+
116+
QgsSymbolSelectorWidget* d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), coverageLayer, nullptr );
113117
QgsSymbolWidgetContext symbolContext;
114118
symbolContext.setExpressionContext( &context );
115-
d.setContext( symbolContext );
119+
d->setContext( symbolContext );
116120

117-
if ( d.exec() == QDialog::Accepted )
118-
{
119-
mComposerShape->beginCommand( tr( "Shape style changed" ) );
120-
mComposerShape->setShapeStyleSymbol( newSymbol );
121-
updateShapeStyle();
122-
mComposerShape->endCommand();
123-
}
124-
delete newSymbol;
121+
connect( d, SIGNAL( widgetChanged() ), this, SLOT( updateSymbolFromWidget() ) );
122+
connect( d, SIGNAL( panelAccepted( QgsPanelWidget* ) ), this, SLOT( cleanUpSymbolSelector( QgsPanelWidget* ) ) );
123+
openPanel( d );
124+
mComposerShape->beginCommand( tr( "Shape style changed" ) );
125125
}
126126

127127
void QgsComposerShapeWidget::updateShapeStyle()
@@ -182,5 +182,22 @@ void QgsComposerShapeWidget::toggleRadiusSpin( const QString& shapeText )
182182
}
183183
}
184184

185+
void QgsComposerShapeWidget::updateSymbolFromWidget()
186+
{
187+
QgsSymbolSelectorWidget* w = qobject_cast<QgsSymbolSelectorWidget*>( sender() );
188+
mComposerShape->setShapeStyleSymbol( dynamic_cast< QgsFillSymbol* >( w->symbol() ) );
189+
}
190+
191+
void QgsComposerShapeWidget::cleanUpSymbolSelector( QgsPanelWidget* container )
192+
{
193+
QgsSymbolSelectorWidget* w = qobject_cast<QgsSymbolSelectorWidget*>( container );
194+
if ( !w )
195+
return;
196+
197+
delete w->symbol();
198+
updateShapeStyle();
199+
mComposerShape->endCommand();
200+
}
201+
185202

186203

‎src/app/composer/qgscomposershapewidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class QgsComposerShapeWidget: public QgsComposerItemBaseWidget, private Ui::QgsC
4949

5050
/** Enables or disables the rounded radius spin box based on shape type*/
5151
void toggleRadiusSpin( const QString& shapeText );
52+
void updateSymbolFromWidget();
53+
void cleanUpSymbolSelector( QgsPanelWidget* container );
5254
};
5355

5456
#endif // QGSCOMPOSERSHAPEWIDGET_H

‎src/core/composer/qgscomposeritemcommand.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,14 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
8787
//composer label
8888
ComposerLabelSetText,
8989
ComposerLabelSetId,
90+
ComposerLabelFontColor,
9091
//composer map
9192
ComposerMapRotation,
9293
ComposerMapAnnotationDistance,
94+
ComposerMapGridFramePenColor,
95+
ComposerMapGridFrameFill1Color,
96+
ComposerMapGridFrameFill2Color,
97+
ComposerMapGridAnnotationFontColor,
9398
//composer legend
9499
ComposerLegendText,
95100
LegendColumnCount,
@@ -107,8 +112,12 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
107112
LegendBoxSpace,
108113
LegendColumnSpace,
109114
LegendRasterBorderWidth,
115+
LegendFontColor,
116+
LegendRasterBorderColor,
110117
//composer picture
111118
ComposerPictureRotation,
119+
ComposerPictureFillColor,
120+
ComposerPictureOutlineColor,
112121
// composer scalebar
113122
ScaleBarLineWidth,
114123
ScaleBarHeight,
@@ -119,6 +128,10 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
119128
ScaleBarMapUnitsSegment,
120129
ScaleBarLabelBarSize,
121130
ScaleBarBoxContentSpace,
131+
ScaleBarFontColor,
132+
ScaleBarFillColor,
133+
ScaleBarFill2Color,
134+
ScaleBarStrokeColor,
122135
// composer table
123136
TableMaximumFeatures,
124137
TableMargin,
@@ -128,9 +141,13 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
128141
ShapeOutlineWidth,
129142
//composer arrow
130143
ArrowOutlineWidth,
144+
ArrowHeadFillColor,
145+
ArrowHeadOutlineColor,
131146
ArrowHeadWidth,
132147
//item
133148
ItemOutlineWidth,
149+
ItemOutlineColor,
150+
ItemBackgroundColor,
134151
ItemMove,
135152
ItemRotation,
136153
ItemTransparency,

‎src/core/composer/qgscomposermultiframecommand.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ class CORE_EXPORT QgsComposerMultiFrameMergeCommand: public QgsComposerMultiFram
7878
TableMaximumFeatures,
7979
TableMargin,
8080
TableGridStrokeWidth,
81-
TableCellStyle
81+
TableCellStyle,
82+
TableHeaderFontColor,
83+
TableContentFontColor,
84+
TableGridColor,
85+
TableBackgroundColor,
8286
};
8387

8488
QgsComposerMultiFrameMergeCommand( Context c, QgsComposerMultiFrame* multiFrame, const QString& text );

0 commit comments

Comments
 (0)
Please sign in to comment.