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