00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <QLineEdit>
00020 #include <QValidator>
00021
00022 #include "qgsattributetable.h"
00023 #include "qgsfield.h"
00024 #include "qgslogger.h"
00025 #include "qgsvectordataprovider.h"
00026 #include "qgsvectorlayer.h"
00027
00028 #include <QApplication>
00029 #include <QClipboard>
00030 #include <QHeaderView>
00031 #include <QKeyEvent>
00032 #include <QMenu>
00033
00034
00035 QgsAttributeTableItemDelegate::QgsAttributeTableItemDelegate( QgsAttributeTable *table, QObject *parent )
00036 : QItemDelegate( parent ), mTable( table )
00037 {
00038 }
00039
00040 QWidget *QgsAttributeTableItemDelegate::createEditor(
00041 QWidget *parent,
00042 const QStyleOptionViewItem &option,
00043 const QModelIndex &index ) const
00044 {
00045 QWidget *editor = QItemDelegate::createEditor( parent, option, index );
00046 QLineEdit *le = dynamic_cast<QLineEdit*>( editor );
00047 if ( !le )
00048 return editor;
00049
00050 int col = index.column();
00051 QTableWidgetItem *twi = mTable->horizontalHeaderItem( col );
00052 if ( !twi )
00053 {
00054 QgsDebugMsg( QString( "horizontalHeaderItem %1 not found" ).arg( col ) );
00055 return editor;
00056 }
00057
00058 int type = twi->data( QgsAttributeTable::AttributeType ).toInt();
00059 if ( type == QVariant::Int )
00060 {
00061 le->setValidator( new QIntValidator( le ) );
00062 }
00063 else if ( type == QVariant::Double )
00064 {
00065 le->setValidator( new QDoubleValidator( le ) );
00066 }
00067
00068 return editor;
00069 }
00070
00071
00072 QgsAttributeTable::QgsAttributeTable( QWidget * parent ) :
00073 QTableWidget( parent ),
00074 lockKeyPressed( false ),
00075 mEditable( false ),
00076 mEdited( false ),
00077 mActionPopup( 0 ),
00078 mPreviousSortIndicatorColumn( -1 )
00079 {
00080 QFont f( font() );
00081 #ifdef Q_WS_MAC
00082
00083 f.setPointSize( 11 );
00084 #else
00085 f.setFamily( "Helvetica" );
00086 f.setPointSize( 9 );
00087 #endif
00088 setFont( f );
00089 mDelegate = new QgsAttributeTableItemDelegate( this );
00090 setItemDelegate( mDelegate );
00091 setSelectionBehavior( QAbstractItemView::SelectRows );
00092 connect( this, SIGNAL( itemSelectionChanged() ), this, SLOT( handleChangedSelections() ) );
00093 connect( horizontalHeader(), SIGNAL( sectionClicked( int ) ), this, SLOT( columnClicked( int ) ) );
00094 connect( verticalHeader(), SIGNAL( sectionClicked( int ) ), this, SLOT( rowClicked( int ) ) );
00095 setFocus();
00096 }
00097
00098 QgsAttributeTable::~QgsAttributeTable()
00099 {
00100 delete mActionPopup;
00101 delete mDelegate;
00102 }
00103
00104 void QgsAttributeTable::setReadOnly( bool b )
00105 {
00106 blockSignals( true );
00107
00108 setEditTriggers( b ? QAbstractItemView::NoEditTriggers :
00109 QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed );
00110 if ( !b )
00111 {
00112 setColumnReadOnly( 0, true );
00113 }
00114
00115 blockSignals( false );
00116 }
00117
00118 void QgsAttributeTable::setColumnReadOnly( int col, bool ro )
00119 {
00120 for ( int i = 0; i < rowCount(); ++i )
00121 {
00122 QTableWidgetItem *twi = item( i, col );
00123 twi->setFlags( ro ? twi->flags() & ~Qt::ItemIsEditable : twi->flags() | Qt::ItemIsEditable );
00124 }
00125 }
00126
00127 void QgsAttributeTable::columnClicked( int col )
00128 {
00129 QApplication::setOverrideCursor( Qt::WaitCursor );
00130
00131
00132 QList<int> idsOfSelected;
00133 QList<QTableWidgetSelectionRange> selection = selectedRanges();
00134 for ( int i = 0; i < selection.count(); i++ )
00135 {
00136 for ( int j = selection.at( i ).topRow(); j <= selection.at( i ).bottomRow(); j++ )
00137 {
00138 idsOfSelected.append( item( j, 0 )->text().toInt() );
00139 }
00140 }
00141
00142 QHeaderView *header = horizontalHeader();
00143 if ( !header->isSortIndicatorShown() )
00144 {
00145 header->setSortIndicatorShown( true );
00146 header->setSortIndicator( col, Qt::AscendingOrder );
00147 }
00148 if ( col != mPreviousSortIndicatorColumn )
00149 {
00150
00151 header->setSortIndicator( col, header->sortIndicatorOrder() == Qt::AscendingOrder ?
00152 Qt::DescendingOrder : Qt::AscendingOrder );
00153 }
00154 mPreviousSortIndicatorColumn = col;
00155 sortColumn( col, header->sortIndicatorOrder() == Qt::DescendingOrder );
00156
00157
00158 rowIdMap.clear();
00159 int id;
00160 for ( int i = 0; i < rowCount(); i++ )
00161 {
00162 id = item( i, 0 )->text().toInt();
00163 rowIdMap.insert( id, i );
00164 }
00165
00166 disconnect( this, SIGNAL( itemSelectionChanged() ), this, SLOT( handleChangedSelections() ) );
00167 clearSelection();
00168
00169
00170
00171 QList < int >::iterator it;
00172 for ( it = idsOfSelected.begin(); it != idsOfSelected.end(); ++it )
00173 {
00174 selectRowWithId(( *it ) );
00175 }
00176 connect( this, SIGNAL( itemSelectionChanged() ), this, SLOT( handleChangedSelections() ) );
00177
00178 QApplication::restoreOverrideCursor();
00179 }
00180
00181 void QgsAttributeTable::keyPressEvent( QKeyEvent * ev )
00182 {
00183 if ( ev->key() == Qt::Key_Control || ev->key() == Qt::Key_Shift )
00184 {
00185 lockKeyPressed = true;
00186 }
00187 }
00188
00189 void QgsAttributeTable::keyReleaseEvent( QKeyEvent * ev )
00190 {
00191 if ( ev->key() == Qt::Key_Control || ev->key() == Qt::Key_Shift )
00192 {
00193 lockKeyPressed = false;
00194 }
00195 }
00196
00197 void QgsAttributeTable::handleChangedSelections()
00198 {
00199 emit selectionRemoved( false );
00200
00201 QList<QTableWidgetSelectionRange> selectedItemRanges = selectedRanges();
00202 QList<QTableWidgetSelectionRange>::const_iterator range_it = selectedItemRanges.constBegin();
00203 for ( ; range_it != selectedItemRanges.constEnd(); ++range_it )
00204 {
00205 for ( int index = range_it->topRow(); index <= range_it->bottomRow(); index++ )
00206 {
00207 emit selected( item( index, 0 )->text().toInt(), false );
00208 }
00209 }
00210
00211
00212
00213
00214 }
00215
00216 void QgsAttributeTable::insertFeatureId( int id, int row )
00217 {
00218 rowIdMap.insert( id, row );
00219 }
00220
00221 void QgsAttributeTable::selectRowWithId( int id )
00222 {
00223 QMap < int, int >::iterator it = rowIdMap.find( id );
00224 setRangeSelected( QTableWidgetSelectionRange( it.value(), 0, it.value(), columnCount() - 1 ), true );
00225 }
00226
00227 void QgsAttributeTable::sortColumn( int col, bool ascending )
00228 {
00229 int type = horizontalHeaderItem( col )->data( QgsAttributeTable::AttributeType ).toInt();
00230 qsort( 0, rowCount() - 1, col, ascending, type != QVariant::Int && type != QVariant::Double );
00231 }
00232
00233
00237 int QgsAttributeTable::compareItems( QString s1, QString s2, bool ascending, bool alphanumeric )
00238 {
00239 if ( alphanumeric )
00240 {
00241 if ( s1 > s2 )
00242 {
00243 if ( ascending )
00244 {
00245 return 1;
00246 }
00247 else
00248 {
00249 return -1;
00250 }
00251 }
00252 else if ( s1 < s2 )
00253 {
00254 if ( ascending )
00255 {
00256 return -1;
00257 }
00258 else
00259 {
00260 return 1;
00261 }
00262 }
00263 else if ( s1 == s2 )
00264 {
00265 return 0;
00266 }
00267 }
00268 else
00269 {
00270 double d1 = s1.toDouble();
00271 double d2 = s2.toDouble();
00272 if ( d1 > d2 )
00273 {
00274 if ( ascending )
00275 {
00276 return 1;
00277 }
00278 else
00279 {
00280 return -1;
00281 }
00282 }
00283 else if ( d1 < d2 )
00284 {
00285 if ( ascending )
00286 {
00287 return -1;
00288 }
00289 else
00290 {
00291 return 1;
00292 }
00293 }
00294 else if ( d1 == d2 )
00295 {
00296 return 0;
00297 }
00298 }
00299
00300 return 0;
00301 }
00302
00303 void QgsAttributeTable::qsort( int lower, int upper, int col, bool ascending, bool alphanumeric )
00304 {
00305 int i, j;
00306 QString v;
00307 if ( upper > lower )
00308 {
00309
00310 int element = int (( double )rand() / ( double )RAND_MAX * ( upper - lower ) + lower );
00311 swapRows( element, upper );
00312 v = item( upper, col )->text();
00313 i = lower - 1;
00314 j = upper;
00315 for ( ;; )
00316 {
00317 while ( compareItems( item( ++i, col )->text(), v, ascending, alphanumeric ) == -1 )
00318 ;
00319 while ( compareItems( item( --j, col )->text(), v, ascending, alphanumeric ) == 1 && j > 0 );
00320 if ( i >= j )
00321 {
00322 break;
00323 }
00324 swapRows( i, j );
00325 }
00326 swapRows( i, upper );
00327 qsort( lower, i - 1, col, ascending, alphanumeric );
00328 qsort( i + 1, upper, col, ascending, alphanumeric );
00329 }
00330 }
00331
00332 void QgsAttributeTable::swapRows( int row1, int row2 )
00333 {
00334 for ( int col = 0; col < columnCount(); col++ )
00335 {
00336 QTableWidgetItem *item = takeItem( row1, col );
00337 setItem( row1, col, takeItem( row2, col ) );
00338 setItem( row2, col, item );
00339 }
00340 }
00341
00342 void QgsAttributeTable::contextMenuEvent( QContextMenuEvent *event )
00343 {
00344 const QPoint& pos = event->globalPos();
00345 int row = rowAt( pos.x() );
00346 int col = columnAt( pos.y() );
00347
00348
00349
00350 if ( mActionPopup == 0 )
00351 {
00352 mActionPopup = new QMenu();
00353 mActionPopup->addAction( tr( "Run action" ) );
00354 mActionPopup->addSeparator();
00355
00356 QgsAttributeAction::aIter iter = mActions.begin();
00357 for ( int j = 0; iter != mActions.end(); ++iter, ++j )
00358 {
00359 QAction* a = mActionPopup->addAction( iter->name() );
00360
00361
00362 a->setData( QVariant::fromValue( j ) );
00363 }
00364 connect( mActionPopup, SIGNAL( triggered( QAction* ) ),
00365 this, SLOT( popupItemSelected( QAction* ) ) );
00366 }
00367
00368
00369
00370
00371 mActionValues.clear();
00372
00373 for ( int i = 0; i < columnCount(); ++i )
00374 {
00375 if ( row >= 0 )
00376 {
00377 mActionValues.push_back(
00378 std::make_pair(
00379 horizontalHeaderItem( i )->text(),
00380 item( row, i )->text() ) );
00381 }
00382 }
00383
00384
00385 mClickedOnValue = col;
00386
00387 if ( mActions.size() > 0 )
00388 mActionPopup->popup( pos );
00389 }
00390
00391 void QgsAttributeTable::popupItemSelected( QAction* menuAction )
00392 {
00393 int id = menuAction->data().toInt();
00394 mActions.doAction( id, mActionValues, mClickedOnValue );
00395 }
00396
00397
00398 void QgsAttributeTable::copySelectedRows()
00399 {
00400
00401
00402 QString toClipboard;
00403 const char fieldSep = '\t';
00404
00405
00406 for ( int i = 0; i < columnCount(); ++i )
00407 toClipboard += horizontalHeaderItem( i )->text() + fieldSep;
00408 toClipboard += '\n';
00409
00410 QList<QTableWidgetSelectionRange> selection = selectedRanges();
00411
00412 for ( int i = 0; i < selection.count(); ++i )
00413 {
00414 QTableWidgetSelectionRange sel = selection.at( i );
00415 for ( int row = sel.topRow(); row < sel.topRow() + sel.rowCount(); ++row )
00416 {
00417 for ( int column = 0; column < columnCount(); ++column )
00418 toClipboard += item( row, column )->text() + fieldSep;
00419 toClipboard += '\n';
00420 }
00421 }
00422 #ifdef QGISDEBUG
00423 std::cerr << "Selected data in table is:\n" << toClipboard.data();
00424 #endif
00425
00426 QClipboard* clipboard = QApplication::clipboard();
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436 clipboard->setText( toClipboard, QClipboard::Selection );
00437 clipboard->setText( toClipboard, QClipboard::Clipboard );
00438 }
00439
00440 void QgsAttributeTable::fillTable( QgsVectorLayer *layer )
00441 {
00442 blockSignals( true );
00443
00444 const QgsFieldMap &fields = layer->pendingFields();
00445
00446
00447 setColumnCount( fields.size() + 1 );
00448
00449 setHorizontalHeaderItem( 0, new QTableWidgetItem( "id" ) );
00450
00451 int h = 1;
00452 for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); fldIt++, h++ )
00453 {
00454 QgsDebugMsg( QString( "%1: field %2: %3 | %4" )
00455 .arg( h ).arg( fldIt.key() ).arg( fldIt->name() ).arg( QVariant::typeToName( fldIt->type() ) ) );
00456
00457 QTableWidgetItem *twi = new QTableWidgetItem( fldIt->name() );
00458 twi->setData( AttributeIndex, fldIt.key() );
00459 twi->setData( AttributeName, fldIt->name() );
00460 twi->setData( AttributeType, fldIt->type() );
00461 setHorizontalHeaderItem( h, twi );
00462
00463 mAttrIdxMap.insert( fldIt.key(), h );
00464 }
00465
00466 QgsFeatureList features;
00467 if ( layer->selectedFeatureCount() == 0 )
00468 {
00469 layer->select( layer->pendingAllAttributesList(), QgsRect(), false );
00470
00471 QgsFeature f;
00472 while ( layer->nextFeature( f ) )
00473 features << f;
00474 }
00475 else
00476 {
00477 features = layer->selectedFeatures();
00478 }
00479
00480 setRowCount( features.size() );
00481
00482 for ( int i = 0; i < features.size(); i++ )
00483 putFeatureInTable( i, features[i] );
00484
00485
00486 resizeRowsToContents();
00487
00488
00489 for ( int i = 0; i < columnCount(); i++ )
00490 resizeColumnToContents( i );
00491
00492 blockSignals( false );
00493 }
00494
00495 void QgsAttributeTable::putFeatureInTable( int row, const QgsFeature& fet )
00496 {
00497
00498 if ( row >= rowCount() )
00499 {
00500 setRowCount( row + 1 );
00501 }
00502
00503
00504 int id = fet.id();
00505 QTableWidgetItem *twi = new QTableWidgetItem( QString::number( id ) );
00506 twi->setTextAlignment( Qt::AlignRight | Qt::AlignVCenter );
00507 setItem( row, 0, twi );
00508 insertFeatureId( id, row );
00509
00510 const QgsAttributeMap& attr = fet.attributeMap();
00511
00512 for ( QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it )
00513 {
00514 if ( !mAttrIdxMap.contains( it.key() ) )
00515 continue;
00516
00517 int h = mAttrIdxMap[ it.key()];
00518
00519 twi = horizontalHeaderItem( h );
00520 if ( !twi )
00521 {
00522 QgsDebugMsg( "header item not found." );
00523 continue;
00524 }
00525
00526 int type = twi->data( AttributeType ).toInt();
00527 bool isNum = ( type == QVariant::Double || type == QVariant::Int );
00528
00529 QString value;
00530
00531 if ( it->isNull() )
00532 {
00533 if ( isNum )
00534 value = "";
00535 else
00536 value = "NULL";
00537 }
00538 else
00539 {
00540 value = it->toString();
00541 }
00542
00543 twi = new QTableWidgetItem( value );
00544 if ( isNum )
00545 twi->setTextAlignment( Qt::AlignRight | Qt::AlignVCenter );
00546 setItem( row, h, twi );
00547 }
00548 }
00549
00550 void QgsAttributeTable::bringSelectedToTop()
00551 {
00552 blockSignals( true );
00553 horizontalHeader()->setSortIndicatorShown( false );
00554 mPreviousSortIndicatorColumn = -1;
00555 int swaptorow = 0;
00556 QList<QTableWidgetSelectionRange> selections = selectedRanges();
00557 bool removeselection;
00558
00559 for ( QList<QTableWidgetSelectionRange>::iterator iter = selections.begin();iter != selections.end();++iter )
00560 {
00561 removeselection = true;
00562 while ( swaptorow < rowCount() && item( swaptorow, 0 )->isSelected() )
00563 {
00564 ++swaptorow;
00565 }
00566
00567 for ( int j = iter->topRow();j <= iter->bottomRow();++j )
00568 {
00569 if ( j > swaptorow )
00570 {
00571 swapRows( j, swaptorow );
00572 setRangeSelected( QTableWidgetSelectionRange( swaptorow, 0, swaptorow, columnCount() - 1 ), true );
00573 ++swaptorow;
00574
00575 }
00576 else
00577 {
00578 removeselection = false;
00579 }
00580 }
00581 if ( removeselection )
00582 {
00583 setRangeSelected( *iter, false );
00584 }
00585 }
00586
00587
00588 rowIdMap.clear();
00589 int id;
00590 for ( int i = 0; i < rowCount(); i++ )
00591 {
00592 id = item( i, 0 )->text().toInt();
00593 rowIdMap.insert( id, i );
00594 }
00595
00596 blockSignals( false );
00597 }
00598
00599 void QgsAttributeTable::selectRowsWithId( const QgsFeatureIds& ids )
00600 {
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624 disconnect( this, SIGNAL( itemSelectionChanged() ), this, SLOT( handleChangedSelections() ) );
00625
00626 clearSelection();
00627 QgsFeatureIds::const_iterator it;
00628 for ( it = ids.begin(); it != ids.end(); it++ )
00629 {
00630 selectRowWithId( *it );
00631 }
00632
00633 connect( this, SIGNAL( itemSelectionChanged() ), this, SLOT( handleChangedSelections() ) );
00634 emit repaintRequested();
00635 }
00636
00637 void QgsAttributeTable::showRowsWithId( const QgsFeatureIds& ids )
00638 {
00639 setUpdatesEnabled( false );
00640
00641
00642 for ( int i = 0; i < rowCount(); i++ )
00643 hideRow( i );
00644
00645
00646 QgsFeatureIds::const_iterator it;
00647 for ( it = ids.begin(); it != ids.end(); it++ )
00648 {
00649 showRow( rowIdMap[*it] );
00650 }
00651
00652 clearSelection();
00653 setUpdatesEnabled( true );
00654 }
00655
00656 void QgsAttributeTable::showAllRows()
00657 {
00658 for ( int i = 0; i < rowCount(); i++ )
00659 showRow( i );
00660 }
00661
00662 void QgsAttributeTable::rowClicked( int row )
00663 {
00664 if ( checkSelectionChanges() )
00665 {
00666 emit repaintRequested();
00667 }
00668 }
00669
00670 void QgsAttributeTable::mouseReleaseEvent( QMouseEvent* e )
00671 {
00672 QTableWidget::mouseReleaseEvent( e );
00673 if ( checkSelectionChanges() )
00674 {
00675 emit repaintRequested();
00676 }
00677 }
00678
00679 bool QgsAttributeTable::checkSelectionChanges()
00680 {
00681 std::set<int> theCurrentSelection;
00682 QList<QTableWidgetSelectionRange> selectedItemRanges = selectedRanges();
00683 QList<QTableWidgetSelectionRange>::const_iterator range_it = selectedItemRanges.constBegin();
00684 for ( ; range_it != selectedItemRanges.constEnd(); ++range_it )
00685 {
00686 for ( int index = range_it->topRow(); index <= range_it->bottomRow(); index++ )
00687 {
00688 theCurrentSelection.insert( index );
00689 }
00690 }
00691
00692 if ( theCurrentSelection == mLastSelectedRows )
00693 {
00694 return false;
00695 }
00696 else
00697 {
00698 mLastSelectedRows = theCurrentSelection;
00699 return true;
00700 }
00701 }
00702
00703 void QgsAttributeTable::attributeValueChanged( int fid, int idx, const QVariant &value )
00704 {
00705 if ( !rowIdMap.contains( fid ) )
00706 return;
00707
00708 if ( !mAttrIdxMap.contains( idx ) )
00709 return;
00710
00711 QTableWidgetItem *twi = horizontalHeaderItem( mAttrIdxMap[ idx ] );
00712 if ( !twi )
00713 {
00714 QgsDebugMsg( "header item not found." );
00715 return;
00716 }
00717
00718 int type = twi->data( AttributeType ).toInt();
00719 bool isNum = ( type == QVariant::Double || type == QVariant::Int );
00720
00721 QString v;
00722
00723 if ( value.isNull() )
00724 {
00725 if ( isNum )
00726 v = "";
00727 else
00728 v = "NULL";
00729 }
00730 else
00731 {
00732 v = value.toString();
00733 }
00734
00735 item( rowIdMap[fid], mAttrIdxMap[idx] )->setText( v );
00736 }
00737
00738 void QgsAttributeTable::featureDeleted( int fid )
00739 {
00740 if ( !rowIdMap.contains( fid ) )
00741 return;
00742
00743 int row = rowIdMap[fid];
00744
00745 removeRow( row );
00746
00747 for ( QMap<int, int>::iterator it = rowIdMap.begin(); it != rowIdMap.end(); it++ )
00748 if ( it.value() > row )
00749 rowIdMap[ it.key()]--;
00750 }
00751
00752 void QgsAttributeTable::addAttribute( int attr, const QgsField &fld )
00753 {
00754 QTableWidgetItem *twi = new QTableWidgetItem( fld.name() );
00755 twi->setData( AttributeIndex, attr );
00756 twi->setData( AttributeName, fld.name() );
00757 twi->setData( AttributeType, fld.type() );
00758
00759 insertColumn( columnCount() );
00760 setHorizontalHeaderItem( columnCount() - 1, twi );
00761
00762 mAttrIdxMap.insert( attr, columnCount() - 1 );
00763 }
00764
00765 void QgsAttributeTable::deleteAttribute( int attr )
00766 {
00767 int column = mAttrIdxMap[attr];
00768
00769 removeColumn( column );
00770 mAttrIdxMap.remove( attr );
00771 for ( QMap<int, int>::iterator it = mAttrIdxMap.begin(); it != mAttrIdxMap.end(); it++ )
00772 {
00773 if ( it.value() > column )
00774 mAttrIdxMap[ it.key()]--;
00775 }
00776 }