QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsattributeeditor.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsattributeeditor.cpp - description
3  -------------------
4  begin : July 2009
5  copyright : (C) 2009 by Jürgen E. Fischer
6  email : [email protected]
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 #include "qgseditorwidgetfactory.h"
20 #include "qgseditorwidgetregistry.h"
21 #include "qgseditorwidgetwrapper.h"
22 
23 #include "qgsattributedialog.h"
24 #include "qgsattributeeditor.h"
27 #include "qgscolorbutton.h"
28 #include "qgsexpression.h"
29 #include "qgsfieldvalidator.h"
30 #include "qgsfilterlineedit.h"
31 #include "qgslogger.h"
32 #include "qgslonglongvalidator.h"
33 #include "qgsmaplayerregistry.h"
35 #include "qgsrelationeditor.h"
36 #include "qgsrelationmanager.h"
37 #include "qgsvectordataprovider.h"
38 #include "qgsvectorlayer.h"
39 
40 #include <QScrollArea>
41 #include <QPushButton>
42 #include <QLineEdit>
43 #include <QTextEdit>
44 #include <QFileDialog>
45 #include <QComboBox>
46 #include <QListWidget>
47 #include <QCheckBox>
48 #include <QSpinBox>
49 #include <QCompleter>
50 #include <QHBoxLayout>
51 #include <QPlainTextEdit>
52 #include <QDial>
53 #include <QCalendarWidget>
54 #include <QDialogButtonBox>
55 #include <QSettings>
56 #include <QDir>
57 #include <QUuid>
58 #include <QGroupBox>
59 #include <QLabel>
60 #include <QWebView>
61 #include <QDesktopServices>
62 
64 {
65  QPushButton *pb = qobject_cast<QPushButton *>( sender() );
66  if ( !pb )
67  return;
68 
69  QWidget *hbox = qobject_cast<QWidget *>( pb->parent() );
70  if ( !hbox )
71  return;
72 
73  QLineEdit *le = hbox->findChild<QLineEdit *>();
74  if ( !le )
75  return;
76 
77  QString fileName = QFileDialog::getOpenFileName( 0 , tr( "Select a file" ), QFileInfo( le->text() ).absolutePath() );
78  if ( fileName.isNull() )
79  return;
80 
81  le->setText( QDir::toNativeSeparators( fileName ) );
82 }
83 
85 {
86  QPushButton *pb = qobject_cast<QPushButton *>( sender() );
87  if ( !pb )
88  return;
89 
90  QWidget *hbox = qobject_cast<QWidget *>( pb->parent() );
91  if ( !hbox )
92  return;
93 
94  QLineEdit *le = hbox->findChild<QLineEdit *>();
95  if ( !le )
96  return;
97 
98  QDialog *dlg = new QDialog();
99  dlg->setWindowTitle( tr( "Select a date" ) );
100  QVBoxLayout *vl = new QVBoxLayout( dlg );
101 
102  const QgsFieldValidator *v = dynamic_cast<const QgsFieldValidator *>( le->validator() );
103  QString dateFormat = v ? v->dateFormat() : "yyyy-MM-dd";
104 
105  QCalendarWidget *cw = new QCalendarWidget( dlg );
106  QString prevValue = le->text();
107  cw->setSelectedDate( QDate::fromString( prevValue, dateFormat ) );
108  vl->addWidget( cw );
109 
110  QDialogButtonBox *buttonBox = new QDialogButtonBox( dlg );
111  buttonBox->addButton( QDialogButtonBox::Ok );
112  buttonBox->addButton( QDialogButtonBox::Cancel );
113  vl->addWidget( buttonBox );
114 
115  connect( buttonBox, SIGNAL( accepted() ), dlg, SLOT( accept() ) );
116  connect( buttonBox, SIGNAL( rejected() ), dlg, SLOT( reject() ) );
117 
118  if ( dlg->exec() == QDialog::Accepted )
119  {
120  QString newValue = cw->selectedDate().toString( dateFormat );
121  le->setText( newValue );
122  le->setModified( newValue != prevValue );
123  }
124 }
125 
126 void QgsAttributeEditor::loadUrl( const QString &url )
127 {
128  QLineEdit *le = qobject_cast<QLineEdit *>( sender() );
129  if ( !le )
130  return;
131 
132  QWidget *hbox = qobject_cast<QWidget *>( le->parent() );
133  if ( !hbox )
134  return;
135 
136  QWebView *ww = hbox->findChild<QWebView *>();
137  if ( !ww )
138  return;
139 
140  ww->load( url );
141 }
142 
143 void QgsAttributeEditor::loadPixmap( const QString &name )
144 {
145  QLineEdit *le = qobject_cast<QLineEdit *>( sender() );
146  if ( !le )
147  return;
148 
149  QWidget *hbox = qobject_cast<QWidget *>( le->parent() );
150  if ( !hbox )
151  return;
152 
153  QLabel *lw = hbox->findChild<QLabel *>();
154  if ( !lw )
155  return;
156 
157  QPixmap pm( name );
158  if ( pm.isNull() )
159  return;
160 
161  QSize size( mLayer->widgetSize( mIdx ) );
162  if ( size.width() == 0 && size.height() > 0 )
163  {
164  size.setWidth( size.height() * pm.size().width() / pm.size().height() );
165  }
166  else if ( size.width() > 0 && size.height() == 0 )
167  {
168  size.setHeight( size.width() * pm.size().height() / pm.size().width() );
169  }
170 
171  pm = pm.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
172 
173  lw->setPixmap( pm );
174  lw->setMinimumSize( size );
175 }
176 
178 {
179  QPushButton *pb = qobject_cast<QPushButton *>( sender() );
180  if ( !pb )
181  return;
182 
183  QWidget *hbox = qobject_cast<QWidget *>( pb->parent() );
184  if ( !hbox )
185  return;
186 
187  QWebView *ww = hbox->findChild<QWebView *>();
188  if ( !ww )
189  return;
190 
191  QLineEdit *le = hbox->findChild<QLineEdit *>();
192  if ( !le )
193  return;
194 
195  le->blockSignals( true );
196  le->setText( ww->url().toString() );
197  le->blockSignals( false );
198 }
199 
201 {
202  QPushButton *pb = qobject_cast<QPushButton *>( sender() );
203  if ( !pb )
204  return;
205 
206  QWidget *hbox = qobject_cast<QWidget *>( pb->parent() );
207  if ( !hbox )
208  return;
209 
210  QWebView *ww = hbox->findChild<QWebView *>();
211  if ( !ww )
212  return;
213 
214  QDesktopServices::openUrl( ww->url().toString() );
215 }
216 
218 {
219  QString color;
220  QgsColorButton *scb = qobject_cast<QgsColorButton *>( sender() );
221  QLineEdit *sle = qobject_cast<QLineEdit *>( sender() );
222 
223  if ( !scb && !sle )
224  return;
225 
226  QWidget *hbox = qobject_cast<QWidget *>( sender()->parent() );
227  if ( !hbox )
228  return;
229 
230  QgsColorButton *cb = hbox->findChild<QgsColorButton *>();
231  if ( !cb )
232  return;
233 
234  QLineEdit *le = hbox->findChild<QLineEdit *>();
235  if ( !le )
236  return;
237 
238  if ( scb )
239  {
240  le->blockSignals( true );
241  le->setText( scb->color().name() );
242  le->blockSignals( false );
243  }
244 
245  if ( sle )
246  {
247  cb->blockSignals( true );
248  cb->setColor( QColor( sle->text() ) );
249  cb->blockSignals( false );
250  }
251 }
252 
253 QComboBox *QgsAttributeEditor::comboBox( QWidget *editor, QWidget *parent )
254 {
255  QComboBox *cb = 0;
256  if ( editor )
257  cb = qobject_cast<QComboBox *>( editor );
258  else
259  cb = new QComboBox( parent );
260 
261  return cb;
262 }
263 
264 QListWidget *QgsAttributeEditor::listWidget( QWidget *editor, QWidget *parent )
265 {
266  QListWidget *lw = 0;
267  if ( editor )
268  lw = qobject_cast<QListWidget *>( editor );
269  else
270  lw = new QListWidget( parent );
271 
272  return lw;
273 }
274 
275 QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value )
276 {
278  context.addProxyWidgets( vl, QMap<int, QWidget*>() );
279 
280  return createAttributeEditor( parent, editor, vl, idx, value, context );
281 }
282 
283 QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value, QMap<int, QWidget*> &proxyWidgets )
284 {
286  context.addProxyWidgets( vl, proxyWidgets );
287 
288  return createAttributeEditor( parent, editor, vl, idx, value, context );
289 }
290 
291 QWidget* QgsAttributeEditor::createAttributeEditor( QWidget* parent, QWidget* editor, QgsVectorLayer* vl, int idx, const QVariant& value, QgsAttributeEditorContext& context )
292 {
293  if ( !vl )
294  return 0;
295 
296  QWidget *myWidget = 0;
297  QgsVectorLayer::EditType editType = vl->editType( idx );
298  const QgsField &field = vl->pendingFields()[idx];
299  QVariant::Type myFieldType = field.type();
300 
301  bool synchronized = false;
302 
303  switch ( editType )
304  {
306  {
307  QList<QVariant> values;
308  vl->dataProvider()->uniqueValues( idx, values );
309 
310  QComboBox *cb = comboBox( editor, parent );
311  if ( cb )
312  {
313  cb->setEditable( false );
314 
315  for ( QList<QVariant>::iterator it = values.begin(); it != values.end(); ++it )
316  cb->addItem( it->toString(), it->toString() );
317 
318  myWidget = cb;
319  }
320 
321  }
322  break;
323 
325  {
326  QStringList enumValues;
327  vl->dataProvider()->enumValues( idx, enumValues );
328 
329  QComboBox *cb = comboBox( editor, parent );
330  if ( cb )
331  {
332  QStringList::const_iterator s_it = enumValues.constBegin();
333  for ( ; s_it != enumValues.constEnd(); ++s_it )
334  {
335  cb->addItem( *s_it, *s_it );
336  }
337 
338  myWidget = cb;
339  }
340  }
341  break;
342 
344  {
345  const QMap<QString, QVariant> &map = vl->valueMap( idx );
346 
347  QComboBox *cb = comboBox( editor, parent );
348  if ( cb )
349  {
350  for ( QMap<QString, QVariant>::const_iterator it = map.begin(); it != map.end(); ++it )
351  {
352  cb->addItem( it.key(), it.value() );
353  }
354 
355  myWidget = cb;
356  }
357  }
358  break;
359 
361  {
362  const QgsVectorLayer::ValueRelationData &data = vl->valueRelation( idx );
363 
364  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( data.mLayer ) );
365  QMap< QString, QString > map;
366 
367  if ( layer )
368  {
369  int ki = layer->fieldNameIndex( data.mOrderByValue ? data.mValue : data.mKey );
370  int vi = layer->fieldNameIndex( data.mOrderByValue ? data.mKey : data.mValue );
371 
372  QgsExpression *e = 0;
373  if ( !data.mFilterExpression.isEmpty() )
374  {
375  e = new QgsExpression( data.mFilterExpression );
376  if ( e->hasParserError() || !e->prepare( layer->pendingFields() ) )
377  ki = -1;
378  }
379 
380  if ( ki >= 0 && vi >= 0 )
381  {
382  QSet<int> attributes;
383  attributes << ki << vi;
384 
386 
387  if ( e )
388  {
389  if ( e->needsGeometry() )
391 
392  foreach ( const QString &field, e->referencedColumns() )
393  {
394  int idx = layer->fieldNameIndex( field );
395  if ( idx < 0 )
396  continue;
397  attributes << idx;
398  }
399  }
400 
401  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFlags( flags ).setSubsetOfAttributes( attributes.toList() ) );
402  QgsFeature f;
403  while ( fit.nextFeature( f ) )
404  {
405  if ( e && !e->evaluate( &f ).toBool() )
406  continue;
407 
408  map.insert( f.attribute( ki ).toString(), f.attribute( vi ).toString() );
409  }
410  }
411  }
412 
413  if ( !data.mAllowMulti )
414  {
415  QComboBox *cb = comboBox( editor, parent );
416  if ( cb )
417  {
418  if ( data.mAllowNull )
419  {
420  QSettings settings;
421  cb->addItem( tr( "(no selection)" ), settings.value( "qgis/nullValue", "NULL" ).toString() );
422  }
423 
424  for ( QMap< QString, QString >::const_iterator it = map.begin(); it != map.end(); ++it )
425  {
426  if ( data.mOrderByValue )
427  cb->addItem( it.key(), it.value() );
428  else
429  cb->addItem( it.value(), it.key() );
430  }
431 
432  myWidget = cb;
433  }
434  }
435  else
436  {
437  QListWidget *lw = listWidget( editor, parent );
438  if ( lw )
439  {
440  QStringList checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( "," );
441 
442  for ( QMap< QString, QString >::const_iterator it = map.begin(); it != map.end(); ++it )
443  {
444  QListWidgetItem *item;
445  if ( data.mOrderByValue )
446  {
447  item = new QListWidgetItem( it.key() );
448  item->setData( Qt::UserRole, it.value() );
449  item->setCheckState( checkList.contains( it.value() ) ? Qt::Checked : Qt::Unchecked );
450  }
451  else
452  {
453  item = new QListWidgetItem( it.value() );
454  item->setData( Qt::UserRole, it.key() );
455  item->setCheckState( checkList.contains( it.key() ) ? Qt::Checked : Qt::Unchecked );
456  }
457  lw->addItem( item );
458  }
459 
460  myWidget = lw;
461  }
462  }
463  }
464  break;
465 
467  {
468  const QString& widgetType = vl->editorWidgetV2( idx );
469  const QgsEditorWidgetConfig widgetConfig = vl->editorWidgetV2Config( idx );
470 
471  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, vl, idx, widgetConfig, editor, parent );
472 
473  if ( eww )
474  myWidget = eww->widget();
475  }
476  break;
477 
479  {
480  QMap<QString, QString> classes;
481 
482  const QgsCategorizedSymbolRendererV2 *csr = dynamic_cast<const QgsCategorizedSymbolRendererV2 *>( vl->rendererV2() );
483  if ( csr )
484  {
485  const QgsCategoryList &categories = (( QgsCategorizedSymbolRendererV2 * )csr )->categories(); // FIXME: QgsCategorizedSymbolRendererV2::categories() should be const
486  for ( int i = 0; i < categories.size(); i++ )
487  {
488  QString label = categories[i].label();
489  QString value = categories[i].value().toString();
490  if ( label.isEmpty() )
491  label = value;
492  classes.insert( value, label );
493  }
494  }
495 
496  QComboBox *cb = comboBox( editor, parent );
497  if ( cb )
498  {
499  for ( QMap<QString, QString>::const_iterator it = classes.begin(); it != classes.end(); ++it )
500  {
501  cb->addItem( it.value(), it.key() );
502  }
503 
504  myWidget = cb;
505  }
506  }
507  break;
508 
512  {
513  if ( myFieldType == QVariant::Int || myFieldType == QVariant::LongLong )
514  {
515  int min = vl->range( idx ).mMin.toInt();
516  int max = vl->range( idx ).mMax.toInt();
517  int step = vl->range( idx ).mStep.toInt();
518 
519  if ( editType == QgsVectorLayer::EditRange )
520  {
521  QSpinBox *sb = 0;
522 
523  if ( editor )
524  sb = qobject_cast<QSpinBox *>( editor );
525  else
526  sb = new QSpinBox( parent );
527 
528  if ( sb )
529  {
530  sb->setRange( min, max );
531  sb->setSingleStep( step );
532 
533  myWidget = sb;
534  }
535  }
536  else
537  {
538  QAbstractSlider *sl = 0;
539 
540  if ( editor )
541  {
542  sl = qobject_cast<QAbstractSlider*>( editor );
543  }
544  else if ( editType == QgsVectorLayer::DialRange )
545  {
546  sl = new QDial( parent );
547  }
548  else
549  {
550  sl = new QSlider( Qt::Horizontal, parent );
551  }
552 
553  if ( sl )
554  {
555  sl->setRange( min, max );
556  sl->setSingleStep( step );
557 
558  myWidget = sl;
559  }
560  }
561  break;
562  }
563  else if ( myFieldType == QVariant::Double )
564  {
565  QDoubleSpinBox *dsb = 0;
566  if ( editor )
567  dsb = qobject_cast<QDoubleSpinBox*>( editor );
568  else
569  dsb = new QDoubleSpinBox( parent );
570 
571  if ( dsb )
572  {
573  double min = vl->range( idx ).mMin.toDouble();
574  double max = vl->range( idx ).mMax.toDouble();
575  double step = vl->range( idx ).mStep.toDouble();
576 
577  dsb->setRange( min, max );
578  dsb->setSingleStep( step );
579 
580  myWidget = dsb;
581  }
582  break;
583  }
584  }
585 
587  {
588  QCheckBox *cb = 0;
589  QGroupBox *gb = 0;
590  if ( editor )
591  {
592  gb = qobject_cast<QGroupBox *>( editor );
593  cb = qobject_cast<QCheckBox*>( editor );
594  }
595  else
596  cb = new QCheckBox( parent );
597 
598  if ( cb )
599  {
600  myWidget = cb;
601  break;
602  }
603  else if ( gb )
604  {
605  myWidget = gb;
606  break;
607  }
608  }
609 
610  // fall-through
611 
617  {
618  QLineEdit *le = 0;
619  QTextEdit *te = 0;
620  QPlainTextEdit *pte = 0;
621  QComboBox * cb = 0;
622 
623  if ( editor )
624  {
625  le = qobject_cast<QLineEdit *>( editor );
626  te = qobject_cast<QTextEdit *>( editor );
627  pte = qobject_cast<QPlainTextEdit *>( editor );
628  cb = qobject_cast<QComboBox *>( editor );
629  }
630  else if ( editType == QgsVectorLayer::TextEdit )
631  {
632  pte = new QPlainTextEdit( parent );
633  }
634  else
635  {
636  le = new QgsFilterLineEdit( parent );
637  }
638 
639  if ( le )
640  {
641  if ( editType == QgsVectorLayer::UniqueValuesEditable )
642  {
643  QList<QVariant> values;
644  vl->dataProvider()->uniqueValues( idx, values );
645 
646  QStringList svalues;
647  for ( QList<QVariant>::const_iterator it = values.begin(); it != values.end(); ++it )
648  svalues << it->toString();
649 
650  QCompleter *c = new QCompleter( svalues );
651  c->setCompletionMode( QCompleter::PopupCompletion );
652  le->setCompleter( c );
653  }
654 
655  if ( editType == QgsVectorLayer::UuidGenerator )
656  {
657  le->setReadOnly( true );
658  }
659 
660  le->setValidator( new QgsFieldValidator( le, field, vl->dateFormat( idx ) ) );
661 
662  myWidget = le;
663  }
664 
665  if ( te )
666  {
667  te->setAcceptRichText( true );
668  myWidget = te;
669  }
670 
671  if ( pte )
672  {
673  myWidget = pte;
674  }
675 
676  if ( cb )
677  {
678  if ( cb->isEditable() )
679  cb->setValidator( new QgsFieldValidator( cb, field, vl->dateFormat( idx ) ) );
680  myWidget = cb;
681  }
682 
683  if ( myWidget )
684  {
685  if ( editType == QgsVectorLayer::Immutable )
686  {
687  myWidget->setDisabled( true );
688  }
689 
690  QgsStringRelay* relay = NULL;
691 
692  QWidget* pwdg = context.proxyWidget( vl, idx );
693  if ( pwdg )
694  {
695  relay = pwdg->property( "QgisAttrEditProxy" ).value<QgsStringRelay*>();
696  }
697  else
698  {
699  relay = new QgsStringRelay( myWidget );
700  }
701 
702  const char* rSlot = SLOT( changeText( QString ) );
703  const char* rSig = SIGNAL( textChanged( QString ) );
704  const char* wSlot = SLOT( setText( QString ) );
705  const char* wSig = SIGNAL( textChanged( QString ) );
706  if ( te || pte )
707  {
708  rSlot = SLOT( changeText() );
709  wSig = SIGNAL( textChanged() );
710  }
711  if ( pte )
712  {
713  wSlot = SLOT( setPlainText( QString ) );
714  }
715  if ( cb && cb->isEditable() )
716  {
717  wSlot = SLOT( setEditText( QString ) );
718  wSig = SIGNAL( editTextChanged( QString ) );
719  }
720 
721  synchronized = connect( relay, rSig, myWidget, wSlot );
722  synchronized &= connect( myWidget, wSig, relay, rSlot );
723 
724  // store list of proxies in relay
725  relay->appendProxy( myWidget );
726 
727  if ( !cb || cb->isEditable() )
728  {
729  myWidget->setProperty( "QgisAttrEditSlot", QVariant( QByteArray( wSlot ) ) );
730  myWidget->setProperty( "QgisAttrEditProxy", QVariant::fromValue<QgsStringRelay*>( relay ) );
731  }
732  }
733  }
734  break;
735 
737  myWidget = 0;
738  break;
739 
745  {
746  QCalendarWidget *cw = qobject_cast<QCalendarWidget *>( editor );
747  if ( cw )
748  {
749  myWidget = cw;
750  break;
751  }
752 
753  QWebView *ww = qobject_cast<QWebView *>( editor );
754  if ( ww )
755  {
756  ww->page()->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
757  // ww->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
758  ww->settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true );
759  ww->settings()->setAttribute( QWebSettings::JavascriptCanOpenWindows, true );
760 #ifdef QGISDEBUG
761  ww->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
762 #endif
763 
764  QSize size( vl->widgetSize( idx ) );
765  if ( size.width() > 0 || size.height() > 0 )
766  {
767  if ( size.width() == 0 )
768  size.setWidth( 1 );
769  if ( size.height() == 0 )
770  size.setHeight( 1 );
771  ww->setMinimumSize( size );
772  }
773 
774  myWidget = ww;
775  break;
776  }
777 
778  QLabel *lw = qobject_cast<QLabel *>( editor );
779  if ( lw )
780  {
781  myWidget = lw;
782  break;
783  }
784 
785  QgsColorButton *cb = qobject_cast<QgsColorButton *>( editor );
786  if ( cb )
787  {
788  myWidget = cb;
789  break;
790  }
791 
792  QPushButton *pb0 = 0;
793  QPushButton *pb1 = 0;
794  QLineEdit *le = qobject_cast<QLineEdit *>( editor );
795  if ( le )
796  {
797  if ( le )
798  myWidget = le;
799 
800  if ( editor->parent() )
801  {
802  pb0 = editor->parent()->findChild<QPushButton *>();
803  }
804  }
805  else
806  {
807  myWidget = new QWidget( parent );
808  myWidget->setBackgroundRole( QPalette::Window );
809  myWidget->setAutoFillBackground( true );
810 
811  le = new QgsFilterLineEdit( myWidget );
812  switch ( editType )
813  {
817  pb0 = new QPushButton( tr( "..." ), myWidget );
818  break;
819 
821  pb0 = new QPushButton( tr( "<" ), myWidget );
822  pb0->setObjectName( "saveUrl" );
823  pb1 = new QPushButton( tr( "..." ), myWidget );
824  pb1->setObjectName( "openUrl" );
825  break;
826 
828  pb0 = new QgsColorButton( myWidget );
829  break;
830 
831  default:
832  break;
833  }
834 
835 
836  int row = 0;
837  QGridLayout *layout = new QGridLayout( myWidget );
838  if ( editType == QgsVectorLayer::Photo )
839  {
840  lw = new QLabel( myWidget );
841  layout->addWidget( lw, 0, 0, 1, 2 );
842  row++;
843  }
844  else if ( editType == QgsVectorLayer::WebView )
845  {
846  ww = new QWebView( myWidget );
847  ww->setObjectName( "webview" );
848  ww->page()->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
849  // ww->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
850  ww->settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true );
851  ww->settings()->setAttribute( QWebSettings::JavascriptCanOpenWindows, true );
852 #ifdef QGISDEBUG
853  ww->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
854 #endif
855 
856  QSize size( vl->widgetSize( idx ) );
857  if ( size.width() > 0 || size.height() > 0 )
858  {
859  if ( size.width() == 0 )
860  size.setWidth( 1 );
861  if ( size.height() == 0 )
862  size.setHeight( 1 );
863  ww->setMinimumSize( size );
864  }
865 
866  layout->addWidget( ww, 0, 0, 1, 2 );
867  row++;
868  }
869 
870  layout->addWidget( le, row, 0 );
871  layout->addWidget( pb0, row, 1 );
872  if ( pb1 )
873  layout->addWidget( pb1, row, 2 );
874 
875  myWidget->setLayout( layout );
876  }
877 
878  if ( le )
879  {
880  le->setValidator( new QgsFieldValidator( le, field, vl->dateFormat( idx ) ) );
881 
882  if ( ww )
883  connect( le, SIGNAL( textChanged( const QString & ) ), new QgsAttributeEditor( le, vl, idx ), SLOT( loadUrl( const QString & ) ) );
884  if ( lw )
885  connect( le, SIGNAL( textChanged( const QString & ) ), new QgsAttributeEditor( le, vl, idx ), SLOT( loadPixmap( const QString & ) ) );
886  if ( editType == QgsVectorLayer::Color )
887  connect( le, SIGNAL( textChanged( const QString & ) ), new QgsAttributeEditor( le ), SLOT( updateColor() ) );
888  }
889 
890  if ( pb0 )
891  {
892  if ( editType == QgsVectorLayer::FileName || editType == QgsVectorLayer::Photo )
893  {
894  connect( pb0, SIGNAL( clicked() ), new QgsAttributeEditor( pb0 ), SLOT( selectFileName() ) );
895  pb0->setToolTip( tr( "Select filename..." ) );
896  }
897  if ( editType == QgsVectorLayer::WebView )
898  {
899  connect( pb0, SIGNAL( clicked() ), new QgsAttributeEditor( pb0 ), SLOT( updateUrl() ) );
900  pb0->setToolTip( tr( "Save current page url in attribute" ) );
901  }
902  if ( editType == QgsVectorLayer::Calendar )
903  {
904  connect( pb0, SIGNAL( clicked() ), new QgsAttributeEditor( pb0 ), SLOT( selectDate() ) );
905  pb0->setToolTip( tr( "Select date in calendar" ) );
906  }
907  if ( editType == QgsVectorLayer::Color )
908  {
909  connect( pb0, SIGNAL( colorChanged( const QColor & ) ), new QgsAttributeEditor( pb0 ), SLOT( updateColor() ) );
910  pb0->setToolTip( tr( "Select color in browser" ) );
911  }
912  }
913 
914  if ( pb1 )
915  {
916  if ( editType == QgsVectorLayer::WebView )
917  {
918  connect( pb1, SIGNAL( clicked() ), new QgsAttributeEditor( pb1 ), SLOT( openUrl() ) );
919  pb1->setToolTip( tr( "Open current page in default browser" ) );
920  }
921  }
922  }
923  break;
924  }
925 
926  if ( context.proxyWidget( vl, idx ) )
927  {
928  if ( !synchronized )
929  {
930  myWidget->setEnabled( false );
931  }
932  }
933  else
934  {
935  context.addProxyWidget( vl, idx, myWidget );
936  }
937 
938  setValue( myWidget, vl, idx, value );
939 
940  return myWidget;
941 }
942 
943 bool QgsAttributeEditor::retrieveValue( QWidget *widget, QgsVectorLayer *vl, int idx, QVariant &value )
944 {
945  if ( !widget )
946  return false;
947 
948  QgsEditorWidgetWrapper* wrapper = QgsEditorWidgetWrapper::fromWidget( widget );
949 
950  if ( wrapper )
951  {
952  value = wrapper->value();
953  return true;
954  }
955 
956  const QgsField &theField = vl->pendingFields()[idx];
957  QgsVectorLayer::EditType editType = vl->editType( idx );
958  bool modified = false;
959  QString text;
960 
961  QSettings settings;
962  QString nullValue = settings.value( "qgis/nullValue", "NULL" ).toString();
963 
964  QLineEdit *le = qobject_cast<QLineEdit *>( widget );
965  if ( le )
966  {
967  text = le->text();
968  modified = le->isModified();
969  if ( text == nullValue )
970  {
971  text = QString::null;
972  }
973  }
974 
975  QTextEdit *te = qobject_cast<QTextEdit *>( widget );
976  if ( te )
977  {
978  text = te->toHtml();
979  modified = te->document()->isModified();
980  if ( text == nullValue )
981  {
982  text = QString::null;
983  }
984  }
985 
986  QPlainTextEdit *pte = qobject_cast<QPlainTextEdit *>( widget );
987  if ( pte )
988  {
989  text = pte->toPlainText();
990  modified = pte->document()->isModified();
991  if ( text == nullValue )
992  {
993  text = QString::null;
994  }
995  }
996 
997  QComboBox *cb = qobject_cast<QComboBox *>( widget );
998  if ( cb )
999  {
1000  if ( editType == QgsVectorLayer::UniqueValues ||
1001  editType == QgsVectorLayer::ValueMap ||
1002  editType == QgsVectorLayer::Classification ||
1003  editType == QgsVectorLayer::ValueRelation )
1004  {
1005  text = cb->itemData( cb->currentIndex() ).toString();
1006  if ( text == nullValue )
1007  {
1008  text = QString::null;
1009  }
1010  }
1011  else
1012  {
1013  text = cb->currentText();
1014  }
1015  modified = true;
1016  }
1017 
1018  QListWidget *lw = qobject_cast<QListWidget *>( widget );
1019  if ( lw )
1020  {
1021  if ( editType == QgsVectorLayer::ValueRelation )
1022  {
1023  text = '{';
1024  for ( int i = 0, n = 0; i < lw->count(); i++ )
1025  {
1026  if ( lw->item( i )->checkState() == Qt::Checked )
1027  {
1028  if ( n > 0 )
1029  {
1030  text.append( ',' );
1031  }
1032  text.append( lw->item( i )->data( Qt::UserRole ).toString() );
1033  n++;
1034  }
1035  }
1036  text.append( '}' );
1037  }
1038  else
1039  {
1040  text = QString::null;
1041  }
1042  modified = true;
1043  }
1044 
1045  QSpinBox *sb = qobject_cast<QSpinBox *>( widget );
1046  if ( sb )
1047  {
1048  text = QString::number( sb->value() );
1049  }
1050 
1051  QAbstractSlider *slider = qobject_cast<QAbstractSlider *>( widget );
1052  if ( slider )
1053  {
1054  text = QString::number( slider->value() );
1055  }
1056 
1057  QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox *>( widget );
1058  if ( dsb )
1059  {
1060  text = QString::number( dsb->value() );
1061  }
1062 
1063  QCheckBox *ckb = qobject_cast<QCheckBox *>( widget );
1064  if ( ckb )
1065  {
1066  QPair<QString, QString> states = vl->checkedState( idx );
1067  text = ckb->isChecked() ? states.first : states.second;
1068  }
1069 
1070  QGroupBox *gb = qobject_cast<QGroupBox *>( widget );
1071  if ( gb )
1072  {
1073  QPair<QString, QString> states = vl->checkedState( idx );
1074  text = gb->isChecked() ? states.first : states.second;
1075  }
1076 
1077  QCalendarWidget *cw = qobject_cast<QCalendarWidget *>( widget );
1078  if ( cw )
1079  {
1080  text = cw->selectedDate().toString( vl->dateFormat( idx ) );
1081  }
1082 
1083  le = widget->findChild<QLineEdit *>();
1084  // QCalendarWidget and QGroupBox have an internal QLineEdit which returns the year
1085  // part of the date so we need to skip this if we have a QCalendarWidget
1086  if ( !cw && !gb && le )
1087  {
1088  text = le->text();
1089  modified = le->isModified();
1090  if ( text == nullValue )
1091  {
1092  text = QString::null;
1093  }
1094  }
1095 
1096  switch ( theField.type() )
1097  {
1098  case QVariant::Int:
1099  {
1100  bool ok;
1101  int myIntValue = text.toInt( &ok );
1102  if ( ok && !text.isEmpty() )
1103  {
1104  value = QVariant( myIntValue );
1105  modified = true;
1106  }
1107  else if ( modified )
1108  {
1109  value = QVariant();
1110  }
1111  }
1112  break;
1113  case QVariant::LongLong:
1114  {
1115  bool ok;
1116  qlonglong myLongValue = text.toLong( &ok );
1117  if ( ok && !text.isEmpty() )
1118  {
1119  value = QVariant( myLongValue );
1120  modified = true;
1121  }
1122  else if ( modified )
1123  {
1124  value = QVariant();
1125  }
1126  }
1127  case QVariant::Double:
1128  {
1129  bool ok;
1130  double myDblValue = text.toDouble( &ok );
1131  if ( ok && !text.isEmpty() )
1132  {
1133  value = QVariant( myDblValue );
1134  modified = true;
1135  }
1136  else if ( modified )
1137  {
1138  value = QVariant();
1139  }
1140  }
1141  break;
1142  case QVariant::Date:
1143  {
1144  QDate myDateValue = QDate::fromString( text, vl->dateFormat( idx ) );
1145  if ( myDateValue.isValid() && !text.isEmpty() )
1146  {
1147  value = myDateValue;
1148  modified = true;
1149  }
1150  else if ( modified )
1151  {
1152  value = QVariant();
1153  }
1154  }
1155  break;
1156  default: //string
1157  modified = true;
1158  if ( text.isNull() )
1159  value = QVariant( theField.type() );
1160  else
1161  value = QVariant( text );
1162  break;
1163  }
1164 
1165  return modified;
1166 }
1167 
1168 bool QgsAttributeEditor::setValue( QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value )
1169 {
1170  if ( !editor )
1171  return false;
1172 
1173  QgsVectorLayer::EditType editType = vl->editType( idx );
1174  const QgsField &field = vl->pendingFields()[idx];
1175  QVariant::Type myFieldType = field.type();
1176 
1177  QSettings settings;
1178  QString nullValue = settings.value( "qgis/nullValue", "NULL" ).toString();
1179 
1180  switch ( editType )
1181  {
1187  {
1188  QVariant v = value;
1189  QComboBox *cb = qobject_cast<QComboBox *>( editor );
1190  if ( !cb )
1191  return false;
1192 
1193  if ( v.isNull() )
1194  {
1195  v = nullValue;
1196  }
1197 
1198  int idx = cb->findData( v );
1199  if ( idx < 0 )
1200  return false;
1201 
1202  cb->setCurrentIndex( idx );
1203  }
1204  break;
1205 
1209  {
1210  if ( myFieldType == QVariant::Int )
1211  {
1212  if ( editType == QgsVectorLayer::EditRange )
1213  {
1214  QSpinBox *sb = qobject_cast<QSpinBox *>( editor );
1215  if ( !sb )
1216  return false;
1217  sb->setValue( value.toInt() );
1218  }
1219  else
1220  {
1221  QAbstractSlider *sl = qobject_cast<QAbstractSlider *>( editor );
1222  if ( !sl )
1223  return false;
1224  sl->setValue( value.toInt() );
1225  }
1226  break;
1227  }
1228  else if ( myFieldType == QVariant::Double )
1229  {
1230  QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox *>( editor );
1231  if ( !dsb )
1232  return false;
1233  dsb->setValue( value.toDouble() );
1234  }
1235  }
1236 
1238  {
1239  QGroupBox *gb = qobject_cast<QGroupBox *>( editor );
1240  if ( gb )
1241  {
1242  QPair<QString, QString> states = vl->checkedState( idx );
1243  gb->setChecked( value == states.first );
1244  break;
1245  }
1246 
1247  QCheckBox *cb = qobject_cast<QCheckBox *>( editor );
1248  if ( cb )
1249  {
1250  QPair<QString, QString> states = vl->checkedState( idx );
1251  cb->setChecked( value == states.first );
1252  break;
1253  }
1254  }
1255 
1256  // fall-through
1257 
1263  {
1264  QgsFilterLineEdit *fle = qobject_cast<QgsFilterLineEdit *>( editor );
1265  QLineEdit *le = qobject_cast<QLineEdit *>( editor );
1266  QComboBox *cb = qobject_cast<QComboBox *>( editor );
1267  QTextEdit *te = qobject_cast<QTextEdit *>( editor );
1268  QPlainTextEdit *pte = qobject_cast<QPlainTextEdit *>( editor );
1269  if ( !le && ! cb && !te && !pte )
1270  return false;
1271 
1272  if ( fle && !( myFieldType == QVariant::Int || myFieldType == QVariant::Double || myFieldType == QVariant::LongLong || myFieldType == QVariant::Date ) )
1273  {
1274  fle->setNullValue( nullValue );
1275  }
1276 
1277  QString text;
1278  if ( value.isNull() )
1279  {
1280  if ( myFieldType == QVariant::Int || myFieldType == QVariant::Double || myFieldType == QVariant::LongLong || myFieldType == QVariant::Date )
1281  text = "";
1282  else if ( editType == QgsVectorLayer::UuidGenerator )
1283  text = QUuid::createUuid().toString();
1284  else
1285  text = nullValue;
1286  }
1287  else
1288  {
1289  text = field.displayString( value );
1290  }
1291 
1292  if ( le )
1293  le->setText( text );
1294  if ( cb && cb->isEditable() )
1295  cb->setEditText( text );
1296  if ( te )
1297  te->setHtml( text );
1298  if ( pte )
1299  pte->setPlainText( text );
1300  }
1301  break;
1302 
1305  case QgsVectorLayer::Photo:
1307  case QgsVectorLayer::Color:
1308  {
1309  QCalendarWidget *cw = qobject_cast<QCalendarWidget *>( editor );
1310  if ( cw )
1311  {
1312  cw->setSelectedDate( value.toDate() );
1313  break;
1314  }
1315 
1316  QWebView *ww = qobject_cast<QWebView *>( editor );
1317  if ( ww )
1318  {
1319  ww->load( value.toString() );
1320  break;
1321  }
1322 
1323  QLabel *lw = qobject_cast<QLabel *>( editor );
1324  if ( lw )
1325  break;
1326 
1327  QgsColorButton *cb = qobject_cast<QgsColorButton *>( editor );
1328  if ( cb )
1329  {
1330  cb->setColor( QColor( value.toString() ) );
1331  break;
1332  }
1333 
1334  QgsFilterLineEdit *fle = qobject_cast<QgsFilterLineEdit*>( editor );
1335  QLineEdit *le = qobject_cast<QLineEdit*>( editor );
1336  if ( !le )
1337  {
1338  le = editor->findChild<QLineEdit *>();
1339  fle = qobject_cast<QgsFilterLineEdit *>( le );
1340  }
1341  if ( !le )
1342  return false;
1343 
1344  if ( fle && !( myFieldType == QVariant::Int || myFieldType == QVariant::Double || myFieldType == QVariant::LongLong || myFieldType == QVariant::Date ) )
1345  {
1346  fle->setNullValue( nullValue );
1347  }
1348 
1349  QString text;
1350  if ( value.isNull() )
1351  {
1352  if ( myFieldType == QVariant::Int || myFieldType == QVariant::Double || myFieldType == QVariant::LongLong || myFieldType == QVariant::Date )
1353  text = "";
1354  else
1355  text = nullValue;
1356  }
1357  else if ( editType == QgsVectorLayer::Calendar && value.canConvert( QVariant::Date ) )
1358  {
1359  text = value.toDate().toString( vl->dateFormat( idx ) );
1360  }
1361  else
1362  {
1363  text = value.toString();
1364  }
1365 
1366  le->setText( text );
1367  }
1368  break;
1369 
1371  break;
1372 
1374  QgsEditorWidgetWrapper* wrapper = QgsEditorWidgetWrapper::fromWidget( editor );
1375  if ( wrapper )
1376  {
1377  wrapper->setValue( value );
1378  }
1379  break;
1380  }
1381 
1382  return true;
1383 }
1384 
1385 QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElement* widgetDef, QWidget* parent, QgsVectorLayer* vl, const QgsFeature& feat, QgsAttributeEditorContext& context, QString& labelText, bool& labelOnTop )
1386 {
1387  QWidget *newWidget = 0;
1388 
1389  switch ( widgetDef->type() )
1390  {
1392  {
1393  const QgsAttributeEditorField* fieldDef = dynamic_cast<const QgsAttributeEditorField*>( widgetDef );
1394  int fldIdx = fieldDef->idx();
1395  if ( fldIdx < vl->pendingFields().count() && fldIdx >= 0 )
1396  {
1397  newWidget = createAttributeEditor( parent, 0, vl, fldIdx, feat.attributes().value( fldIdx, QVariant() ), context );
1398 
1399  if ( newWidget )
1400  {
1401  if ( vl->editType( fldIdx ) != QgsVectorLayer::Immutable )
1402  {
1403  if ( newWidget->isEnabled() && vl->isEditable() && vl->fieldEditable( fldIdx ) )
1404  {
1405  newWidget->setEnabled( true );
1406  }
1407  else if ( vl->editType( fldIdx ) == QgsVectorLayer::Photo )
1408  {
1409  foreach ( QWidget *w, newWidget->findChildren<QWidget *>() )
1410  {
1411  w->setEnabled( qobject_cast<QLabel *>( w ) ? true : false );
1412  }
1413  }
1414  else if ( vl->editType( fldIdx ) == QgsVectorLayer::WebView )
1415  {
1416  foreach ( QWidget *w, newWidget->findChildren<QWidget *>() )
1417  {
1418  if ( qobject_cast<QWebView *>( w ) )
1419  w->setEnabled( true );
1420  else if ( qobject_cast<QPushButton *>( w ) && w->objectName() == "openUrl" )
1421  w->setEnabled( true );
1422  else
1423  w->setEnabled( false );
1424  }
1425  }
1426  else
1427  {
1428  newWidget->setEnabled( false );
1429  }
1430  }
1431  }
1432  }
1433  else
1434  {
1435  newWidget = new QLabel( tr( "<i>Error: Field does not exist in datasource</i>" ), parent );
1436  }
1437  labelOnTop = vl->labelOnTop( fieldDef->idx() );
1438  labelText = vl->attributeDisplayName( fieldDef->idx() );
1439  break;
1440  }
1441 
1443  {
1444  const QgsAttributeEditorRelation* relDef = dynamic_cast<const QgsAttributeEditorRelation*>( widgetDef );
1445 
1446  newWidget = QgsRelationEditorWidget::createRelationEditor( relDef->relation(), feat, context );
1447  labelText = QString::null;
1448  labelOnTop = true;
1449  break;
1450  }
1451 
1453  {
1454  const QgsAttributeEditorContainer* container = dynamic_cast<const QgsAttributeEditorContainer*>( widgetDef );
1455  QWidget* myContainer;
1456 
1457  if ( container->isGroupBox() )
1458  {
1459  QGroupBox* groupBox = new QGroupBox( parent );
1460  groupBox->setTitle( container->name() );
1461  myContainer = groupBox;
1462  newWidget = myContainer;
1463  }
1464  else
1465  {
1466  QScrollArea *scrollArea = new QScrollArea( parent );
1467 
1468  myContainer = new QWidget( scrollArea );
1469 
1470  scrollArea->setWidget( myContainer );
1471  scrollArea->setWidgetResizable( true );
1472  scrollArea->setFrameShape( QFrame::NoFrame );
1473 
1474  newWidget = scrollArea;
1475  }
1476 
1477  QGridLayout* gbLayout = new QGridLayout( myContainer );
1478  myContainer->setLayout( gbLayout );
1479 
1480  int index = 0;
1481 
1482  QList<QgsAttributeEditorElement*> children = container->children();
1483 
1484  Q_FOREACH( QgsAttributeEditorElement* childDef, children )
1485  {
1486  QString labelText;
1487  bool labelOnTop;
1488  QWidget* editor = createWidgetFromDef( childDef, myContainer, vl, feat, context, labelText, labelOnTop );
1489 
1490  if ( labelText == QString::null )
1491  {
1492  gbLayout->addWidget( editor, index, 0, 1, 2 );
1493  }
1494  else
1495  {
1496  QLabel* mypLabel = new QLabel( labelText );
1497  if ( labelOnTop )
1498  {
1499  gbLayout->addWidget( mypLabel, index, 0, 1, 2 );
1500  ++index;
1501  gbLayout->addWidget( editor, index, 0, 1 , 2 );
1502  }
1503  else
1504  {
1505  gbLayout->addWidget( mypLabel, index, 0 );
1506  gbLayout->addWidget( editor, index, 1 );
1507  }
1508  }
1509 
1510  ++index;
1511  }
1512  gbLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ), index , 0 );
1513 
1514  labelText = QString::null;
1515  labelOnTop = true;
1516  break;
1517  }
1518 
1519  default:
1520  QgsDebugMsg( "Unknown attribute editor widget type encountered..." );
1521  break;
1522  }
1523 
1524  return newWidget;
1525 }
1526 
1528 {
1529  QObject* sObj = QObject::sender();
1530  QTextEdit *te = qobject_cast<QTextEdit *>( sObj );
1531  QPlainTextEdit *pte = qobject_cast<QPlainTextEdit *>( sObj );
1532 
1533  if ( te )
1534  changeText( te->toPlainText() );
1535  if ( pte )
1536  changeText( pte->toPlainText() );
1537 }
1538 
1539 void QgsStringRelay::changeText( QString str )
1540 {
1541  QObject* sObj = QObject::sender();
1542  const char* sSlot = sObj->property( "QgisAttrEditSlot" ).toByteArray().constData();
1543 
1544  // disconnect widget being edited from relay's signal
1545  disconnect( this, SIGNAL( textChanged( QString ) ), sObj, sSlot );
1546 
1547  // block all proxies' signals
1548  QList<bool> oldBlockSigs;
1549  for ( int i = 0; i < mProxyList.size(); ++i )
1550  {
1551  oldBlockSigs << ( mProxyList[i] )->blockSignals( true );
1552  }
1553 
1554  // update all proxies not being edited without creating cyclical signals/slots
1555  emit textChanged( str );
1556 
1557  // reconnect widget being edited and reset blockSignals state
1558  connect( this, SIGNAL( textChanged( QString ) ), sObj, sSlot );
1559  for ( int i = 0; i < mProxyList.size(); ++i )
1560  {
1561  mProxyList[i]->blockSignals( oldBlockSigs[i] );
1562  }
1563 }