00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "qgsattributetabledisplay.h"
00021
00022 #include "qgisapp.h"
00023 #include "qgsapplication.h"
00024 #include "qgsfeature.h"
00025 #include "qgsfield.h"
00026 #include "qgslogger.h"
00027 #include "qgssearchquerybuilder.h"
00028 #include "qgssearchstring.h"
00029 #include "qgssearchtreenode.h"
00030 #include "qgsvectorlayer.h"
00031 #include "qgsvectordataprovider.h"
00032 #include "qgscontexthelp.h"
00033
00034 #include <QCloseEvent>
00035 #include <QMenuBar>
00036 #include <QMessageBox>
00037 #include <QIcon>
00038 #include <QPixmap>
00039 #include <QSettings>
00040 #include <QToolButton>
00041 #include <QDockWidget>
00042
00043 class QAttributeTableDock : public QDockWidget
00044 {
00045 public:
00046 QAttributeTableDock( const QString & title, QWidget * parent = 0, Qt::WindowFlags flags = 0 )
00047 : QDockWidget( title, parent, flags )
00048 {
00049 }
00050
00051 virtual void closeEvent( QCloseEvent * ev )
00052 {
00053 deleteLater();
00054 }
00055 };
00056
00057 QgsAttributeTableDisplay::QgsAttributeTableDisplay( QgsVectorLayer* layer )
00058 : QDialog( 0, Qt::Window ),
00059 mLayer( layer ),
00060 mDock( NULL )
00061 {
00062 setupUi( this );
00063 restorePosition();
00064 setTheme();
00065
00066 mToggleEditingButton->setEnabled( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues );
00067 mToggleEditingButton->setChecked( layer->isEditable() );
00068
00069 connect( mRemoveSelectionButton, SIGNAL( clicked() ), this, SLOT( removeSelection() ) );
00070 connect( mSelectedToTopButton, SIGNAL( clicked() ), this, SLOT( selectedToTop() ) );
00071 connect( mInvertSelectionButton, SIGNAL( clicked() ), this, SLOT( invertSelection() ) );
00072 connect( mCopySelectedRowsButton, SIGNAL( clicked() ), this, SLOT( copySelectedRowsToClipboard() ) );
00073 connect( mZoomMapToSelectedRowsButton, SIGNAL( clicked() ), this, SLOT( zoomMapToSelectedRows() ) );
00074 connect( mSearchButton, SIGNAL( clicked() ), this, SLOT( search() ) );
00075 connect( mSearchShowResults, SIGNAL( activated( int ) ), this, SLOT( searchShowResultsChanged( int ) ) );
00076 connect( btnAdvancedSearch, SIGNAL( clicked() ), this, SLOT( advancedSearch() ) );
00077 connect( buttonBox, SIGNAL( helpRequested() ), this, SLOT( showHelp() ) );
00078 connect( buttonBox->button( QDialogButtonBox::Close ), SIGNAL( clicked() ), this, SLOT( close() ) );
00079
00080 connect( mToggleEditingButton, SIGNAL( clicked() ), this, SLOT( toggleEditing() ) );
00081 connect( this, SIGNAL( editingToggled( QgsMapLayer * ) ), QgisApp::instance(), SLOT( toggleEditing( QgsMapLayer * ) ) );
00082
00083
00084 connect( tblAttributes, SIGNAL( cellChanged( int, int ) ), this, SLOT( changeFeatureAttribute( int, int ) ) );
00085
00086
00087 connect( mLayer, SIGNAL( layerDeleted() ), this, SLOT( close() ) );
00088
00089 connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
00090
00091 connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
00092 connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );
00093
00094 connect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
00095 connect( mLayer, SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
00096
00097 connect( mLayer, SIGNAL( attributeValueChanged( int, int, const QVariant & ) ),
00098 tblAttributes, SLOT( attributeValueChanged( int, int, const QVariant & ) ) );
00099
00100 connect( mLayer, SIGNAL( featureDeleted( int ) ),
00101 tblAttributes, SLOT( featureDeleted( int ) ) );
00102
00103
00104 connect( tblAttributes, SIGNAL( selected( int, bool ) ), mLayer, SLOT( select( int, bool ) ) );
00105 connect( tblAttributes, SIGNAL( selectionRemoved( bool ) ), mLayer, SLOT( removeSelection( bool ) ) );
00106 connect( tblAttributes, SIGNAL( repaintRequested() ), mLayer, SLOT( triggerRepaint() ) );
00107
00108
00109 const QgsFieldMap& xfields = mLayer->pendingFields();
00110 QgsFieldMap::const_iterator fldIt;
00111 for ( fldIt = xfields.constBegin(); fldIt != xfields.constEnd(); ++fldIt )
00112 {
00113 mSearchColumns->addItem( fldIt->name() );
00114 }
00115
00116
00117 mSearchShowResults->addItem( tr( "select" ) );
00118 mSearchShowResults->addItem( tr( "select and bring to top" ) );
00119 mSearchShowResults->addItem( tr( "show only matching" ) );
00120
00121 QSettings mySettings;
00122 bool myDockFlag = mySettings.value( "/qgis/dockAttributeTable", false ).toBool();
00123 if ( myDockFlag )
00124 {
00125 mDock = new QAttributeTableDock( tr( "Attribute table - " ) + layer->name(), QgisApp::instance() );
00126 mDock->setAllowedAreas( Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea );
00127 mDock->setWidget( this );
00128 QgisApp::instance()->addDockWidget( Qt::BottomDockWidgetArea, mDock );
00129 buttonBox->setVisible( false );
00130 }
00131
00132 setWindowTitle( tr( "Attribute table - " ) + layer->name() );
00133
00134 #ifdef Q_WS_MAC
00135 if ( !myDockFlag )
00136 {
00137 QMenuBar *menuBar = new QMenuBar( this );
00138
00139 QMenu *appMenu = menuBar->addMenu( tr( "QGIS" ) );
00140 appMenu->addAction( QgisApp::instance()->actionAbout() );
00141 appMenu->addAction( QgisApp::instance()->actionOptions() );
00142
00143 QMenu *fileMenu = menuBar->addMenu( tr( "File" ) );
00144 QAction *closeAction = fileMenu->addAction( tr( "Close" ), this, SLOT( close() ), tr( "Ctrl+W" ) );
00145
00146 QMenu *editMenu = menuBar->addMenu( tr( "Edit" ) );
00147 QAction *undoAction = editMenu->addAction( tr( "&Undo" ), this, SLOT( undo() ), tr( "Ctrl+Z" ) );
00148 undoAction->setEnabled( false );
00149 editMenu->addSeparator();
00150 QAction *cutAction = editMenu->addAction( tr( "Cu&t" ), this, SLOT( cut() ), tr( "Ctrl+X" ) );
00151 cutAction->setEnabled( false );
00152 QAction *copyAction = editMenu->addAction(
00153 mCopySelectedRowsButton->icon(), tr( "&Copy" ), this, SLOT( copySelectedRowsToClipboard() ), tr( "Ctrl+C" ) );
00154 QAction *pasteAction = editMenu->addAction( tr( "&Paste" ), this, SLOT( paste() ), tr( "Ctrl+V" ) );
00155 pasteAction->setEnabled( false );
00156 QAction *deleteAction = editMenu->addAction(
00157 mRemoveSelectionButton->icon(), tr( "Delete" ), this, SLOT( removeSelection() ) );
00158
00159 QMenu *layerMenu = menuBar->addMenu( tr( "Layer" ) );
00160 QAction *zoomToSelectedAction = layerMenu->addAction(
00161 mZoomMapToSelectedRowsButton->icon(), tr( "Zoom to Selection" ), this, SLOT( zoomMapToSelectedRows() ), tr( "Ctrl+J" ) );
00162 layerMenu->addSeparator();
00163 QAction *toggleEditingAction = layerMenu->addAction(
00164 mToggleEditingButton->icon(), tr( "Toggle Editing" ), this, SLOT( toggleEditing() ) );
00165 toggleEditingAction->setEnabled( mToggleEditingButton->isEnabled() );
00166 toggleEditingAction->setCheckable( true );
00167 toggleEditingAction->setChecked( mToggleEditingButton->isChecked() );
00168 connect( mToggleEditingButton, SIGNAL( toggled( bool ) ), toggleEditingAction, SLOT( setChecked( bool ) ) );
00169
00170 QMenu *tableMenu = menuBar->addMenu( tr( "Table" ) );
00171 QAction *moveToTopAction = tableMenu->addAction(
00172 mSelectedToTopButton->icon(), tr( "Move to Top" ), this, SLOT( selectedToTop() ) );
00173 QAction *invertAction = tableMenu->addAction(
00174 mInvertSelectionButton->icon(), tr( "Invert" ), this, SLOT( invertSelection() ) );
00175
00176 #ifndef Q_WS_MAC64
00177 menuBar->addMenu( QgisApp::instance()->windowMenu() );
00178
00179 menuBar->addMenu( QgisApp::instance()->helpMenu() );
00180 #endif
00181
00182
00183 mWindowAction = new QAction( windowTitle(), this );
00184 connect( mWindowAction, SIGNAL( triggered() ), this, SLOT( activate() ) );
00185 QgisApp::instance()->addWindow( mWindowAction );
00186 }
00187 #endif
00188 }
00189
00190 QgsAttributeTableDisplay::~QgsAttributeTableDisplay()
00191 {
00192 smTables.remove( mLayer );
00193 }
00194
00195 #ifdef Q_WS_MAC
00196 void QgsAttributeTableDisplay::changeEvent( QEvent* event )
00197 {
00198 QDialog::changeEvent( event );
00199 switch ( event->type() )
00200 {
00201 case QEvent::ActivationChange:
00202 if ( QApplication::activeWindow() == this )
00203 {
00204 mWindowAction->setChecked( true );
00205 }
00206 break;
00207
00208 default:
00209 break;
00210 }
00211 }
00212 #endif
00213
00214 void QgsAttributeTableDisplay::closeEvent( QCloseEvent *ev )
00215 {
00216 QDialog::closeEvent( ev );
00217
00218 if ( mDock == NULL )
00219 saveWindowLocation();
00220
00221 deleteLater();
00222 }
00223
00224 void QgsAttributeTableDisplay::fillTable()
00225 {
00226 tblAttributes->fillTable( mLayer );
00227 tblAttributes->setReadOnly( !mLayer->isEditable() );
00228
00229 selectionChanged();
00230
00231
00232 setAttributeActions( *mLayer->actions() );
00233 }
00234
00235 void QgsAttributeTableDisplay::toggleEditing()
00236 {
00237 emit editingToggled( mLayer );
00238 }
00239
00240 void QgsAttributeTableDisplay::setAttributeActions( const QgsAttributeAction &action )
00241 {
00242 tblAttributes->setAttributeActions( action );
00243 }
00244
00245 void QgsAttributeTableDisplay::selectRowsWithId( const QgsFeatureIds &ids )
00246 {
00247 tblAttributes->selectRowsWithId( ids );
00248 }
00249
00250 void QgsAttributeTableDisplay::setTheme()
00251 {
00252 mRemoveSelectionButton->setIcon( QgisApp::getThemeIcon( "/mActionUnselectAttributes.png" ) );
00253 mSelectedToTopButton->setIcon( QgisApp::getThemeIcon( "/mActionSelectedToTop.png" ) );
00254 mInvertSelectionButton->setIcon( QgisApp::getThemeIcon( "/mActionInvertSelection.png" ) );
00255 mCopySelectedRowsButton->setIcon( QgisApp::getThemeIcon( "/mActionCopySelected.png" ) );
00256 mZoomMapToSelectedRowsButton->setIcon( QgisApp::getThemeIcon( "/mActionZoomToSelected.png" ) );
00257 mToggleEditingButton->setIcon( QgisApp::getThemeIcon( "/mActionToggleEditing.png" ) );
00258 }
00259
00260 void QgsAttributeTableDisplay::editingToggled()
00261 {
00262 mToggleEditingButton->setChecked( mLayer->isEditable() );
00263 tblAttributes->setReadOnly( !mLayer->isEditable() );
00264 }
00265
00266 void QgsAttributeTableDisplay::selectedToTop()
00267 {
00268 tblAttributes->bringSelectedToTop();
00269 }
00270
00271 void QgsAttributeTableDisplay::invertSelection()
00272 {
00273 if ( !mLayer )
00274 return;
00275
00276 QApplication::setOverrideCursor( Qt::WaitCursor );
00277 mLayer->invertSelection();
00278 QApplication::restoreOverrideCursor();
00279 }
00280
00281 void QgsAttributeTableDisplay::removeSelection()
00282 {
00283 tblAttributes->clearSelection();
00284 mLayer->triggerRepaint();
00285 }
00286
00287 void QgsAttributeTableDisplay::copySelectedRowsToClipboard()
00288 {
00289 QgisApp::instance()->editCopy( mLayer );
00290 }
00291
00292 void QgsAttributeTableDisplay::zoomMapToSelectedRows()
00293 {
00294 QgisApp::instance()->zoomToSelected();
00295 }
00296
00297 void QgsAttributeTableDisplay::search()
00298 {
00299 int type = tblAttributes->item( 0, mSearchColumns->currentIndex() )->data( QgsAttributeTable::AttributeType ).toInt();
00300 bool numeric = ( type == QVariant::Int || type == QVariant::Double );
00301
00302 QString str;
00303 str = mSearchColumns->currentText();
00304 if ( numeric )
00305 str += " = '";
00306 else
00307 str += " ~ '";
00308 str += mSearchText->text();
00309 str += "'";
00310
00311 doSearch( str );
00312 }
00313
00314
00315 void QgsAttributeTableDisplay::advancedSearch()
00316 {
00317 QgsSearchQueryBuilder dlg( mLayer, this );
00318 dlg.setSearchString( mSearchString );
00319 if ( dlg.exec() )
00320 {
00321 doSearch( dlg.searchString() );
00322 }
00323 }
00324
00325
00326 void QgsAttributeTableDisplay::searchShowResultsChanged( int item )
00327 {
00328 QApplication::setOverrideCursor( Qt::WaitCursor );
00329
00330 if ( item == 2 )
00331 {
00332 tblAttributes->showRowsWithId( mSearchIds );
00333 }
00334 else
00335 {
00336
00337 tblAttributes->showAllRows();
00338
00339
00340 mLayer->setSelectedFeatures( mSearchIds );
00341
00342 if ( item == 1 )
00343 tblAttributes->bringSelectedToTop();
00344 }
00345
00346 QApplication::restoreOverrideCursor();
00347 }
00348
00349
00350 void QgsAttributeTableDisplay::doSearch( QString searchString )
00351 {
00352 mSearchString = searchString;
00353
00354
00355 QgsSearchString search;
00356 if ( !search.setString( searchString ) )
00357 {
00358 QMessageBox::critical( this, tr( "Search string parsing error" ), search.parserErrorMsg() );
00359 return;
00360 }
00361
00362 QgsSearchTreeNode* searchTree = search.tree();
00363 if ( searchTree == NULL )
00364 {
00365 QMessageBox::information( this, tr( "Search results" ), tr( "You've supplied an empty search string." ) );
00366 return;
00367 }
00368
00369 QgsDebugMsg( "Search by attribute: " + searchString + " parsed as: " + search.tree()->makeSearchString() );
00370
00371 QApplication::setOverrideCursor( Qt::WaitCursor );
00372
00373 mSearchIds.clear();
00374
00375 mLayer->select( mLayer->pendingAllAttributesList(), QgsRect(), false );
00376
00377 QgsFeature f;
00378 while ( mLayer->nextFeature( f ) )
00379 {
00380 if ( searchTree->checkAgainst( mLayer->pendingFields(), f.attributeMap() ) )
00381 {
00382 mSearchIds << f.id();
00383 }
00384
00385
00386 if ( searchTree->hasError() )
00387 break;
00388 }
00389
00390 QApplication::restoreOverrideCursor();
00391
00392 if ( searchTree->hasError() )
00393 {
00394 QMessageBox::critical( this, tr( "Error during search" ), searchTree->errorMsg() );
00395 return;
00396 }
00397
00398
00399 searchShowResultsChanged( mSearchShowResults->currentIndex() );
00400
00401 QString str;
00402 if ( mSearchIds.size() )
00403 str.sprintf( tr( "Found %d matching features.", "", mSearchIds.size() ).toUtf8(), mSearchIds.size() );
00404 else
00405 str = tr( "No matching features found." );
00406 QMessageBox::information( this, tr( "Search results" ), str );
00407 }
00408
00409 void QgsAttributeTableDisplay::restorePosition()
00410 {
00411 QSettings settings;
00412 restoreGeometry( settings.value( "/Windows/AttributeTable/geometry" ).toByteArray() );
00413 }
00414
00415 void QgsAttributeTableDisplay::saveWindowLocation()
00416 {
00417 QSettings settings;
00418 settings.setValue( "/Windows/AttributeTable/geometry", saveGeometry() );
00419 }
00420
00421 void QgsAttributeTableDisplay::showHelp()
00422 {
00423 QgsContextHelp::run( context_id );
00424 }
00425
00426 void QgsAttributeTableDisplay::changeFeatureAttribute( int row, int column )
00427 {
00428 if ( column == 0 )
00429 return;
00430
00431 if ( !mLayer->isEditable() )
00432 return;
00433
00434 mLayer->changeAttributeValue(
00435 tblAttributes->item( row, 0 )->text().toInt(),
00436 tblAttributes->horizontalHeaderItem( column )->data( QgsAttributeTable::AttributeIndex ).toInt(),
00437 tblAttributes->item( row, column )->text(),
00438 false
00439 );
00440 }
00441
00442 QMap<QgsVectorLayer *, QgsAttributeTableDisplay *> QgsAttributeTableDisplay::smTables;
00443
00444 QgsAttributeTableDisplay *QgsAttributeTableDisplay::attributeTable( QgsVectorLayer *layer )
00445 {
00446 if ( !layer )
00447 return NULL;
00448
00449 if ( smTables.contains( layer ) )
00450 {
00451 QgsAttributeTableDisplay *td = smTables[layer];
00452 td->setAttributeActions( *layer->actions() );
00453 td->activate();
00454
00455 return td;
00456 }
00457
00458 QgsAttributeTableDisplay *td = new QgsAttributeTableDisplay( layer );
00459 if ( !td )
00460 return NULL;
00461
00462
00463 QApplication::setOverrideCursor( Qt::WaitCursor );
00464
00465 try
00466 {
00467 td->fillTable();
00468 }
00469 catch ( std::bad_alloc& ba )
00470 {
00471 Q_UNUSED( ba );
00472 QMessageBox::critical( 0, tr( "bad_alloc exception" ), tr( "Filling the attribute table has been stopped because there was no more virtual memory left" ) );
00473 delete td;
00474 td = NULL;
00475 }
00476
00477 QApplication::restoreOverrideCursor();
00478
00479 if ( !td )
00480 return NULL;
00481
00482 smTables[layer] = td;
00483 td->show();
00484
00485 return td;
00486 }
00487
00488 void QgsAttributeTableDisplay::activate()
00489 {
00490 raise();
00491 setWindowState( windowState() & ~Qt::WindowMinimized );
00492 activateWindow();
00493 }
00494
00495 void QgsAttributeTableDisplay::selectionChanged()
00496 {
00497
00498 selectRowsWithId( mLayer->selectedFeaturesIds() );
00499 }
00500
00501 void QgsAttributeTableDisplay::attributeAdded( int attr )
00502 {
00503 tblAttributes->addAttribute( attr, mLayer->pendingFields()[attr] );
00504 }
00505
00506 void QgsAttributeTableDisplay::attributeDeleted( int attr )
00507 {
00508 tblAttributes->deleteAttribute( attr );
00509 }