src/app/qgsattributetabledisplay.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           QgsAttributeTableDisplay.cpp  -  description
00003                              -------------------
00004     begin                : Sat Nov 23 2002
00005     copyright            : (C) 2002 by Gary E.Sherman
00006     email                : sherman at mrcc dot com
00007        Romans 3:23=>Romans 6:23=>Romans 5:8=>Romans 10:9,10=>Romans 12
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 /* $Id: qgsattributetabledisplay.cpp 9506 2008-10-20 21:44:15Z timlinux $ */
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   // establish connection to table
00084   connect( tblAttributes, SIGNAL( cellChanged( int, int ) ), this, SLOT( changeFeatureAttribute( int, int ) ) );
00085 
00086   // establish connections to layer
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   // establish connections between table and vector layer
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   // fill in mSearchColumns with available columns
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   // TODO: create better labels
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 /* assertion failure in NSMenuItem setSubmenu (Qt 4.5.0-snapshot-20080830) */
00177     menuBar->addMenu( QgisApp::instance()->windowMenu() );
00178 
00179     menuBar->addMenu( QgisApp::instance()->helpMenu() );
00180 #endif
00181 
00182     // Create action to select this window and add it to Window menu
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   // Give the table the most recent copy of the actions for this layer.
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 ) // show only matching
00331   {
00332     tblAttributes->showRowsWithId( mSearchIds );
00333   }
00334   else
00335   {
00336     // make sure that all rows are shown
00337     tblAttributes->showAllRows();
00338 
00339     // select matching
00340     mLayer->setSelectedFeatures( mSearchIds );
00341 
00342     if ( item == 1 ) // select matching and bring to top
00343       tblAttributes->bringSelectedToTop();
00344   }
00345 
00346   QApplication::restoreOverrideCursor();
00347 }
00348 
00349 
00350 void QgsAttributeTableDisplay::doSearch( QString searchString )
00351 {
00352   mSearchString = searchString;
00353 
00354   // parse search string (and build parsed tree)
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     // check if there were errors during evaluating
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   // update table
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   // display the attribute table
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   // select rows which should be selected
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 }

Generated on Tue Oct 28 16:51:26 2008 for Quantum GIS API Documentation by  doxygen 1.5.1