1 /***************************************************************************
2   qgsprocessingdxflayerswidgetwrapper.cpp
3   ---------------------
4   Date                 : September 2020
5   Copyright            : (C) 2020 by Alexander Bruy
6   Email                : alexander dot bruy 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  ***************************************************************************/
15 
16 #include "qgsprocessingdxflayerswidgetwrapper.h"
17 
18 #include <QBoxLayout>
19 #include <QLineEdit>
20 #include <QMessageBox>
21 #include <QPushButton>
22 #include <QStandardItemModel>
23 #include <QToolButton>
24 
25 #include "qgspanelwidget.h"
26 #include "qgsprocessingparameters.h"
27 #include "qgsprocessingoutputs.h"
28 #include "qgsprocessingparameterdxflayers.h"
29 
30 /// @cond private
31 
32 //
33 // QgsProcessingDxfLayerDetailsWidget
34 //
35 
QgsProcessingDxfLayerDetailsWidget(const QVariant & value,QgsProject * project)36 QgsProcessingDxfLayerDetailsWidget::QgsProcessingDxfLayerDetailsWidget( const QVariant &value, QgsProject *project )
37 {
38   setupUi( this );
39 
40   mFieldsComboBox->setAllowEmptyFieldName( true );
41 
42   mContext.setProject( project );
43 
44   const QgsDxfExport::DxfLayer layer = QgsProcessingParameterDxfLayers::variantMapAsLayer( value.toMap(), mContext );
45   mLayer = layer.layer();
46 
47   if ( !mLayer )
48     return;
49 
50   mFieldsComboBox->setLayer( mLayer );
51   mFieldsComboBox->setCurrentIndex( layer.layerOutputAttributeIndex() );
52 
53   connect( mFieldsComboBox, &QgsFieldComboBox::fieldChanged, this, &QgsPanelWidget::widgetChanged );
54 }
55 
value() const56 QVariant QgsProcessingDxfLayerDetailsWidget::value() const
57 {
58   const int index = mLayer->fields().lookupField( mFieldsComboBox->currentField() );
59   const QgsDxfExport::DxfLayer layer( mLayer, index );
60   return QgsProcessingParameterDxfLayers::layerAsVariantMap( layer );
61 }
62 
63 
64 //
65 // QgsProcessingDxfLayersPanelWidget
66 //
67 
QgsProcessingDxfLayersPanelWidget(const QVariant & value,QgsProject * project,QWidget * parent)68 QgsProcessingDxfLayersPanelWidget::QgsProcessingDxfLayersPanelWidget(
69   const QVariant &value,
70   QgsProject *project,
71   QWidget *parent )
72   : QgsProcessingMultipleSelectionPanelWidget( QVariantList(), QVariantList(), parent )
73   , mProject( project )
74 {
75   connect( listView(), &QListView::doubleClicked, this, &QgsProcessingDxfLayersPanelWidget::configureLayer );
76 
77   QPushButton *configureLayerButton = new QPushButton( tr( "Configure Layer…" ) );
78   connect( configureLayerButton, &QPushButton::clicked, this, &QgsProcessingDxfLayersPanelWidget::configureLayer );
79   buttonBox()->addButton( configureLayerButton, QDialogButtonBox::ActionRole );
80 
81   // populate the list: first layers already selected, then layers from project not yet selected
82   mContext.setProject( project );
83 
84   QSet<const QgsVectorLayer *> seenVectorLayers;
85   const QVariantList valueList = value.toList();
86   for ( const QVariant &v : valueList )
87   {
88     const QgsDxfExport::DxfLayer layer = QgsProcessingParameterDxfLayers::variantMapAsLayer( v.toMap(), mContext );
89     if ( !layer.layer() )
90       continue;  // skip any invalid layers
91 
92     addOption( v, titleForLayer( layer ), true );
93     seenVectorLayers.insert( layer.layer() );
94   }
95 
96   const QList<QgsVectorLayer *> options = QgsProcessingUtils::compatibleVectorLayers( project, QList< int >() );
97   for ( const QgsVectorLayer *layer : options )
98   {
99     if ( seenVectorLayers.contains( layer ) )
100       continue;
101 
102     QVariantMap vm;
103     vm["layer"] = layer->id();
104     vm["attributeIndex"] = -1;
105 
106     const QString title = layer->name();
107     addOption( vm, title, false );
108   }
109 }
110 
configureLayer()111 void QgsProcessingDxfLayersPanelWidget::configureLayer()
112 {
113   const QModelIndexList selection = listView()->selectionModel()->selectedIndexes();
114   if ( selection.size() != 1 )
115   {
116     QMessageBox::warning( this, tr( "Configure Layer" ), tr( "Please select a single layer." ) );
117     return;
118   }
119 
120   QStandardItem *item = mModel->itemFromIndex( selection[0] );
121   const QVariant value = item->data( Qt::UserRole );
122 
123   QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
124   if ( panel && panel->dockMode() )
125   {
126     QgsProcessingDxfLayerDetailsWidget *widget = new QgsProcessingDxfLayerDetailsWidget( value, mProject );
127     widget->setPanelTitle( tr( "Configure Layer" ) );
128     widget->buttonBox()->hide();
129 
130     connect( widget, &QgsProcessingDxfLayerDetailsWidget::widgetChanged, this, [ = ]()
131     {
132       setItemValue( item, widget->value() );
133     } );
134     panel->openPanel( widget );
135   }
136   else
137   {
138     QDialog dlg;
139     dlg.setWindowTitle( tr( "Configure Layer" ) );
140     QVBoxLayout *vLayout = new QVBoxLayout();
141     QgsProcessingDxfLayerDetailsWidget *widget = new QgsProcessingDxfLayerDetailsWidget( value, mProject );
142     vLayout->addWidget( widget );
143     connect( widget->buttonBox(), &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
144     connect( widget->buttonBox(), &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
145     dlg.setLayout( vLayout );
146     if ( dlg.exec() )
147     {
148       setItemValue( item, widget->value() );
149     }
150   }
151 }
152 
setItemValue(QStandardItem * item,const QVariant & value)153 void QgsProcessingDxfLayersPanelWidget::setItemValue( QStandardItem *item, const QVariant &value )
154 {
155   mContext.setProject( mProject );
156 
157   const QgsDxfExport::DxfLayer layer = QgsProcessingParameterDxfLayers::variantMapAsLayer( value.toMap(), mContext );
158 
159   item->setText( titleForLayer( layer ) );
160   item->setData( value, Qt::UserRole );
161 }
162 
titleForLayer(const QgsDxfExport::DxfLayer & layer)163 QString QgsProcessingDxfLayersPanelWidget::titleForLayer( const QgsDxfExport::DxfLayer &layer )
164 {
165   QString title = layer.layer()->name();
166 
167   if ( layer.layerOutputAttributeIndex() != -1 )
168     title += tr( " [split attribute: %1]" ).arg( layer.splitLayerAttribute() );
169 
170   return title;
171 }
172 
173 
174 //
175 // QgsProcessingDxfLayersWidget
176 //
177 
QgsProcessingDxfLayersWidget(QWidget * parent)178 QgsProcessingDxfLayersWidget::QgsProcessingDxfLayersWidget( QWidget *parent )
179   : QWidget( parent )
180 {
181   QHBoxLayout *hl = new QHBoxLayout();
182   hl->setContentsMargins( 0, 0, 0, 0 );
183 
184   mLineEdit = new QLineEdit();
185   mLineEdit->setEnabled( false );
186   hl->addWidget( mLineEdit, 1 );
187 
188   mToolButton = new QToolButton();
189   mToolButton->setText( QString( QChar( 0x2026 ) ) );
190   hl->addWidget( mToolButton );
191 
192   setLayout( hl );
193 
194   updateSummaryText();
195 
196   connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingDxfLayersWidget::showDialog );
197 }
198 
setValue(const QVariant & value)199 void QgsProcessingDxfLayersWidget::setValue( const QVariant &value )
200 {
201   if ( value.isValid() )
202     mValue = value.type() == QVariant::List ? value.toList() : QVariantList() << value;
203   else
204     mValue.clear();
205 
206   updateSummaryText();
207   emit changed();
208 }
209 
setProject(QgsProject * project)210 void QgsProcessingDxfLayersWidget::setProject( QgsProject *project )
211 {
212   mProject = project;
213 }
214 
showDialog()215 void QgsProcessingDxfLayersWidget::showDialog()
216 {
217   QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
218   if ( panel && panel->dockMode() )
219   {
220     QgsProcessingDxfLayersPanelWidget *widget = new QgsProcessingDxfLayersPanelWidget( mValue, mProject );
221     widget->setPanelTitle( tr( "Input layers" ) );
222     connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [ = ]()
223     {
224       setValue( widget->selectedOptions() );
225     } );
226     connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
227     panel->openPanel( widget );
228   }
229   else
230   {
231     QDialog dlg;
232     dlg.setWindowTitle( tr( "Input layers" ) );
233     QVBoxLayout *vLayout = new QVBoxLayout();
234     QgsProcessingDxfLayersPanelWidget *widget = new QgsProcessingDxfLayersPanelWidget( mValue, mProject );
235     vLayout->addWidget( widget );
236     widget->buttonBox()->addButton( QDialogButtonBox::Cancel );
237     connect( widget->buttonBox(), &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
238     connect( widget->buttonBox(), &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
239     dlg.setLayout( vLayout );
240     if ( dlg.exec() )
241     {
242       setValue( widget->selectedOptions() );
243     }
244   }
245 }
246 
updateSummaryText()247 void QgsProcessingDxfLayersWidget::updateSummaryText()
248 {
249   mLineEdit->setText( tr( "%1 vector layers selected" ).arg( mValue.count() ) );
250 }
251 
252 
253 //
254 // QgsProcessingDxfLayersWidgetWrapper
255 //
256 
QgsProcessingDxfLayersWidgetWrapper(const QgsProcessingParameterDefinition * parameter,QgsProcessingGui::WidgetType type,QWidget * parent)257 QgsProcessingDxfLayersWidgetWrapper::QgsProcessingDxfLayersWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
258   : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
259 {
260 }
261 
parameterType() const262 QString QgsProcessingDxfLayersWidgetWrapper::parameterType() const
263 {
264   return QgsProcessingParameterDxfLayers::typeName();
265 }
266 
createWidgetWrapper(const QgsProcessingParameterDefinition * parameter,QgsProcessingGui::WidgetType type)267 QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDxfLayersWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
268 {
269   return new QgsProcessingDxfLayersWidgetWrapper( parameter, type );
270 }
271 
createWidget()272 QWidget *QgsProcessingDxfLayersWidgetWrapper::createWidget()
273 {
274   mPanel = new QgsProcessingDxfLayersWidget( nullptr );
275   mPanel->setProject( widgetContext().project() );
276   connect( mPanel, &QgsProcessingDxfLayersWidget::changed, this, [ = ]
277   {
278     emit widgetValueHasChanged( this );
279   } );
280   return mPanel;
281 }
282 
setWidgetContext(const QgsProcessingParameterWidgetContext & context)283 void QgsProcessingDxfLayersWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
284 {
285   QgsAbstractProcessingParameterWidgetWrapper::setWidgetContext( context );
286   if ( mPanel )
287   {
288     mPanel->setProject( context.project() );
289   }
290 }
291 
setWidgetValue(const QVariant & value,QgsProcessingContext & context)292 void QgsProcessingDxfLayersWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
293 {
294   Q_UNUSED( context )
295   if ( mPanel )
296   {
297     mPanel->setValue( value );
298   }
299 }
300 
widgetValue() const301 QVariant QgsProcessingDxfLayersWidgetWrapper::widgetValue() const
302 {
303   return mPanel ? mPanel->value() : QVariant();
304 }
305 
compatibleParameterTypes() const306 QStringList QgsProcessingDxfLayersWidgetWrapper::compatibleParameterTypes() const
307 {
308   return QStringList()
309          << QgsProcessingParameterMultipleLayers::typeName()
310          << QgsProcessingParameterMapLayer::typeName()
311          << QgsProcessingParameterVectorLayer::typeName()
312          << QgsProcessingParameterFeatureSource::typeName()
313          << QgsProcessingParameterFile::typeName()
314          << QgsProcessingParameterString::typeName();
315 }
316 
compatibleOutputTypes() const317 QStringList QgsProcessingDxfLayersWidgetWrapper::compatibleOutputTypes() const
318 {
319   return QStringList()
320          << QgsProcessingOutputString::typeName()
321          << QgsProcessingOutputMapLayer::typeName()
322          << QgsProcessingOutputVectorLayer::typeName()
323          << QgsProcessingOutputMultipleLayers::typeName()
324          << QgsProcessingOutputFile::typeName();
325 }
326 
327 /// @endcond
328