From 43278d720b5a99256e892e2023c7bb38c0e5ff9e Mon Sep 17 00:00:00 2001 From: jef Date: Wed, 20 Aug 2008 12:15:14 +0000 Subject: [PATCH] handling vector data geometry and attribute updates refactored QgsVectorLayer: - move attribute part of editing to vector layer class and unify with geometry handling: * remove commitAttributeChanges(), addedFeatures(), deletedFeatureIds(), changedAttributes() and replace with changeAttributeValue(), deleteFeature(), addAttribute() and deleteAttribute() * add pendingFields(), pendingAttributeList(), pendingFeatureCount() * emit signals on start editing and commit, change of attribute values, adding/deleting of attributes and layer or feature removal (currently used in the attribute table) - new commitErrors() method to query errors from commitChanges() - replaced featuresInRectangle with select/getNextFeature combo - edit types added to support more input widgets and input constraints QgsFeature: - remove update aware ctor - unify geometry handling in ctors QgsVectorDataProvider: - add QVariant::Type to supportNativeTypes() QgisApp: - add instance() method to query QgisApp object - replace code at various place to use it instead of passing the pointer arround or searching it in the widget tree. - move toggleEditing() code from the legend here QgsAttributeTable/QgsAttributeTableDisplay: - move attribute table creation legend here - make attribute table dockable (from Tim) - most editing logic moved to QgsVectorLayer - adding/deleting attributes moved to QgsVectorLayerProperties QgsIdentifyResults: - add support for attribute editing when it edit mode QgsVectorLayerProperties: add a new tab to show attribute list: * start/stop editing * add/delete attributes * assign edit type to attributes (unique values, value map, ranges) QgsAttributeDialog: add support for attribute edit types: * selection from unique value render classes (combobox) * selection from unique values of existing features (combobox or line edits with completion) * spinboxes for ranges QgsPostgresProvider: - use read-only connection for cursors and read-write connection for updates - updated native types QgsOgrProvider: - remove unused references to GEOS geometry factory - updated native types git-svn-id: http://svn.osgeo.org/qgis/trunk@9092 c8812cc2-4d05-0410-92ff-de0c093fc19c --- python/core/qgsfeature.sip | 12 - python/core/qgsvectordataprovider.sip | 3 +- python/core/qgsvectorlayer.sip | 179 +- src/app/legend/qgslegend.cpp | 41 +- src/app/legend/qgslegendlayerfile.cpp | 193 +- src/app/legend/qgslegendlayerfile.h | 31 +- src/app/main.cpp | 27 +- src/app/qgisapp.cpp | 405 +-- src/app/qgisapp.h | 15 +- src/app/qgsaddattrdialog.cpp | 28 +- src/app/qgsattributeactiondialog.h | 2 - src/app/qgsattributedialog.cpp | 247 +- src/app/qgsattributetable.cpp | 436 ++- src/app/qgsattributetable.h | 88 +- src/app/qgsattributetabledisplay.cpp | 401 ++- src/app/qgsattributetabledisplay.h | 46 +- src/app/qgsbookmarks.cpp | 6 +- src/app/qgsclipboard.h | 14 +- src/app/qgsdbsourceselect.cpp | 4 +- src/app/qgsdbsourceselect.h | 4 +- src/app/qgsidentifyresults.cpp | 53 +- src/app/qgsidentifyresults.h | 39 +- src/app/qgsmaptooladdfeature.cpp | 779 +++--- src/app/qgsmaptoolidentify.cpp | 335 +-- src/app/qgsmaptoolidentify.h | 19 +- src/app/qgsmaptoolmovefeature.cpp | 98 +- src/app/qgsoptions.cpp | 5 +- src/app/qgsoptions.h | 3 - src/app/qgsserversourceselect.cpp | 3 +- src/app/qgsserversourceselect.h | 5 +- src/app/qgsvectorlayerproperties.cpp | 291 +- src/app/qgsvectorlayerproperties.h | 65 +- src/core/qgsdistancearea.h | 2 - src/core/qgsfeature.cpp | 148 +- src/core/qgsfeature.h | 36 +- src/core/qgsfield.h | 3 + src/core/qgslabel.cpp | 2 +- src/core/qgslabel.h | 4 +- src/core/qgslogger.h | 46 +- src/core/qgssearchtreenode.h | 7 +- src/core/qgssnapper.cpp | 2 +- src/core/qgsvectordataprovider.cpp | 14 +- src/core/qgsvectordataprovider.h | 25 +- src/core/qgsvectorfilewriter.cpp | 6 +- src/core/qgsvectorfilewriter.h | 23 +- src/core/qgsvectorlayer.cpp | 2341 +++++++++-------- src/core/qgsvectorlayer.h | 256 +- .../renderer/qgsgraduatedsymbolrenderer.cpp | 2 +- .../renderer/qgsgraduatedsymbolrenderer.h | 4 +- src/core/renderer/qgssinglesymbolrenderer.cpp | 4 +- src/core/renderer/qgssinglesymbolrenderer.h | 2 +- src/core/renderer/qgsuniquevaluerenderer.cpp | 2 +- src/core/renderer/qgsuniquevaluerenderer.h | 4 +- src/core/symbology/qgssymbol.cpp | 2 +- src/plugins/grass/qgsgrassmodule.h | 8 +- src/providers/gpx/qgsgpxprovider.h | 2 +- src/providers/ogr/qgsogrprovider.cpp | 21 +- src/providers/ogr/qgsogrprovider.h | 17 +- .../postgres/qgspostgresprovider.cpp | 249 +- src/providers/postgres/qgspostgresprovider.h | 23 +- src/ui/qgsattributetablebase.ui | 132 +- src/ui/qgsoptionsbase.ui | 11 +- src/ui/qgsvectorlayerpropertiesbase.ui | 696 +++-- 63 files changed, 4123 insertions(+), 3848 deletions(-) diff --git a/python/core/qgsfeature.sip b/python/core/qgsfeature.sip index 8aa8b3c04d4..01859514758 100644 --- a/python/core/qgsfeature.sip +++ b/python/core/qgsfeature.sip @@ -13,18 +13,6 @@ class QgsFeature //! Constructor QgsFeature(int id = 0, QString typeName = "" ); - /** create a copy of this feature in its uncommitted state. - To do this, you also pass in a reference to the feature's - layer's uncommitted attribute and geometry changes. - The resulting feature will have those changes applied. - - This is useful in the cut/copy routine, where you'd - want a copy of the "current" feature, not the on-disk feature. - */ - QgsFeature( const QgsFeature & rhs, - const QMap >& changedAttributes, - const QMap & changedGeometries ); - /** copy ctor needed due to internal pointer */ QgsFeature(const QgsFeature & rhs ); diff --git a/python/core/qgsvectordataprovider.sip b/python/core/qgsvectordataprovider.sip index 3828f7bfec8..fcbfdf10226 100644 --- a/python/core/qgsvectordataprovider.sip +++ b/python/core/qgsvectordataprovider.sip @@ -88,7 +88,6 @@ class QgsVectorDataProvider : QgsDataProvider */ virtual QGis::WKBTYPE geometryType() const = 0; - /** * Number of features in the layer * @return long containing number of features @@ -230,7 +229,7 @@ class QgsVectorDataProvider : QgsDataProvider QList allAttributesList(); /**Returns the names of the numerical types*/ - const QSet& supportedNativeTypes() const; + const QMap &supportedNativeTypes() const; /** * Set whether provider should return also features that don't have diff --git a/python/core/qgsvectorlayer.sip b/python/core/qgsvectorlayer.sip index 9c3c0b76110..e84aae84c34 100644 --- a/python/core/qgsvectorlayer.sip +++ b/python/core/qgsvectorlayer.sip @@ -1,12 +1,27 @@ - - class QgsVectorLayer : QgsMapLayer { %TypeHeaderCode -#include +#include "qgsvectorlayer.h" %End public: + enum EditType { + LineEdit, + UniqueValues, + UniqueValuesEditable, + ValueMap, + Classification, + Range, + }; + + struct RangeData { + RangeData(); + RangeData(QVariant theMin, QVariant theMax, QVariant theStep); + + QVariant mMin; + QVariant mMax; + QVariant mStep; + }; /** Constructor */ QgsVectorLayer(QString baseName = 0, QString path = 0, QString providerLib = 0); @@ -66,10 +81,6 @@ public: /** Returns the bounding box of the selected features. If there is no selection, QgsRect(0,0,0,0) is returned */ QgsRect boundingBoxOfSelected(); - - /** Insert a copy of the given features into the layer */ - bool addFeatures(QList features, bool makeSelected = TRUE); - /** Copies the symbology settings from another layer. Returns true in case of success */ bool copySymbologySettings(const QgsMapLayer& other); @@ -92,12 +103,12 @@ public: QString providerType() const; /** reads vector layer specific state from project file DOM node. - * @note Called by QgsMapLayer::readXML(). + * @note Called by QgsMapLayer::readXml(). */ virtual bool readXml( QDomNode & layer_node ); /** write vector layer specific state to project file DOM node. - * @note Called by QgsMapLayer::writeXML(). + * @note Called by QgsMapLayer::writeXml(). */ virtual bool writeXml( QDomNode & layer_node, QDomDocument & doc ); @@ -130,17 +141,20 @@ public: */ virtual QString subsetString(); -/**Returns the features contained in the rectangle. Considers the changed, added, deleted and permanent features - @return 0 in case of success*/ - int featuresInRectangle(const QgsRect& searchRect, QList& features /Out/, bool fetchGeometries = true, bool fetchAttributes = true); + void select(QList fetchAttributes = QList(), + QgsRect rect = QgsRect(), + bool fetchGeometry = true); -/**Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features + bool getNextFeature(QgsFeature& feature); + + + /**Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features @return 0 in case of success*/ - int getFeatureAtId(int featureId, QgsFeature& f, bool fetchGeometries = true, bool fetchAttributes = true); + int getFeatureAtId(int featureId, QgsFeature& f, bool fetchGeometries = true, bool fetchAttributes = true); /** Adds a feature - @param lastFeatureInBatch If True, will also go to the effort of e.g. updating the extents. - @return Irue in case of success and False in case of error + @param alsoUpdateExtent If True, will also go to the effort of e.g. updating the extents. + @return True in case of success and False in case of error */ bool addFeature(QgsFeature& f, bool alsoUpdateExtent = TRUE); @@ -168,49 +182,60 @@ public: bool deleteSelectedFeatures(); /**Adds a ring to polygon/multipolygon features - @return 0 in case of success, 1 problem with feature type, 2 ring not closed, 3 ring not valid, 4 ring crosses \ -existing rings, 5 no feature found where ring can be inserted*/ + @return + 0 in case of success, + 1 problem with feature type, + 2 ring not closed, + 3 ring not valid, + 4 ring crosses existing rings, + 5 no feature found where ring can be inserted*/ int addRing(const QList& ring); /**Adds a new island polygon to a multipolygon feature - @return 0 in case of success, 1 if selected feature is not multipolygon, 2 if ring is not a valid geometry, \ -3 if new polygon ring not disjoint with existing rings, 4 if no feature was selected, 5 if several features are selected, \ -6 if selected geometry not found*/ + @return + 0 in case of success, + 1 if selected feature is not multipolygon, + 2 if ring is not a valid geometry, + 3 if new polygon ring not disjoint with existing rings, + 4 if no feature was selected, + 5 if several features are selected, + 6 if selected geometry not found*/ int addIsland(const QList& ring); - /**Translates feature by dx, dy + /**Translates feature by dx, dy @param featureId id of the feature to translate @param dx translation of x-coordinate @param dy translation of y-coordinate @return 0 in case of success*/ int translateFeature(int featureId, double dx, double dy); -/**Splits features cut by the given line + /**Splits features cut by the given line @param splitLine line that splits the layer features @param topologicalEditing true if topological editing is enabled - @return 0 in case of success, 1 if several intersections but only 1 split done, \ - 2 if intersection too complex to be handled, else other error*/ + @return + 0 in case of success, + 1 if several intersections but only 1 split done, + 2 if intersection too complex to be handled, else other error*/ int splitFeatures(const QList& splitLine, bool topologicalEditing = false); -/**Changes the specified geometry such that it has no intersections with other \ + /**Changes the specified geometry such that it has no intersections with other \ polygon (or multipolygon) geometries in this vector layer - @param geom geometry to modify - @return 0 in case of success*/ + @param geom geometry to modify + @return 0 in case of success*/ int removePolygonIntersections(QgsGeometry* geom); -/**Adds topological points for every vertex of the - geometry - @param geom the geometry where each vertex is added to segments of other features - Note: geom is not going to be modified by the function - @return 0 in case of success*/ + /**Adds topological points for every vertex of the geometry + @param geom the geometry where each vertex is added to segments of other features + Note: geom is not going to be modified by the function + @return 0 in case of success*/ int addTopologicalPoints(QgsGeometry* geom); -/**Adds a vertex to segments which intersect point p but don't - already have a vertex there. If a feature already has a vertex at position p, - no additional vertex is inserted. This method is usefull for topological - editing. - @param p position of the vertex - @return 0 in case of success*/ + /**Adds a vertex to segments which intersect point p but don't + already have a vertex there. If a feature already has a vertex at position p, + no additional vertex is inserted. This method is usefull for topological + editing. + @param p position of the vertex + @return 0 in case of success*/ int addTopologicalPoints(const QgsPoint& p); /**Inserts vertices to the snapped segments. @@ -246,29 +271,7 @@ existing rings, 5 no feature found where ring can be inserted*/ @return 0 in case of success */ int snapWithContext(const QgsPoint& startPoint, double snappingTolerance, QMultiMap& snappingResults /Out/, - QgsSnapper::SNAP_TO snap_to); - - /** - Commits edited attributes. Depending on the feature id, - the changes are written to not commited features or redirected to - the data provider - - The commits (in this version) occur in three distinct stages, - (delete attributes, add attributes, change attribute values) - so if a stage fails, it's difficult to roll back cleanly. - - \todo Need to indicate at which stage the failed commit occurred, - for better cleanup and recovery from the error. - - \param deleted Set of attribute indices (i.e. columns) to delete - \param added Map (name, type) of attribute names (i.e. columns) to add - \param changed Map (feature ID, Map (attribute name, new value) ) - of attribute values to change - - */ - bool commitAttributeChanges(const QSet& deleted, - const QMap& added, - const QMap >& changed); + QgsSnapper::SNAP_TO snap_to); /** Draws the layer using coordinate transformation * @return FALSE if an error occurred during drawing @@ -283,20 +286,36 @@ existing rings, 5 no feature found where ring can be inserted*/ */ void drawLabels(QPainter * p, const QgsRect& viewExtent, const QgsMapToPixel* cXf, const QgsCoordinateTransform* ct, double scale); - /** returns array of added features */ - QList& addedFeatures(); + /** returns list of attributes */ + QList pendingAllAttributesList(); - /** returns array of deleted feature IDs */ - QSet& deletedFeatureIds(); - - /** returns array of features with changed attributes */ - QMap >& changedAttributes(); + /** returns fields list which are not commited */ + const QMap &pendingFields(); + /** returns feature count after commit */ + int pendingFeatureCount(); + /** Sets whether some features are modified or not */ void setModified(bool modified = TRUE, bool onlyGeometryWasModified = FALSE); /** Make layer editable */ bool startEditing(); + + /** changed an attribute value (but does not commit it */ + bool changeAttributeValue(int fid, int field, QVariant value, bool emitSignal = true); + + /** add an attribute field (but does not commit it) + returns the field index or -1 in case of failure */ + bool addAttribute(QString name, QString type); + + /** delete an attribute field (but does not commit it) */ + bool deleteAttribute(int attr); + + /** Insert a copy of the given features into the layer (but does not commit it) */ + bool addFeatures(QList features, bool makeSelected = TRUE); + + /** delete a feature from the layer (but does not commit it) */ + bool deleteFeature(int fid); /** Attempts to commit any changes to disk. Returns the result of the attempt. @@ -314,10 +333,23 @@ existing rings, 5 no feature found where ring can be inserted*/ */ bool commitChanges(); + const QStringList &commitErrors(); /** Stop editing and discard the edits */ bool rollBack(); + /**get edit type*/ + EditType editType(int idx); + + /**set edit type*/ + void setEditType(int idx, EditType edit); + + /**access value map*/ + QMap &valueMap(int idx); + + /**access range */ + RangeData &range(int idx); + public slots: /** Select feature by its ID, optionally emit signal selectionChanged() */ @@ -341,6 +373,15 @@ signals: /** This signal is emitted when modifications has been done on layer */ void wasModified(bool onlyGeometry); + void editingStarted(); + void editingStopped(); + void attributeAdded(int idx); + void attributeDeleted(int idx); + void featureDeleted(int fid); + void layerDeleted(); + + void attributeValueChanged(int fid, int idx, const QVariant &); + private: // Private methods /** vector layers are not copyable */ diff --git a/src/app/legend/qgslegend.cpp b/src/app/legend/qgslegend.cpp index 6e4c641e9a3..9a41b5361bc 100755 --- a/src/app/legend/qgslegend.cpp +++ b/src/app/legend/qgslegend.cpp @@ -36,9 +36,8 @@ #include "qgsproject.h" #include "qgsrasterlayer.h" #include "qgsrasterlayerproperties.h" -#include "qgsvectorlayer.h" #include "qgsvectorlayerproperties.h" -#include "qgsvectordataprovider.h" +#include "qgsattributetabledisplay.h" #include #include @@ -1811,29 +1810,39 @@ void QgsLegend::legendLayerZoomNative() void QgsLegend::legendLayerAttributeTable() { if(!mMapCanvas || mMapCanvas->isDrawing()) - { - return; - } + { + return; + } + + QgsVectorLayer *vlayer = 0; // try whether it's a legend layer QgsLegendLayer* ll = dynamic_cast(currentItem()); if (ll) { - ll->table(); - return; + vlayer = dynamic_cast(ll->firstMapLayer()); } - // try whether it's a legend layer file - QgsLegendLayerFile* llf = dynamic_cast(currentItem()); - if (llf) - { - llf->table(); - return; + if(!vlayer) { + // try whether it's a legend layer file + QgsLegendLayerFile* llf = dynamic_cast(currentItem()); + if (llf) + { + vlayer = dynamic_cast(llf->layer()); + } } - // nothing selected - QMessageBox::information(this, tr("No Layer Selected"), - tr("To open an attribute table, you must select a vector layer in the legend")); + if(vlayer) + { + QgsAttributeTableDisplay::attributeTable( vlayer ); + } + else + { + // nothing selected + QMessageBox::information(this, + tr("No Layer Selected"), + tr("To open an attribute table, you must select a vector layer in the legend")); + } } void QgsLegend::readProject(const QDomDocument & doc) diff --git a/src/app/legend/qgslegendlayerfile.cpp b/src/app/legend/qgslegendlayerfile.cpp index 15e26fac3d1..2206e287c6a 100644 --- a/src/app/legend/qgslegendlayerfile.cpp +++ b/src/app/legend/qgslegendlayerfile.cpp @@ -43,7 +43,7 @@ QgsLegendLayerFile::QgsLegendLayerFile(QTreeWidgetItem * theLegendItem, QString theString, QgsMapLayer* theLayer) - : QgsLegendItem(theLegendItem, theString), mLyr(theLayer), mTableDisplay(NULL) + : QgsLegendItem(theLegendItem, theString), mLyr(theLayer) { // Set the initial visibility flag for layers // This user option allows the user to turn off inital drawing of @@ -69,23 +69,12 @@ QgsLegendLayerFile::QgsLegendLayerFile(QTreeWidgetItem * theLegendItem, QString QgsVectorLayer *isVectLyr = dynamic_cast < QgsVectorLayer * >(currentLayer); if (isVectLyr) { - // get notifications of changed selection - used to update attribute table - connect(mLyr.layer(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); - // get notifications of modified layer - used to close table as it's out of sync - connect(mLyr.layer(), SIGNAL(wasModified(bool)), this, SLOT(closeTable(bool))); + connect(mLyr.layer(), SIGNAL(editingStarted()), this, SLOT(updateLegendItem())); + connect(mLyr.layer(), SIGNAL(editingStopped()), this, SLOT(updateLegendItem())); } connect(mLyr.layer(), SIGNAL(layerNameChanged()), this, SLOT(layerNameChanged())); } -QgsLegendLayerFile::~QgsLegendLayerFile() -{ - if (mTableDisplay) - { - mTableDisplay->close(); - delete mTableDisplay; - } -} - QgsLegendItem::DRAG_ACTION QgsLegendLayerFile::accept(LEGEND_ITEM_TYPE type) { return NO_ACTION; @@ -219,112 +208,16 @@ void QgsLegendLayerFile::showInOverview() legend()->updateOverview(); } - -void QgsLegendLayerFile::table() -{ - QgsVectorLayer* vlayer = dynamic_cast(mLyr.layer()); - if (!vlayer) - { - QMessageBox::information(0, tr("Not a vector layer"), - tr("To open an attribute table, you must select a vector layer in the legend")); - return; - } - - QgsAttributeAction& actions = *vlayer->actions(); - - if (mTableDisplay) - { - - mTableDisplay->raise(); - - // Give the table the most recent copy of the actions for this layer. - mTableDisplay->table()->setAttributeActions(actions); - } - else - { - // display the attribute table - QApplication::setOverrideCursor(Qt::WaitCursor); - - // TODO: pointer to QgisApp should be passed instead of NULL - // but we don't have pointer to it. [MD] - // but be can get it using this ugly hack. [jef] - // TODO: do this cleanly - QgisApp *app = NULL; - - QList list = QApplication::topLevelWidgets(); - - int i; - for(i=0; iwindowTitle().startsWith("Quantum GIS") ) - { - app=reinterpret_cast(list[i]); - break; - } - - mTableDisplay = new QgsAttributeTableDisplay(vlayer, app); - try - { - mTableDisplay->table()->fillTable(vlayer); - } - catch(std::bad_alloc& ba) - { - Q_UNUSED(ba); - QMessageBox::critical(0, tr("bad_alloc exception"), tr("Filling the attribute table has been stopped because there was no more virtual memory left")); - } - - connect(mTableDisplay, SIGNAL(deleted()), this, SLOT(invalidateTableDisplay())); - - mTableDisplay->setTitle(tr("Attribute table - ") + vlayer->name()); - mTableDisplay->show(); - - // Give the table the most recent copy of the actions for this layer. - mTableDisplay->table()->setAttributeActions(actions); - - // select rows which should be selected - selectionChanged(); - - // etablish the necessary connections between the table and the vector layer - connect(mTableDisplay->table(), SIGNAL(selected(int, bool)), mLyr.layer(), SLOT(select(int, bool))); - connect(mTableDisplay->table(), SIGNAL(selectionRemoved(bool)), mLyr.layer(), SLOT(removeSelection(bool))); - connect(mTableDisplay->table(), SIGNAL(repaintRequested()), mLyr.layer(), SLOT(triggerRepaint())); - - QApplication::restoreOverrideCursor(); - } - -} - -void QgsLegendLayerFile::invalidateTableDisplay() -{ - // from signal deleted() - table doesn't exist anymore, just erase our pointer - mTableDisplay = 0; -} - -void QgsLegendLayerFile::selectionChanged() -{ - if (!mTableDisplay) - return; - - QgsVectorLayer* vlayer = dynamic_cast(mLyr.layer()); - const QgsFeatureIds& ids = vlayer->selectedFeaturesIds(); - mTableDisplay->table()->selectRowsWithId(ids); - -} - -void QgsLegendLayerFile::closeTable(bool onlyGeometryWasChanged) -{ - if (mTableDisplay) - { - mTableDisplay->close(); - delete mTableDisplay; - mTableDisplay = NULL; - } -} - void QgsLegendLayerFile::saveAsShapefile() { saveAsShapefileGeneral(FALSE); } +void QgsLegendLayerFile::table() +{ + QgsAttributeTableDisplay::attributeTable( dynamic_cast(mLyr.layer()) ); +} + void QgsLegendLayerFile::saveSelectionAsShapefile() { saveAsShapefileGeneral(TRUE); @@ -413,69 +306,6 @@ void QgsLegendLayerFile::saveAsShapefileGeneral(bool saveOnlySelection) } } -void QgsLegendLayerFile::toggleEditing() -{ - QgsVectorLayer* vlayer = dynamic_cast(mLyr.layer()); - if (!vlayer) - return; - - if (!vlayer->isEditable()) - { - vlayer->startEditing(); - if(!(vlayer->getDataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures)) - { - QMessageBox::information(0,tr("Start editing failed"), - tr("Provider cannot be opened for editing")); - } - else - { - vlayer->triggerRepaint(); - } - } - else - { - if(vlayer->isModified()) - { - - // commit or roll back? - QMessageBox::StandardButton commit = QMessageBox::information(0,tr("Stop editing"), tr("Do you want to save the changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - - if(commit==QMessageBox::Save) - { - if(!vlayer->commitChanges()) - { - QMessageBox::information(0,tr("Error"),tr("Could not commit changes")); - - // Leave the in-memory editing state alone, - // to give the user a chance to enter different values - // and try the commit again later - } - } - else if(commit==QMessageBox::Discard) - { - if(!vlayer->rollBack()) - { - QMessageBox::information(0,tr("Error"), - tr("Problems during roll back")); - } - } - else //cancel - { - return; - } - } - else //layer not modified - { - vlayer->rollBack(); - } - vlayer->triggerRepaint(); - - } - - updateLegendItem(); - -} - bool QgsLegendLayerFile::isEditing() { QgsVectorLayer* vlayer = dynamic_cast(mLyr.layer()); @@ -491,7 +321,6 @@ void QgsLegendLayerFile::layerNameChanged() legend()->setName(this, name); } - void QgsLegendLayerFile::addToPopupMenu(QMenu& theMenu, QAction* toggleEditingAction) { QgsMapLayer* lyr = layer(); @@ -526,9 +355,9 @@ void QgsLegendLayerFile::addToPopupMenu(QMenu& theMenu, QAction* toggleEditingAc ||(cap & QgsVectorDataProvider::DeleteFeatures)) { if(toggleEditingAction) - { - theMenu.addAction(toggleEditingAction); - } + { + theMenu.addAction(toggleEditingAction); + } } // save as shapefile diff --git a/src/app/legend/qgslegendlayerfile.h b/src/app/legend/qgslegendlayerfile.h index 06e94482124..3b63b46ad8d 100644 --- a/src/app/legend/qgslegendlayerfile.h +++ b/src/app/legend/qgslegendlayerfile.h @@ -1,6 +1,6 @@ /*************************************************************************** - * Copyright (C) 2005 by Tim Sutton * - * aps02ts@macbuntu * + * Copyright (C) 2005 by Tim Sutton * + * aps02ts@macbuntu * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -26,7 +26,6 @@ #include "qgsmapcanvas.h" class QgsMapLayer; -class QgsAttributeTableDisplay; /** @author Tim Sutton @@ -37,7 +36,7 @@ class QgsLegendLayerFile : public QgsLegendItem public: QgsLegendLayerFile(QTreeWidgetItem * theLegendItem, QString theString, QgsMapLayer* theLayer); - ~QgsLegendLayerFile(); + bool isLeafNode() {return true;} DRAG_ACTION accept(LEGEND_ITEM_TYPE type); QgsLegendItem::DRAG_ACTION accept(const QgsLegendItem* li) const; @@ -47,16 +46,12 @@ public: QPixmap getOriginalPixmap() const; - /** updates item to the current state of the layer */ - void updateLegendItem(); - void setIconAppearance(bool inOverview, bool editable); /**Returns a label for a layer. Is static such that the name can be passed to the constructor of QgsLegendLayerFile*/ static QString nameFromLayer(QgsMapLayer* layer); - void setVisible(bool visible = TRUE); bool isVisible(); @@ -67,28 +62,18 @@ public: void addToPopupMenu(QMenu& theMenu, QAction* toggleEditingAction); public slots: + /** updates item to the current state of the layer */ + void updateLegendItem(); /**Open attribute table*/ void table(); - /**Connected to deleted() signal of attribute table*/ - void invalidateTableDisplay(); - - /**Connected to layer's selectionChanged() */ - void selectionChanged(); - - /**Connected to layer's wasModified() */ - void closeTable(bool onlyGeometryWasChanged); - /**Save as shapefile*/ void saveAsShapefile(); /**Save selection as shapefile*/ void saveSelectionAsShapefile(); - /**Toggle editing for layer*/ - void toggleEditing(); - /**Return editing status for layer*/ bool isEditing(); @@ -97,7 +82,7 @@ public: /**Layer name has changed - set it also in legend*/ void layerNameChanged(); - + protected: /**Save as shapefile (called from saveAsShapefile and saveSelectionAsShapefile)*/ @@ -105,10 +90,6 @@ public: /** layer identified by its layer id */ QgsMapCanvasLayer mLyr; - - /** Pointer to the table display object if there is one, otherwise NULL */ - QgsAttributeTableDisplay* mTableDisplay; - }; #endif diff --git a/src/app/main.cpp b/src/app/main.cpp index 5e6cc175e67..c3f7925ee0c 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -156,25 +156,20 @@ OSErr openDocumentsAEHandler(const AppleEvent *event, AppleEvent *reply, SRefCon } } - // Open files now if application has been initialized - QWidgetList wl = QApplication::topLevelWidgets(); - for (QWidgetList::iterator it = wl.begin(); it != wl.end(); ++it) + QgisApp *qgis = QgisApp::instance(); + if(qgis) { - QgisApp *qgis = dynamic_cast(*it); - if (qgis && qgis->objectName() == "QgisApp") + if (!myProjectFileName.isEmpty()) { - if (!myProjectFileName.isEmpty()) - { - qgis->openProject(myProjectFileName); - } - for (QStringList::Iterator myIterator = myFileList.begin(); - myIterator != myFileList.end(); ++myIterator ) - { - QString fileName = *myIterator; - qgis->openLayer(fileName); - } - break; + qgis->openProject(myProjectFileName); } + for (QStringList::Iterator myIterator = myFileList.begin(); + myIterator != myFileList.end(); ++myIterator ) + { + QString fileName = *myIterator; + qgis->openLayer(fileName); + } + break; } } return noErr; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index d47ca1f9f35..ca2262583f4 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -125,6 +125,7 @@ #include "qgsserversourceselect.h" #include "qgsvectordataprovider.h" #include "qgsvectorlayer.h" +#include "qgsattributetabledisplay.h" // // Gdal/Ogr includes @@ -290,7 +291,7 @@ static void customSrsValidation_(QgsSpatialRefSys* srs) // XXX TODO: Change project to store selected CS as 'projectSRS' not 'selectedWKT' proj4String = QgsProject::instance()->readEntry("SpatialRefSys","//ProjectSRSProj4String",GEOPROJ4); QgsDebugMsg("Layer srs set from project: " + proj4String); - srs->createFromProj4(proj4String); + srs->createFromProj4(proj4String); srs->debugPrint(); } else ///Projections/defaultBehaviour==useGlobal @@ -302,19 +303,31 @@ static void customSrsValidation_(QgsSpatialRefSys* srs) } +QgisApp *QgisApp::smInstance = 0; + // constructor starts here QgisApp::QgisApp(QSplashScreen *splash, QWidget * parent, Qt::WFlags fl) : QMainWindow(parent,fl), mSplash(splash), mPythonUtils(NULL), mPythonConsole(NULL) { + if(smInstance) { + QMessageBox::critical( + this, + tr("Multiple Instances of QgisApp"), + tr("Multiple instances of Quantum GIS application object detected.\nPlease contact the developers.\n")); + abort(); + } + + smInstance = this; + // setupUi(this); resize(640, 480); mSplash->showMessage(tr("Checking database"), Qt::AlignHCenter | Qt::AlignBottom); qApp->processEvents(); // Do this early on before anyone else opens it and prevents us copying it - createDB(); + createDB(); mSplash->showMessage(tr("Reading settings"), Qt::AlignHCenter | Qt::AlignBottom); @@ -355,7 +368,7 @@ static void customSrsValidation_(QgsSpatialRefSys* srs) QgsSpatialRefSys::setCustomSrsValidation(customSrsValidation_); // set graphical message output QgsMessageOutput::setMessageOutputCreator(messageOutputViewer_); - + fileNew(); // prepare empty project qApp->processEvents(); @@ -363,7 +376,7 @@ static void customSrsValidation_(QgsSpatialRefSys* srs) mSplash->showMessage(tr("Checking provider plugins"), Qt::AlignHCenter | Qt::AlignBottom); qApp->processEvents(); QgsApplication::initQgis(); - + mSplash->showMessage(tr("Starting Python"), Qt::AlignHCenter | Qt::AlignBottom); qApp->processEvents(); loadPythonSupport(); @@ -382,8 +395,8 @@ static void customSrsValidation_(QgsSpatialRefSys* srs) // now build raster file filter QgsRasterLayer::buildSupportedRasterFileFilter( mRasterFileFilter ); -/* - // Set the background colour for toolbox and overview as they default to +#if 0 + // Set the background colour for toolbox and overview as they default to // white instead of the window color QPalette myPalette = toolBox->palette(); myPalette.setColor(QPalette::Button, myPalette.window().color()); @@ -393,22 +406,22 @@ static void customSrsValidation_(QgsSpatialRefSys* srs) myPalette.setColor(QPalette::Button, myPalette.window().color()); mMapLegend->setPalette(myPalette); //and for overview control this is done in createOverView method -*/ +#endif // Do this last in the ctor to ensure that all members are instantiated properly setupConnections(); // - // Please make sure this is the last thing the ctor does so that we can ensure teh + // Please make sure this is the last thing the ctor does so that we can ensure the // widgets are all initialised before trying to restore their state. // mSplash->showMessage(tr("Restoring window state"), Qt::AlignHCenter | Qt::AlignBottom); qApp->processEvents(); restoreWindowState(); - + mSplash->showMessage(tr("QGIS Ready!"), Qt::AlignHCenter | Qt::AlignBottom); mMapTipsVisible = false; - // setup drag drop + // setup drag drop setAcceptDrops(true); mFullScreenMode = false; @@ -425,7 +438,7 @@ QgisApp::~QgisApp() { delete mInternalClipboard; delete mQgisInterface; - + delete mMapTools.mZoomIn; delete mMapTools.mZoomOut; delete mMapTools.mPan; @@ -446,7 +459,7 @@ QgisApp::~QgisApp() delete mPythonConsole; delete mPythonUtils; - + // delete map layer registry and provider registry QgsApplication::exitQgis(); } @@ -501,8 +514,8 @@ void QgisApp::readSettings() ////////////////////////////////////////////////////////////////////// -// Set Up the gui toolbars, menus, statusbar etc -////////////////////////////////////////////////////////////////////// +// Set Up the gui toolbars, menus, statusbar etc +////////////////////////////////////////////////////////////////////// void QgisApp::createActions() { @@ -560,10 +573,10 @@ void QgisApp::createActions() mActionAddLayer->setShortcut(tr("D","Add a PostGIS Layer")); mActionAddLayer->setStatusTip(tr("Add a PostGIS Layer")); //#ifdef HAVE_POSTGRESQL -// std::cout << "HAVE_POSTGRESQL is defined" << std::endl; +// std::cout << "HAVE_POSTGRESQL is defined" << std::endl; // assert(0); //#else -// std::cout << "HAVE_POSTGRESQL not defined" << std::endl; +// std::cout << "HAVE_POSTGRESQL not defined" << std::endl; // assert(0); //#endif connect(mActionAddLayer, SIGNAL(triggered()), this, SLOT(addDatabaseLayer())); @@ -647,7 +660,7 @@ void QgisApp::createActions() mActionCheckQgisVersion= new QAction(getThemeIcon("mActionCheckQgisVersion.png"), tr("Check Qgis Version"), this); mActionCheckQgisVersion->setStatusTip(tr("Check if your QGIS version is up to date (requires internet access)")); connect(mActionCheckQgisVersion, SIGNAL(triggered()), this, SLOT(checkQgisVersion())); - // + // // View Menu Items // mActionDraw= new QAction(getThemeIcon("mActionDraw.png"), tr("Refresh"), this); @@ -757,18 +770,18 @@ void QgisApp::createActions() // // Add the whats this toolbar button // QWhatsThis::whatsThisButton(mHelpToolBar); - // + // // // Digitising Toolbar Items // - mActionToggleEditing = new QAction(getThemeIcon("mActionToggleEditing.png"), + mActionToggleEditing = new QAction(getThemeIcon("mActionToggleEditing.png"), tr("Toggle editing"), this); - mActionToggleEditing->setStatusTip(tr("Toggles the editing state of the current layer")); + mActionToggleEditing->setStatusTip(tr("Toggles the editing state of the current layer")); mActionToggleEditing->setCheckable(true); connect(mActionToggleEditing, SIGNAL(triggered()), this, SLOT(toggleEditing())); mActionToggleEditing->setEnabled(false); - + // mActionCapturePoint= new QAction(getThemeIcon("mActionCapturePoint.png"), tr("Capture Point"), this); mActionCapturePoint->setShortcut(tr(".","Capture Points")); @@ -854,7 +867,7 @@ void QgisApp::showPythonDialog() { if (!mPythonUtils || !mPythonUtils->isEnabled()) return; - + if (mPythonConsole == NULL) mPythonConsole = new QgsPythonDialog(mQgisInterface, mPythonUtils); mPythonConsole->show(); @@ -968,7 +981,7 @@ void QgisApp::createMenus() mSettingsMenu->addAction(mActionProjectProperties); mSettingsMenu->addAction(mActionCustomProjection); mSettingsMenu->addAction(mActionOptions); - + // // Plugins Menu mPluginMenu = menuBar()->addMenu(tr("&Plugins")); @@ -1000,7 +1013,7 @@ void QgisApp::createToolBars() // Note: we need to set each object name to ensure that // qmainwindow::saveState and qmainwindow::restoreState // work properly - + // // File Toolbar mFileToolBar = addToolBar(tr("File")); @@ -1110,7 +1123,7 @@ void QgisApp::createStatusBar() mProgressBar->setWhatsThis(tr("Progress bar that displays the status " "of rendering layers and other time-intensive operations")); statusBar()->addPermanentWidget(mProgressBar, 1); - // Bumped the font up one point size since 8 was too + // Bumped the font up one point size since 8 was too // small on some platforms. A point size of 9 still provides // plenty of display space on 1024x768 resolutions QFont myFont( "Arial", 9 ); @@ -1191,7 +1204,7 @@ void QgisApp::createStatusBar() mOnTheFlyProjectionStatusButton->setIcon(getThemeIcon("mIconProjectionDisabled.png")); if (!QFile::exists(QgsApplication::defaultThemePath() + "/mIconProjectionDisabled.png")) { - QMessageBox::critical(this, tr("Resource Location Error"), + QMessageBox::critical(this, tr("Resource Location Error"), tr("Error reading icon resources from: \n %1\n Quitting...").arg( QgsApplication::defaultThemePath() + "/mIconProjectionDisabled.png" )); @@ -1285,7 +1298,7 @@ void QgisApp::setupConnections() // connect the "cleanup" slot connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(saveWindowState())); //connect the legend, mapcanvas and overview canvas to the registry - + // connect map layer registry signals to legend connect(QgsMapLayerRegistry::instance(), SIGNAL(layerWillBeRemoved(QString)), mMapLegend, SLOT(removeLayer(QString))); @@ -1296,7 +1309,7 @@ void QgisApp::setupConnections() connect(mMapLegend, SIGNAL(currentLayerChanged(QgsMapLayer*)), this, SLOT(activateDeactivateLayerRelatedActions(QgsMapLayer*))); - + //signal when mouse moved over window (coords display in status bar) connect(mMapCanvas, SIGNAL(xyCoordinates(QgsPoint &)), this, SLOT(showMouseCoordinate(QgsPoint &))); connect(mMapCanvas->mapRenderer(), SIGNAL(drawingProgress(int,int)), this, SLOT(showProgress(int,int))); @@ -1324,7 +1337,7 @@ void QgisApp::createCanvas() mMapCanvas = new QgsMapCanvas(this, "theMapCanvas" ); mMapCanvas->setWhatsThis(tr("Map canvas. This is where raster and vector " "layers are displayed when added to the map")); - + setCentralWidget(mMapCanvas); // set the focus to the map canvas mMapCanvas->setFocus(); @@ -1364,7 +1377,7 @@ void QgisApp::createCanvas() mMapTools.mAddRing->setAction(mActionAddRing); mMapTools.mAddIsland = new QgsMapToolAddIsland(mMapCanvas); //ensure that non edit tool is initialised or we will get crashes in some situations - mNonEditMapTool = mMapTools.mPan; + mNonEditMapTool = mMapTools.mPan; } void QgisApp::createOverview() @@ -1372,7 +1385,7 @@ void QgisApp::createOverview() // overview canvas QgsMapOverviewCanvas* overviewCanvas = new QgsMapOverviewCanvas(NULL, mMapCanvas); overviewCanvas->setWhatsThis(tr("Map overview canvas. This canvas can be used to display a locator map that shows the current extent of the map canvas. The current extent is shown as a red rectangle. Any layer on the map can be added to the overview canvas.")); - + QBitmap overviewPanBmp = QBitmap::fromData(QSize(16, 16), pan_bits); QBitmap overviewPanBmpMask = QBitmap::fromData(QSize(16, 16), pan_mask_bits); mOverviewMapCursor = new QCursor(overviewPanBmp, overviewPanBmpMask, 5, 5); @@ -1387,7 +1400,7 @@ void QgisApp::createOverview() addDockWidget(Qt::LeftDockWidgetArea, mOverviewDock); mMapCanvas->setOverview(overviewCanvas); - + // moved here to set anti aliasing to both map canvas and overview QSettings mySettings; mMapCanvas->enableAntiAliasing(mySettings.value("/qgis/enable_anti_aliasing",false).toBool()); @@ -1563,7 +1576,7 @@ void QgisApp::about() versionString += tr(" This copy of QGIS has been built without PostgreSQL support."); #endif versionString += tr("\nThis binary was compiled against Qt %1," - "and is currently running against Qt %2") + "and is currently running against Qt %2") .arg(QT_VERSION_STR) .arg(qVersion()); @@ -1624,7 +1637,7 @@ void QgisApp::about() void QgisApp::restoreSessionPlugins(QString thePluginDirString) { QSettings mySettings; - + #ifdef WIN32 QString pluginExt = "*.dll"; #else @@ -1643,7 +1656,7 @@ void QgisApp::restoreSessionPlugins(QString thePluginDirString) bool loaded = myLib->load(); if (loaded) { - + name_t * myName =(name_t *) myLib->resolve("name"); description_t * myDescription = (description_t *) myLib->resolve("description"); version_t * myVersion = (version_t *) myLib->resolve("version"); @@ -1679,10 +1692,10 @@ void QgisApp::restoreSessionPlugins(QString thePluginDirString) } QString pluginName, description, version; - + if (mPythonUtils && mPythonUtils->isEnabled()) { - + // check for python plugins system-wide QStringList pluginList = mPythonUtils->pluginList(); std::cout << "Loading python plugins" << std::endl; @@ -1690,11 +1703,11 @@ void QgisApp::restoreSessionPlugins(QString thePluginDirString) for (int i = 0; i < pluginList.size(); i++) { QString packageName = pluginList[i]; - + // import plugin's package if (!mPythonUtils->loadPlugin(packageName)) continue; - + // get information from the plugin // if there are some problems, don't continue with metadata retreival pluginName = mPythonUtils->getPluginMetadata(packageName, "name"); @@ -1704,13 +1717,13 @@ void QgisApp::restoreSessionPlugins(QString thePluginDirString) if (description != "__error__") version = mPythonUtils->getPluginMetadata(packageName, "version"); } - + if (pluginName == "__error__" || description == "__error__" || version == "__error__") { QMessageBox::warning(this, tr("Python error"), tr("Error when reading metadata of plugin ") + packageName); continue; } - + if (mySettings.value("/PythonPlugins/" + packageName).toBool()) { loadPythonPlugin(packageName, pluginName); @@ -1902,7 +1915,7 @@ static void buildSupportedVectorFileFilter_(QString & fileFilters) with the current filter name. */ -static void openFilesRememberingFilter_(QString const &filterName, +static void openFilesRememberingFilter_(QString const &filterName, QString const &filters, QStringList & selectedFiles, QString& enc, QString &title) { @@ -1983,7 +1996,7 @@ void QgisApp::addVectorLayer() addVectorLayers(selectedFiles, enc); } - + bool QgisApp::addVectorLayers(QStringList const & theLayerQStringList, const QString& enc) { @@ -2040,7 +2053,7 @@ bool QgisApp::addVectorLayers(QStringList const & theLayerQStringList, const QSt // update UI qApp->processEvents(); - + // draw the map mMapCanvas->freeze(false); mMapCanvas->refresh(); @@ -2079,7 +2092,6 @@ void QgisApp::addDatabaseLayer() } // only supports postgis layers at present - // show the postgis dialog QgsDbSourceSelect *dbs = new QgsDbSourceSelect(this); @@ -2133,10 +2145,10 @@ void QgisApp::addDatabaseLayer() } delete dbs; - + // update UI qApp->processEvents(); - + // draw the map mMapCanvas->freeze(false); mMapCanvas->refresh(); @@ -2372,8 +2384,8 @@ findMissingFile_( QString const & fileFilters, QDomNode & layerNode ) QString title( QObject::tr("Where is '") + originalDataSource.fileName() + "'? (" + QObject::tr("original location: ") + originalDataSource.absoluteFilePath() + ")"); openFilesRememberingFilter_(memoryQualifier, - myFileFilters, - selectedFiles, + myFileFilters, + selectedFiles, enc, title); @@ -2409,7 +2421,7 @@ findMissingFile_( QString const & fileFilters, QDomNode & layerNode ) */ static void -findLayer_( QString const & fileFilters, QDomNode const & constLayerNode ) +findLayer_( QString const & fileFilters, QDomNode const & constLayerNode ) { // XXX actually we could possibly get away with a copy of the node QDomNode & layerNode = const_cast(constLayerNode); @@ -2463,10 +2475,10 @@ findLayers_( QString const & fileFilters, list const & layerNodes ) void QgisApp::fileExit() { if(mMapCanvas && mMapCanvas->isDrawing()) - { - return; - } - + { + return; + } + if (saveDirty()) { removeAllLayers(); @@ -2484,11 +2496,11 @@ void QgisApp::fileNew() //as file new but accepts flags to indicate whether we should prompt to save void QgisApp::fileNew(bool thePromptToSaveFlag) -{ +{ if(mMapCanvas && mMapCanvas->isDrawing()) - { - return; - } + { + return; + } if (thePromptToSaveFlag) { @@ -2497,9 +2509,9 @@ void QgisApp::fileNew(bool thePromptToSaveFlag) return; } } - + //QgsDebugMsg("erasing project"); - + mMapCanvas->freeze(true); QgsMapLayerRegistry::instance()->removeAllMapLayers(); mMapCanvas->clear(); @@ -2508,35 +2520,35 @@ void QgisApp::fileNew(bool thePromptToSaveFlag) prj->title( QString::null ); prj->filename( QString::null ); prj->clearProperties(); // why carry over properties from previous projects? - + QSettings settings; - + //set the colour for selections - //the default can be set in qgisoptions + //the default can be set in qgisoptions //use project properties to override the colour on a per project basis int myRed = settings.value("/qgis/default_selection_color_red",255).toInt(); int myGreen = settings.value("/qgis/default_selection_color_green",255).toInt(); int myBlue = settings.value("/qgis/default_selection_color_blue",0).toInt(); prj->writeEntry("Gui","/SelectionColorRedPart",myRed); prj->writeEntry("Gui","/SelectionColorGreenPart",myGreen); - prj->writeEntry("Gui","/SelectionColorBluePart",myBlue); + prj->writeEntry("Gui","/SelectionColorBluePart",myBlue); QgsRenderer::setSelectionColor(QColor(myRed,myGreen,myBlue)); //set the canvas to the default background colour - //the default can be set in qgisoptions + //the default can be set in qgisoptions //use project properties to override the colour on a per project basis myRed = settings.value("/qgis/default_canvas_color_red",255).toInt(); myGreen = settings.value("/qgis/default_canvas_color_green",255).toInt(); myBlue = settings.value("/qgis/default_canvas_color_blue",255).toInt(); prj->writeEntry("Gui","/CanvasColorRedPart",myRed); prj->writeEntry("Gui","/CanvasColorGreenPart",myGreen); - prj->writeEntry("Gui","/CanvasColorBluePart",myBlue); + prj->writeEntry("Gui","/CanvasColorBluePart",myBlue); mMapCanvas->setCanvasColor(QColor(myRed,myGreen,myBlue)); - + prj->dirty(false); setTitleBarText_( *this ); - + //QgsDebugMsg("emiting new project signal"); //emit signal so QgsComposer knows we have a new project @@ -2544,9 +2556,9 @@ void QgisApp::fileNew(bool thePromptToSaveFlag) mMapCanvas->freeze(false); mMapCanvas->refresh(); - + mMapCanvas->mapRenderer()->setProjectionsEnabled(FALSE); - + // set the initial map tool mMapCanvas->setMapTool(mMapTools.mPan); mNonEditMapTool = mMapTools.mPan; // signals are not yet setup to catch this @@ -2599,8 +2611,8 @@ void QgisApp::newVectorLayer() // allow for selection of more than one file openFileDialog->setFileMode(QFileDialog::AnyFile); - openFileDialog->setAcceptMode(QFileDialog::AcceptSave); - openFileDialog->setConfirmOverwrite( true ); + openFileDialog->setAcceptMode(QFileDialog::AcceptSave); + openFileDialog->setConfirmOverwrite( true ); if (haveLastUsedFilter) // set the filter to the last one used { @@ -2712,7 +2724,7 @@ void QgisApp::fileOpen() // Persist last used project dir settings.setValue("/UI/lastProjectDir", myPath); } - else + else { // if they didn't select anything, just return delete openFileDialog; @@ -2727,7 +2739,7 @@ void QgisApp::fileOpen() QgsProject::instance()->filename( fullPath ); - try + try { if ( QgsProject::instance()->read() ) { @@ -2742,8 +2754,8 @@ void QgisApp::fileOpen() } catch ( QgsProjectBadLayerException & e ) { - QMessageBox::critical(this, - tr("QGIS Project Read Error"), + QMessageBox::critical(this, + tr("QGIS Project Read Error"), tr("") + "\n" + QString::fromLocal8Bit( e.what() ) ); QgsDebugMsg( QString("%1 bad layers found").arg(e.layers().size()) ); @@ -2753,8 +2765,8 @@ void QgisApp::fileOpen() } catch ( std::exception & e ) { - QMessageBox::critical(this, - tr("QGIS Project Read Error"), + QMessageBox::critical(this, + tr("QGIS Project Read Error"), tr("") + "\n" + QString::fromLocal8Bit( e.what() ) ); QgsDebugMsg("BAD LAYERS FOUND"); } @@ -2809,8 +2821,8 @@ bool QgisApp::addProject(QString projectFile) { QgsDebugMsg( QString("%1 bad layers found").arg( e.layers().size() ) ); - if ( QMessageBox::Ok == QMessageBox::critical( this, - tr("QGIS Project Read Error"), + if ( QMessageBox::Ok == QMessageBox::critical( this, + tr("QGIS Project Read Error"), tr("") + "\n" + QString::fromLocal8Bit( e.what() ) + "\n" + tr("Try to find missing layers?"), QMessageBox::Ok | QMessageBox::Cancel ) ) @@ -2827,7 +2839,7 @@ bool QgisApp::addProject(QString projectFile) { QgsDebugMsg("BAD LAYERS FOUND"); - QMessageBox::critical( this, + QMessageBox::critical( this, tr("Unable to open project"), QString::fromLocal8Bit( e.what() ) ); mMapCanvas->freeze(false); @@ -2871,14 +2883,14 @@ bool QgisApp::fileSave() lastUsedDir, QObject::tr("QGis files (*.qgs)")) ); saveFileDialog->setFileMode(QFileDialog::AnyFile); - saveFileDialog->setAcceptMode(QFileDialog::AcceptSave); - saveFileDialog->setConfirmOverwrite( true ); + saveFileDialog->setAcceptMode(QFileDialog::AcceptSave); + saveFileDialog->setConfirmOverwrite( true ); if (saveFileDialog->exec() == QDialog::Accepted) { fullPath.setFile( saveFileDialog->selectedFiles().first() ); } - else + else { // if they didn't select anything, just return // delete saveFileDialog; auto_ptr auto destroys @@ -2962,7 +2974,7 @@ void QgisApp::fileSaveAs() QString myPath = fullPath.path(); // Persist last used project dir settings.setValue("/UI/lastProjectDir", myPath); - } + } else { // if they didn't select anything, just return @@ -3055,8 +3067,8 @@ void QgisApp::openProject(const QString & fileName) catch ( QgsIOException & io_exception ) { Q_UNUSED(io_exception); - QMessageBox::critical( this, - tr("QGIS: Unable to load project"), + QMessageBox::critical( this, + tr("QGIS: Unable to load project"), tr("Unable to load project ") + fileName ); } } @@ -3075,7 +3087,7 @@ bool QgisApp::openLayer(const QString & fileName) // try to load it as raster QgsMapLayer* ok = NULL; - CPLPushErrorHandler(CPLQuietErrorHandler); + CPLPushErrorHandler(CPLQuietErrorHandler); if (QgsRasterLayer::isValidRasterFileName(fileName)) ok = addRasterLayer(fileName, false); else // nope - try to load it as a shape/ogr @@ -3252,10 +3264,10 @@ void QgisApp::saveMapAsImage(QString theImageFileNameQString, QPixmap * theQPixm void QgisApp::addAllToOverview() { if(mMapLegend) - { - mMapLegend->setOverviewAllLayers(true); - } - + { + mMapLegend->setOverviewAllLayers(true); + } + // notify the project we've made a change QgsProject::instance()->dirty(true); } @@ -3264,10 +3276,10 @@ void QgisApp::addAllToOverview() void QgisApp::removeAllFromOverview() { if(mMapLegend) - { - mMapLegend->setOverviewAllLayers(false); - } - + { + mMapLegend->setOverviewAllLayers(false); + } + // notify the project we've made a change QgsProject::instance()->dirty(true); } @@ -3328,7 +3340,7 @@ void QgisApp::showAllLayers() void QgisApp::zoomIn() { QgsDebugMsg ("Setting map tool to zoomIn"); - + mMapCanvas->setMapTool(mMapTools.mZoomIn); // notify the project we've made a change @@ -3388,13 +3400,15 @@ void QgisApp::measureArea() mMapCanvas->setMapTool(mMapTools.mMeasureArea); } - - void QgisApp::attributeTable() { - mMapLegend->legendLayerAttributeTable(); -} + if(mMapCanvas && mMapCanvas->isDrawing()) + { + return; + } + QgsAttributeTableDisplay::attributeTable( dynamic_cast(mMapLegend->currentLayer()) ); +} void QgisApp::deleteSelected() { @@ -3405,7 +3419,7 @@ void QgisApp::deleteSelected() tr("To delete features, you must select a vector layer in the legend")); return; } - + QgsVectorLayer* vlayer = dynamic_cast(layer); if(!vlayer) { @@ -3413,22 +3427,21 @@ void QgisApp::deleteSelected() tr("Deleting features only works on vector layers")); return; } - + if(!(vlayer->getDataProvider()->capabilities() & QgsVectorDataProvider::DeleteFeatures)) { - QMessageBox::information(this, tr("Provider does not support deletion"), + QMessageBox::information(this, tr("Provider does not support deletion"), tr("Data provider does not support deleting features")); return; } if(!vlayer->isEditable()) { - QMessageBox::information(this, tr("Layer not editable"), + QMessageBox::information(this, tr("Layer not editable"), tr("The current layer is not editable. Choose 'Start editing' in the digitizing toolbar.")); return; } - if(!vlayer->deleteSelectedFeatures()) { QMessageBox::information(this, tr("Problem deleting features"), @@ -3463,19 +3476,19 @@ void QgisApp::capturePoint() void QgisApp::captureLine() { if(mMapCanvas && mMapCanvas->isDrawing()) - { - return; - } - + { + return; + } + mMapCanvas->setMapTool(mMapTools.mCaptureLine); } void QgisApp::capturePolygon() { if(mMapCanvas && mMapCanvas->isDrawing()) - { - return; - } + { + return; + } mMapCanvas->setMapTool(mMapTools.mCapturePolygon); } @@ -3488,11 +3501,11 @@ void QgisApp::select() void QgisApp::addVertex() { if(mMapCanvas && mMapCanvas->isDrawing()) - { - return; - } + { + return; + } mMapCanvas->setMapTool(mMapTools.mVertexAdd); - + } void QgisApp::moveVertex() @@ -3507,9 +3520,9 @@ void QgisApp::moveVertex() void QgisApp::addRing() { if(mMapCanvas && mMapCanvas->isDrawing()) - { - return; - } + { + return; + } mMapCanvas->setMapTool(mMapTools.mAddRing); } @@ -3636,27 +3649,85 @@ void QgisApp::toggleMapTips() void QgisApp::toggleEditing() { - if(mMapCanvas && mMapCanvas->isDrawing()) - { - mActionToggleEditing->setChecked(!mActionToggleEditing->isChecked()); + if( mMapCanvas && mMapCanvas->isDrawing() ) return; - } QgsLegendLayerFile* currentLayerFile = mMapLegend->currentLayerFile(); if(currentLayerFile) - { - currentLayerFile->toggleEditing(); - activateDeactivateLayerRelatedActions( mMapLegend->currentLayer() ); - } + { + toggleEditing( mMapLegend->currentLayer() ); + } else + { + mActionToggleEditing->setChecked(false); + } +} + +void QgisApp::toggleEditing(QgsMapLayer *layer) +{ + if( !layer ) + return; + + QgsVectorLayer *vlayer = dynamic_cast(layer); + if (!vlayer) + return; + + if (!vlayer->isEditable()) + { + vlayer->startEditing(); + if(!(vlayer->getDataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures)) { - mActionToggleEditing->setChecked(false); + QMessageBox::information(0,tr("Start editing failed"), tr("Provider cannot be opened for editing")); } + } + else if(vlayer->isModified()) + { + // commit or roll back? + QMessageBox::StandardButton commit = + QMessageBox::information(this, + tr("Stop editing"), + tr("Do you want to save the changes to layer %1?").arg( vlayer->name() ), + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + + if(commit==QMessageBox::Save) + { + if(!vlayer->commitChanges()) + { + QMessageBox::information(this, + tr("Error"), + tr("Could not commit changes to layer %1\n\nErrors: %2\n") + .arg( vlayer->name() ) + .arg( vlayer->commitErrors().join("\n ") ) ); + // Leave the in-memory editing state alone, + // to give the user a chance to enter different values + // and try the commit again later + } + } + else if(commit==QMessageBox::Discard) + { + if(!vlayer->rollBack()) + { + QMessageBox::information(0,tr("Error"), tr("Problems during roll back")); + } + } + else //cancel + { + return; + } + } + else //layer not modified + { + vlayer->rollBack(); + } + + if( layer==mMapLegend->currentLayer() ) + activateDeactivateLayerRelatedActions( layer ); + + vlayer->triggerRepaint(); } void QgisApp::showMouseCoordinate(QgsPoint & p) { - if ( mMapTipsVisible ) { // store the point, we need it for when the maptips timer fires @@ -3780,7 +3851,7 @@ void QgisApp::removeLayer() QgsLegendLayerFile* currentLayerFile = mMapLegend->currentLayerFile(); if(currentLayerFile && currentLayerFile->isEditing() ) { - currentLayerFile->toggleEditing(); + toggleEditing( dynamic_cast(currentLayerFile->layer()) ); } mMapLegend->legendLayerRemove(); // notify the project we've made a change @@ -3805,7 +3876,7 @@ void QgisApp::zoomToLayerExtent() void QgisApp::showPluginManager() { QgsPluginManager *pm = new QgsPluginManager(mPythonUtils, this); - pm->resizeColumnsToContents(); + pm->resizeColumnsToContents(); if (pm->exec()) { // load selected plugins @@ -3881,25 +3952,25 @@ void QgisApp::loadPythonPlugin(QString packageName, QString pluginName) QgsDebugMsg("Python is not enabled in QGIS."); return; } - - + + QgsPluginRegistry *pRegistry = QgsPluginRegistry::instance(); - + // is loaded already? if (pRegistry->library(pluginName).isEmpty()) { mPythonUtils->loadPlugin(packageName); mPythonUtils->startPlugin(packageName); - + // TODO: test success - + // add to plugin registry pRegistry->addPythonPlugin(packageName, pluginName); - + // add to settings QSettings settings; settings.setValue("/PythonPlugins/" + packageName, true); - std::cout << "Loaded : " << pluginName.toLocal8Bit().constData() << " (package: " + std::cout << "Loaded : " << pluginName.toLocal8Bit().constData() << " (package: " << packageName.toLocal8Bit().constData() << ")" << std::endl; } } @@ -4021,7 +4092,7 @@ void QgisApp::checkQgisVersion() connect(mSocket, SIGNAL(connected()), SLOT(socketConnected())); connect(mSocket, SIGNAL(connectionClosed()), SLOT(socketConnectionClosed())); connect(mSocket, SIGNAL(readyRead()), SLOT(socketReadyRead())); - connect(mSocket, SIGNAL(error(QAbstractSocket::SocketError)), + connect(mSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError))); mSocket->connectToHost("mrcc.com", 80); } @@ -4151,7 +4222,7 @@ void QgisApp::options() QSettings mySettings; mMapCanvas->enableAntiAliasing(mySettings.value("/qgis/enable_anti_aliasing").toBool()); mMapCanvas->useQImageToRender(mySettings.value("/qgis/use_qimage_to_render").toBool()); - + int action = mySettings.value("/qgis/wheel_action", 0).toInt(); double zoomFactor = mySettings.value("/qgis/zoom_factor", 2).toDouble(); mMapCanvas->setWheelAction((QgsMapCanvas::WheelAction) action, zoomFactor); @@ -4250,22 +4321,22 @@ QgsVectorLayer* QgisApp::addVectorLayer(QString vectorLayerPath, QString baseNam { QMessageBox::critical(this,tr("Layer is not valid"), tr("The layer is not a valid layer and can not be added to the map")); - + delete layer; mMapCanvas->freeze(false); return NULL; } - + // update UI qApp->processEvents(); - + // draw the map mMapCanvas->freeze(false); mMapCanvas->refresh(); // Let render() do its own cursor management // QApplication::restoreOverrideCursor(); - + return layer; } // QgisApp::addVectorLayer @@ -4296,10 +4367,10 @@ void QgisApp::addMapLayer(QgsMapLayer *theMapLayer) QMessageBox::critical(this,tr("Layer is not valid"), tr("The layer is not a valid layer and can not be added to the map")); } - + // update UI qApp->processEvents(); - + // draw the map mMapCanvas->freeze(false); mMapCanvas->refresh(); @@ -4397,7 +4468,7 @@ QMenu* QgisApp::getPluginMenu(QString menuName) before = actions.at(i); } - // It doesn't exist, so create + // It doesn't exist, so create QMenu* menu = new QMenu(menuName, this); // Where to put it? - we worked that out above... mPluginMenu->insertMenu(before, menu); @@ -4616,7 +4687,7 @@ void QgisApp::projectProperties() qApp->processEvents(); // Be told if the mouse display precision may have changed by the user // changing things in the project properties dialog box - connect(pp, SIGNAL(displayPrecisionChanged()), this, + connect(pp, SIGNAL(displayPrecisionChanged()), this, SLOT(updateMouseCoordinatePrecision())); QApplication::restoreOverrideCursor(); @@ -4632,7 +4703,7 @@ void QgisApp::projectProperties() long newSRSID = myRender->destinationSrs().srsid(); bool isProjected = myRender->projectionsEnabled(); - + // projections have been turned on/off or dest SRS has changed while projections are on if (wasProjected != isProjected || (isProjected && oldSRSID != newSRSID)) { @@ -4645,10 +4716,10 @@ void QgisApp::projectProperties() int myBlueInt = QgsProject::instance()->readNumEntry("Gui","/CanvasColorBluePart",255); QColor myColor = QColor(myRedInt,myGreenInt,myBlueInt); mMapCanvas->setCanvasColor(myColor); // this is fill colour before rendering onto canvas - + // Set the window title. setTitleBarText_( *this ); - + // delete the property sheet object delete pp; } // QgisApp::projectProperties @@ -4800,7 +4871,7 @@ void QgisApp::activateDeactivateLayerRelatedActions(QgsMapLayer* layer) if(vlayer->vectorType() == QGis::Polygon) { mActionAddRing->setEnabled(true); - //some polygon layers contain also multipolygon features. + //some polygon layers contain also multipolygon features. //Therefore, the test for multipolygon is done in QgsGeometry mActionAddIsland->setEnabled(true); } @@ -4875,7 +4946,7 @@ void QgisApp::addRasterLayer() { return; } - + QString fileFilters; QStringList selectedFiles; @@ -4921,7 +4992,7 @@ bool QgisApp::addRasterLayer(QgsRasterLayer * theRasterLayer) delete theRasterLayer; return false; } - + // register this layer with the central layers registry QgsMapLayerRegistry::instance()->addMapLayer(theRasterLayer); @@ -4955,7 +5026,7 @@ QgsRasterLayer* QgisApp::addRasterLayer(QString const & rasterFile, QString cons QApplication::setOverrideCursor(Qt::WaitCursor); mMapCanvas->freeze(true); - + // XXX ya know QgsRasterLayer can snip out the basename on its own; // XXX why do we have to pass it in for it? QgsRasterLayer *layer = @@ -5048,7 +5119,7 @@ QgsRasterLayer* QgisApp::addRasterLayer(QString const & rasterLayerPath, if( layer && layer->isValid() ) { addRasterLayer(layer); - + statusBar()->showMessage(mMapCanvas->extent().stringRep(2)); } @@ -5137,7 +5208,7 @@ bool QgisApp::addRasterLayers(QStringList const &theFileNameQStringList, bool gu if(guiWarning) { QString msg(*myIterator + tr(" is not a supported raster data source")); - + if( errMsg.size() > 0 ) msg += "\n" + errMsg; @@ -5176,7 +5247,7 @@ bool QgisApp::addRasterLayers(QStringList const &theFileNameQStringList, bool gu void QgisApp::keyPressEvent ( QKeyEvent * e ) { - // The following statment causes a crash on WIN32 and should be + // The following statement causes a crash on WIN32 and should be // enclosed in an #ifdef QGISDEBUG if its really necessary. Its // commented out for now. [gsherman] // std::cout << e->text().toLocal8Bit().data() << " (keypress recevied)" << std::endl; @@ -5192,14 +5263,14 @@ void QgisApp::keyPressEvent ( QKeyEvent * e ) e->ignore(); } } - + // Debug hook - used to output diagnostic messages when evoked (usually from the menu) /* Temporarily disabled... void QgisApp::debugHook() { - std::cout << "Hello from debug hook" << std::endl; + std::cout << "Hello from debug hook" << std::endl; // show the map canvas extent -std::cout << mMapCanvas->extent() << std::endl; +std::cout << mMapCanvas->extent() << std::endl; } */ void QgisApp::customProjection() @@ -5236,7 +5307,7 @@ void QgisApp::newBookmark() // the mapcanvas bool ok; - QString bookmarkName = QInputDialog::getText(this, tr("New Bookmark"), + QString bookmarkName = QInputDialog::getText(this, tr("New Bookmark"), tr("Enter a name for the new bookmark:"), QLineEdit::Normal, QString::null, &ok); if( ok && !bookmarkName.isEmpty()) @@ -5244,7 +5315,7 @@ void QgisApp::newBookmark() if (createDB()) { // create the bookmark - QgsBookmarkItem *bmi = new QgsBookmarkItem(bookmarkName, + QgsBookmarkItem *bmi = new QgsBookmarkItem(bookmarkName, QgsProject::instance()->title(), mMapCanvas->extent(), -1, QgsApplication::qgisUserDbFilePath()); bmi->store(); @@ -5257,7 +5328,7 @@ void QgisApp::newBookmark() QMessageBox::warning(this,tr("Error"), tr("Unable to create the bookmark. Your user database may be missing or corrupted")); } } -} +} void QgisApp::showAllToolbars() { @@ -5309,8 +5380,8 @@ void QgisApp::warnOlderProjectVersion(QString oldVersion) .arg(tr("Settings:Options:General", "Menu path to setting options")) .arg(tr("Warn me when opening a project file saved with an older version of QGIS")) ); - - } + + } return; } diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index f3a70b15b40..6b17bb54514 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -163,6 +163,9 @@ class QgisApp : public QMainWindow * (as documented in Qt documentation. */ void setupProxy(); + + static QgisApp *instance() { return smInstance; } + //! Helper to get a theme icon. It will fall back to the //default theme if the active theme does not have the required //icon. @@ -320,6 +323,7 @@ public slots: void refreshMapCanvas(); //! returns pointer to map legend QgsLegend *legend() { return mMapLegend; } + //! starts/stops editing mode of the current layer void toggleEditing(); @@ -330,7 +334,6 @@ public slots: Is called from the legend when the current legend item has changed*/ void activateDeactivateLayerRelatedActions(QgsMapLayer* layer); - public slots: /** Add a dock widget to the main window. Overloaded from QMainWindow. * After adding the dock widget to the ui (by delegating to the QMainWindow @@ -368,9 +371,13 @@ public slots: void measure(); //! Measure area void measureArea(); + //! show the attribute table for the currently selected layer void attributeTable(); - + + //! starts/stops editing mode of a layer + void toggleEditing(QgsMapLayer *layer); + //! show python console void showPythonDialog(); @@ -644,8 +651,6 @@ class Tools QString mStartupPath; //! full path name of the current map file (if it has been saved or loaded) QString mFullPathName; - //! A dock to show the attribute table (user optional) - QPointer mpTableDockWidget; //! interface to QgisApp for plugins QgisAppInterface *mQgisInterface; @@ -697,6 +702,8 @@ class Tools bool mFullScreenMode; QgsPythonDialog* mPythonConsole; QgsPythonUtils* mPythonUtils; + + static QgisApp *smInstance; }; #endif diff --git a/src/app/qgsaddattrdialog.cpp b/src/app/qgsaddattrdialog.cpp index 9cecf261e8d..89c130c007d 100644 --- a/src/app/qgsaddattrdialog.cpp +++ b/src/app/qgsaddattrdialog.cpp @@ -21,34 +21,34 @@ QgsAddAttrDialog::QgsAddAttrDialog(QgsVectorDataProvider* provider, QWidget *parent, Qt::WFlags fl) : QDialog(parent, fl), mDataProvider(provider) { - setupUi(this); + setupUi(this); - //fill data types into the combo box - const QSet& typelist=mDataProvider->supportedNativeTypes(); + //fill data types into the combo box + const QgsNativeTypeMap &typelist=mDataProvider->supportedNativeTypes(); - for(QSet::const_iterator it = typelist.constBegin(); it != typelist.constEnd(); ++it) - { - mTypeBox->addItem(*it); - } + for(QgsNativeTypeMap::const_iterator it = typelist.constBegin(); it != typelist.constEnd(); ++it) + { + mTypeBox->addItem(it.key()); + } } QgsAddAttrDialog::QgsAddAttrDialog(const std::list& typelist, QWidget *parent, Qt::WFlags fl) : QDialog(parent, fl), mDataProvider(0) { - setupUi(this); + setupUi(this); - for(std::list::const_iterator iter=typelist.begin();iter!=typelist.end();++iter) - { - mTypeBox->addItem(*iter); - } + for(std::list::const_iterator iter=typelist.begin();iter!=typelist.end();++iter) + { + mTypeBox->addItem(*iter); + } } QString QgsAddAttrDialog::name() const { - return mNameEdit->text(); + return mNameEdit->text(); } QString QgsAddAttrDialog::type() const { - return mTypeBox->currentText(); + return mTypeBox->currentText(); } diff --git a/src/app/qgsattributeactiondialog.h b/src/app/qgsattributeactiondialog.h index e2cd6e33ded..d6a1a6de06b 100644 --- a/src/app/qgsattributeactiondialog.h +++ b/src/app/qgsattributeactiondialog.h @@ -30,8 +30,6 @@ back to QgsVectorLayer. class QgsAttributeAction; -typedef QMap QgsFieldMap; - class QgsAttributeActionDialog: public QWidget, private Ui::QgsAttributeActionDialogBase { Q_OBJECT; diff --git a/src/app/qgsattributedialog.cpp b/src/app/qgsattributedialog.cpp index 0dc69aadc6e..a66f7c3a05b 100644 --- a/src/app/qgsattributedialog.cpp +++ b/src/app/qgsattributedialog.cpp @@ -31,8 +31,11 @@ #include #include #include +#include +#include +#include -QgsAttributeDialog::QgsAttributeDialog(QgsVectorLayer *vl, QgsFeature * thepFeature) +QgsAttributeDialog::QgsAttributeDialog(QgsVectorLayer *vl, QgsFeature *thepFeature) : QDialog(), mSettingsPath("/Windows/AttributeDialog/"), mpFeature(thepFeature), @@ -67,7 +70,7 @@ QgsAttributeDialog::QgsAttributeDialog(QgsVectorLayer *vl, QgsFeature * thepFeat int classificationField = -1; - QStringList values; + QMap classes; const QgsUniqueValueRenderer *uvr = dynamic_cast( mLayer->renderer() ); if( uvr ) @@ -78,7 +81,13 @@ QgsAttributeDialog::QgsAttributeDialog(QgsVectorLayer *vl, QgsFeature * thepFeat for(int i=0; ilowerValue() ); + QString label = symbols[i]->label(); + QString name = symbols[i]->lowerValue(); + + if(label=="") + label=name; + + classes.insert(name, label); } } @@ -87,49 +96,135 @@ QgsAttributeDialog::QgsAttributeDialog(QgsVectorLayer *vl, QgsFeature * thepFeat it != myAttributes.end(); ++it) { - QString myFieldName = theFieldMap[it.key()].name(); - int myFieldType = theFieldMap[it.key()].type(); + const QgsField &field = theFieldMap[it.key()]; + QString myFieldName = field.name(); + int myFieldType = field.type(); QLabel * mypLabel = new QLabel(); mypInnerLayout->addWidget(mypLabel,index,0); QVariant myFieldValue = it.value(); - + QWidget *myWidget; - if(classificationField!=it.key()) + + QgsVectorLayer::EditType editType = vl->editType( it.key() ); + + switch( editType ) { - QLineEdit *le = new QLineEdit(); - - //the provider may have provided a default value so use it - le->setText(myFieldValue.toString()); - - if( myFieldType==QVariant::Int ) + case QgsVectorLayer::Range: { - le->setValidator( new QIntValidator(le) ); + if( myFieldType==QVariant::Int ) + { + int min = vl->range( it.key() ).mMin.toInt(); + int max = vl->range( it.key() ).mMax.toInt(); + int step = vl->range( it.key() ).mStep.toInt(); + + QSpinBox *sb = new QSpinBox(); + sb->setMinimum(min); + sb->setMaximum(max); + sb->setSingleStep(step); + sb->setValue( it.value().toInt() ); + + myWidget = sb; + break; + } else if( myFieldType==QVariant::Double ) { + double min = vl->range( it.key() ).mMin.toDouble(); + double max = vl->range( it.key() ).mMax.toDouble(); + double step = vl->range( it.key() ).mStep.toDouble(); + QDoubleSpinBox *dsb = new QDoubleSpinBox(); + + dsb->setMinimum(min); + dsb->setMaximum(max); + dsb->setSingleStep(step); + dsb->setValue( it.value().toDouble() ); + + myWidget = dsb; + + break; + } + } - else if( myFieldType==QVariant::Double ) + + // fall-through + + + case QgsVectorLayer::LineEdit: + case QgsVectorLayer::UniqueValuesEditable: { - le->setValidator( new QIntValidator(le) ); + QLineEdit *le = new QLineEdit( myFieldValue.toString() ); + + if( editType == QgsVectorLayer::UniqueValuesEditable ) + { + QStringList values; + mLayer->getDataProvider()->getUniqueValues(it.key(), values); + + QCompleter *c = new QCompleter(values); + c->setCompletionMode(QCompleter::PopupCompletion); + le->setCompleter(c); + } + + if( myFieldType==QVariant::Int ) + { + le->setValidator( new QIntValidator(le) ); + } + else if( myFieldType==QVariant::Double ) + { + le->setValidator( new QIntValidator(le) ); + } + + myWidget = le; } + break; - myWidget = le; - } - else - { - QComboBox *cb = new QComboBox(); - cb->addItems(values); - cb->setEditable(true); - - //the provider may have provided a default value so use it - cb->setEditText(myFieldValue.toString()); - - if( myFieldType==QVariant::Int ) { - cb->setValidator( new QIntValidator(cb) ); - } - else if( myFieldType==QVariant::Double ) + case QgsVectorLayer::UniqueValues: { - cb->setValidator( new QIntValidator(cb) ); - } + QStringList values; + mLayer->getDataProvider()->getUniqueValues(it.key(), values); - myWidget = cb; + QComboBox *cb = new QComboBox(); + cb->setEditable(true); + cb->addItems(values); + + int idx = cb->findText( myFieldValue.toString() ); + if( idx>= 0 ) + cb->setCurrentIndex( idx ); + + myWidget = cb; + } + break; + + case QgsVectorLayer::ValueMap: + { + const QMap &map = vl->valueMap( it.key() ); + + QComboBox *cb = new QComboBox(); + + for(QMap::const_iterator it=map.begin(); it!=map.end(); it++) + { + cb->addItem( it.key(), it.value() ); + } + + int idx = cb->findData( myFieldValue ); + if( idx>= 0 ) + cb->setCurrentIndex( idx ); + + myWidget = cb; + } + break; + + case QgsVectorLayer::Classification: + { + QComboBox *cb = new QComboBox(); + for(QMap::const_iterator it=classes.begin(); it!=classes.end(); it++) + { + cb->addItem( it.value(), it.key() ); + } + + int idx = cb->findData( myFieldValue ); + if( idx>=0 ) + cb->setCurrentIndex( idx ); + + myWidget = cb; + } + break; } if( myFieldType==QVariant::Int ) @@ -168,10 +263,9 @@ void QgsAttributeDialog::accept() it != myAttributes.end(); ++it) { - const QgsFieldMap &theFieldMap = mLayer->getDataProvider()->fields(); - - //Q_ASSERT(myIndex <= mpWidgets.size()); - QString myFieldName = theFieldMap[it.key()].name(); + const QgsField &theField = mLayer->pendingFields()[it.key()]; + QgsVectorLayer::EditType editType = mLayer->editType( it.key() ); + QString myFieldName = theField.name(); bool myFlag=false; QString myFieldValue; @@ -184,40 +278,62 @@ void QgsAttributeDialog::accept() QComboBox *cb = dynamic_cast(mpWidgets.value(myIndex)); if(cb) { - myFieldValue = cb->currentText(); + if( editType==QgsVectorLayer::UniqueValues || + editType==QgsVectorLayer::ValueMap || + editType==QgsVectorLayer::Classification) + { + myFieldValue = cb->itemData( cb->currentIndex() ).toString(); + } + else + { + myFieldValue = cb->currentText(); + } } - switch( theFieldMap[it.key()].type() ) + + QSpinBox *sb = dynamic_cast(mpWidgets.value(myIndex)); + if(sb) { - case QVariant::Int: + myFieldValue = QString::number(sb->value()); + } + + QDoubleSpinBox *dsb = dynamic_cast(mpWidgets.value(myIndex)); + if(dsb) + { + myFieldValue = QString::number(dsb->value()); + } + + switch( theField.type() ) + { + case QVariant::Int: + { + int myIntValue = myFieldValue.toInt(&myFlag); + if (myFlag && ! myFieldValue.isEmpty()) { - int myIntValue = myFieldValue.toInt(&myFlag); - if (myFlag && ! myFieldValue.isEmpty()) - { - mpFeature->changeAttribute( it.key(), QVariant(myIntValue) ); - } - else - { - mpFeature->changeAttribute( it.key(), QVariant(QString::null) ); - } + mpFeature->changeAttribute( it.key(), QVariant(myIntValue) ); } - break; - case QVariant::Double: + else { - double myDblValue = myFieldValue.toDouble(&myFlag); - if (myFlag && ! myFieldValue.isEmpty()) - { - mpFeature->changeAttribute( it.key(), QVariant(myDblValue) ); - } - else - { - mpFeature->changeAttribute( it.key(), QVariant(QString::null) ); - } + mpFeature->changeAttribute( it.key(), QVariant(QString::null) ); } - break; - default: //string - mpFeature->changeAttribute(it.key(),QVariant( myFieldValue ) ); - break; + } + break; + case QVariant::Double: + { + double myDblValue = myFieldValue.toDouble(&myFlag); + if (myFlag && ! myFieldValue.isEmpty()) + { + mpFeature->changeAttribute( it.key(), QVariant(myDblValue) ); + } + else + { + mpFeature->changeAttribute( it.key(), QVariant(QString::null) ); + } + } + break; + default: //string + mpFeature->changeAttribute(it.key(),QVariant( myFieldValue ) ); + break; } ++myIndex; } @@ -235,4 +351,3 @@ void QgsAttributeDialog::restoreGeometry() QSettings settings; QDialog::restoreGeometry(settings.value(mSettingsPath+"geometry").toByteArray()); } - diff --git a/src/app/qgsattributetable.cpp b/src/app/qgsattributetable.cpp index a95a7c3e6fd..8ea59e32c88 100644 --- a/src/app/qgsattributetable.cpp +++ b/src/app/qgsattributetable.cpp @@ -20,7 +20,6 @@ #include #include "qgsattributetable.h" -#include "qgsfeature.h" #include "qgsfield.h" #include "qgslogger.h" #include "qgsvectordataprovider.h" @@ -33,31 +32,44 @@ #include -QgsAttributeTableItemDelegate::QgsAttributeTableItemDelegate(const QgsFieldMap & fields, QObject *parent) - : QItemDelegate(parent), mFields(fields) -{} +QgsAttributeTableItemDelegate::QgsAttributeTableItemDelegate(QgsAttributeTable *table, QObject *parent) + : mTable(table), QItemDelegate(parent) +{ +} -QWidget * QgsAttributeTableItemDelegate::createEditor( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const +QWidget *QgsAttributeTableItemDelegate::createEditor( + QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index ) const { QWidget *editor = QItemDelegate::createEditor(parent, option, index); QLineEdit *le = dynamic_cast(editor); - if (le) + if (!le) + return editor; + + int col = index.column(); + QTableWidgetItem *twi = mTable->horizontalHeaderItem(col); + if(!twi) { - int col = index.column(); - if( mFields[col-1].type()==QVariant::Int ) - { - le->setValidator( new QIntValidator(le) ); - } - else if( mFields[col-1].type()==QVariant::Double ) - { - le->setValidator( new QDoubleValidator(le) ); - } + QgsDebugMsg( QString("horizontalHeaderItem %1 not found").arg(col) ); + return editor; } + + int type = twi->data(QgsAttributeTable::AttributeType).toInt(); + if( type==QVariant::Int ) + { + le->setValidator( new QIntValidator(le) ); + } + else if( type==QVariant::Double ) + { + le->setValidator( new QDoubleValidator(le) ); + } + return editor; } -QgsAttributeTable::QgsAttributeTable(QWidget * parent): +QgsAttributeTable::QgsAttributeTable(QWidget * parent) : QTableWidget(parent), lockKeyPressed(false), mEditable(false), @@ -69,14 +81,12 @@ QgsAttributeTable::QgsAttributeTable(QWidget * parent): f.setFamily("Helvetica"); f.setPointSize(9); setFont(f); - mDelegate = new QgsAttributeTableItemDelegate(mFields, this); + mDelegate = new QgsAttributeTableItemDelegate(this); setItemDelegate(mDelegate); setSelectionBehavior(QAbstractItemView::SelectRows); connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(handleChangedSelections())); - connect(this, SIGNAL(cellChanged(int, int)), this, SLOT(storeChangedValue(int,int))); connect(horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(columnClicked(int))); connect(verticalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(rowClicked(int))); - setReadOnly(true); setFocus(); } @@ -90,14 +100,17 @@ void QgsAttributeTable::setReadOnly(bool b) { setEditTriggers(b ? QAbstractItemView::NoEditTriggers : QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); + if(!b) { + setColumnReadOnly(0, true); + } } void QgsAttributeTable::setColumnReadOnly(int col, bool ro) { for (int i = 0; i < rowCount(); ++i) { - QTableWidgetItem *item = this->item(i, col); - item->setFlags(ro ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable); + QTableWidgetItem *twi = item(i, col); + twi->setFlags(ro ? twi->flags() & ~Qt::ItemIsEditable : twi->flags() | Qt::ItemIsEditable); } } @@ -173,16 +186,16 @@ void QgsAttributeTable::keyReleaseEvent(QKeyEvent * ev) void QgsAttributeTable::handleChangedSelections() { - emit selectionRemoved(false); + emit selectionRemoved(false); QList selectedItemRanges = selectedRanges(); QList::const_iterator range_it = selectedItemRanges.constBegin(); for (; range_it != selectedItemRanges.constEnd(); ++range_it) { for (int index = range_it->topRow(); index <= range_it->bottomRow(); index++) - { - emit selected(item(index, 0)->text().toInt(), false); - } + { + emit selected(item(index, 0)->text().toInt(), false); + } } //don't send the signal repaintRequested() from here @@ -203,19 +216,8 @@ void QgsAttributeTable::selectRowWithId(int id) void QgsAttributeTable::sortColumn(int col, bool ascending) { - //if the first entry contains a letter, sort alphanumerically, otherwise numerically - QString firstentry = item(0, col)->text(); - bool containsletter = false; - for (int i = 0; i < firstentry.length(); i++) - { - if (firstentry[i].isLetter()) - { - containsletter = true; - break; - } - } - - qsort(0, rowCount() - 1, col, ascending, containsletter); + int type = horizontalHeaderItem(col)->data(QgsAttributeTable::AttributeType).toInt(); + qsort(0, rowCount() - 1, col, ascending, type!=QVariant::Int && type==QVariant::Double); } @@ -302,7 +304,8 @@ void QgsAttributeTable::qsort(int lower, int upper, int col, bool ascending, boo j = upper; for (;;) { - while (compareItems(item(++i, col)->text(), v, ascending, alphanumeric) == -1); + while (compareItems(item(++i, col)->text(), v, ascending, alphanumeric) == -1) + ; while (compareItems(item(--j, col)->text(), v, ascending, alphanumeric) == 1 && j > 0); //make sure that j does not get negative if (i >= j) { @@ -381,45 +384,6 @@ void QgsAttributeTable::popupItemSelected(QAction* menuAction) mActions.doAction(id, mActionValues, mClickedOnValue); } -bool QgsAttributeTable::addAttribute(const QString& name, const QString& type) -{ - //first test if an attribute with the same name is already in the table - for(int i=0;itext()==name) - { - //name conflict - return false; - } - } - mAddedAttributes.insert(name,type); - - QgsDebugMsg("inserting attribute " + name + " of type " + type + ", numCols: " + QString::number(columnCount()) ); - //add a new column at the end of the table - insertColumn(columnCount()); - setHorizontalHeaderItem(columnCount()-1, new QTableWidgetItem(name)); - mEdited=true; - return true; -} - -void QgsAttributeTable::deleteAttribute(const QString& name) -{ - //check, if there is already an attribute with this name in mAddedAttributes - QgsNewAttributesMap::iterator iter = mAddedAttributes.find(name); - if(iter!=mAddedAttributes.end()) - { - mAddedAttributes.erase(iter); - removeAttrColumn(name); - } - else - { - mDeletedAttributes.insert(name); - removeAttrColumn(name); - } - mEdited=true; -} - - /* Deprecated: See QgisApp::editCopy() instead */ void QgsAttributeTable::copySelectedRows() { @@ -463,157 +427,60 @@ void QgsAttributeTable::copySelectedRows() clipboard->setText(toClipboard, QClipboard::Clipboard); } -bool QgsAttributeTable::commitChanges(QgsVectorLayer* layer) +void QgsAttributeTable::fillTable(QgsVectorLayer *layer) { - bool isSuccessful = false; + int row = 0; - if(layer) + const QgsFieldMap &fields = layer->pendingFields(); + + // set up the column headers + setColumnCount(fields.size()+1); + + setHorizontalHeaderItem(0, new QTableWidgetItem("id")); //label for the id-column + + int h = 1; + for (QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt!=fields.end(); fldIt++, h++) { - //convert strings of deleted attributes to ids + QgsDebugMsg( QString("%1: field %2: %3 | %4") + .arg(h).arg(fldIt.key()).arg(fldIt->name()).arg( QVariant::typeToName(fldIt->type()) ) ); - QgsVectorDataProvider* provider = layer->getDataProvider(); + QTableWidgetItem *twi = new QTableWidgetItem(fldIt->name()); + twi->setData( AttributeIndex, fldIt.key() ); + twi->setData( AttributeName, fldIt->name() ); + twi->setData( AttributeType, fldIt->type() ); + setHorizontalHeaderItem(h, twi); - if(provider) - { - - QgsAttributeIds deletedIds; - QSet::const_iterator it = mDeletedAttributes.constBegin(); - - for(; it != mDeletedAttributes.constEnd(); ++it) - { - deletedIds.insert(provider->indexFromFieldName(*it)); - } - - isSuccessful = true; - if( !mAddedAttributes.empty() ) - { - // add new attributes beforehand, so attribute changes can be applied - isSuccessful = layer->commitAttributeChanges(QgsAttributeIds(), mAddedAttributes, QgsChangedAttributesMap()); - - if(isSuccessful) - // forget added attributes on successful addition - mAddedAttributes.clear(); - } - - if(isSuccessful) - { - QgsChangedAttributesMap attributeChanges; //convert mChangedValues to QgsChangedAttributesMap - int fieldIndex; - - QMap >::const_iterator att_it = mChangedValues.constBegin(); - for(; att_it != mChangedValues.constEnd(); ++att_it) - { - QgsAttributeMap newAttMap; - QMap::const_iterator record_it = att_it->constBegin(); - for(; record_it != att_it->constEnd(); ++record_it) - { - fieldIndex = provider->indexFromFieldName(record_it.key()); - if(fieldIndex != -1) - { - if( record_it.value()=="NULL" || - ( record_it.value().isEmpty() && - (provider->fields()[fieldIndex].type()==QVariant::Int || - provider->fields()[fieldIndex].type()==QVariant::Double) ) ) - newAttMap.insert(fieldIndex, QVariant(QString::null) ); - else - newAttMap.insert(fieldIndex, record_it.value()); - } - else - { - QgsDebugMsg("Changed attribute " + record_it.key() + " not found"); - } - } - attributeChanges.insert(att_it.key(), newAttMap); - } - - isSuccessful = layer->commitAttributeChanges(deletedIds, - QgsNewAttributesMap(), - attributeChanges); - } - } + mAttrIdxMap.insert(fldIt.key(), h); } - if (isSuccessful) + QgsFeatureList features; + if( layer->selectedFeatureCount()==0 ) { - mEdited=false; - clearEditingStructures(); + layer->select(layer->pendingAllAttributesList(), QgsRect(), false); + + QgsFeature f; + while( layer->getNextFeature(f) ) + features << f; + } + else + { + features = layer->selectedFeatures(); } - return isSuccessful; -} - -bool QgsAttributeTable::rollBack(QgsVectorLayer* layer) -{ - if(layer) - { - fillTable(layer); - } - mEdited=false; - clearEditingStructures(); - return true; -} - - -void QgsAttributeTable::fillTable(QgsVectorLayer* layer) -{ - QgsVectorDataProvider* provider=layer->getDataProvider(); - if(provider) - { - QgsFeature fet; - int row = 0; - - QgsFeatureList& addedFeatures = layer->addedFeatures(); - QgsFeatureIds& deletedFeatures = layer->deletedFeatureIds(); - - // set up the column headers - mFields = provider->fields(); - int fieldcount=provider->fieldCount(); + setRowCount( features.size() ); - setRowCount(provider->featureCount() + addedFeatures.size() - deletedFeatures.size()); - setColumnCount(fieldcount+1); - setHorizontalHeaderItem(0, new QTableWidgetItem("id")); //label for the id-column + for(int i=0; iname() + - " | " + QString(QVariant::typeToName(fldIt->type())) ); + // Default row height is too tall + resizeRowsToContents(); - setHorizontalHeaderItem(h++, new QTableWidgetItem(fldIt->name())); - } - - //go through the features and fill the values into the table - QgsAttributeList all = provider->allAttributesList(); - provider->select(all, QgsRect(), false); - - while (provider->getNextFeature(fet)) - { - if (!deletedFeatures.contains(fet.featureId())) - { - putFeatureInTable(row, fet); - row++; - } - } - - //also consider the not commited features - for(QgsFeatureList::iterator it = addedFeatures.begin(); it != addedFeatures.end(); it++) - { - putFeatureInTable(row, *it); - row++; - } - - // Default row height is too tall - resizeRowsToContents(); - // Make each column wide enough to show all the contents - for (int i = 0; i < columnCount(); ++i) - { - resizeColumnToContents(i); - } - } + // Make each column wide enough to show all the contents + for (int i=0; i= rowCount()) @@ -623,21 +490,35 @@ void QgsAttributeTable::putFeatureInTable(int row, QgsFeature& fet) //id-field int id = fet.featureId(); - QTableWidgetItem *item = new QTableWidgetItem(QString::number(id)); - item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - setItem(row, 0, item); + QTableWidgetItem *twi = new QTableWidgetItem(QString::number(id)); + twi->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + setItem(row, 0, twi); insertFeatureId(id, row); //insert the id into the search tree of qgsattributetable - const QgsAttributeMap& attr = fet.attributeMap(); - QgsAttributeMap::const_iterator it; - int h = 1; - for (it = attr.begin(); it != attr.end(); ++it) - { - QString value; + const QgsAttributeMap& attr = fet.attributeMap(); + + for (QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it) + { + if( !mAttrIdxMap.contains( it.key() ) ) + continue; + + int h = mAttrIdxMap[ it.key() ]; + + twi = horizontalHeaderItem(h); + if(!twi) + { + QgsDebugMsg("header item not found."); + continue; + } + + int type = twi->data(AttributeType).toInt(); + bool isNum = (type == QVariant::Double || type == QVariant::Int); + + QString value; // get the field values if( it->isNull() ) { - if( mFields[h-1].type()==QVariant::Int || mFields[h-1].type()==QVariant::Double ) + if( isNum ) value=""; else value="NULL"; @@ -645,58 +526,10 @@ void QgsAttributeTable::putFeatureInTable(int row, QgsFeature& fet) value = it->toString(); } - bool isNum; - value.toFloat(&isNum); - item = new QTableWidgetItem(value); - if (isNum) item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - setItem(row, h++, item); - } -} - -void QgsAttributeTable::storeChangedValue(int row, int column) -{ - //id column is not editable - if(column>0) - { - //find feature id - int id=item(row,0)->text().toInt(); - QString field = horizontalHeaderItem(column)->text(); - - if(id>=0) - { - // add empty map for feature if doesn't exist - if (!mChangedValues.contains(id)) - { - mChangedValues.insert(id, QMap()); - } - - mChangedValues[id].insert(field, item(row,column)->text()); - mEdited=true; - } - else - { - // added feature attribute changed - emit featureAttributeChanged(row,column); - } - } -} - -void QgsAttributeTable::clearEditingStructures() -{ - mDeletedAttributes.clear(); - mAddedAttributes.clear(); - mChangedValues.clear(); -} - -void QgsAttributeTable::removeAttrColumn(const QString& name) -{ - for(int i=0;itext()==name) - { - removeColumn(i); - break; - } + twi = new QTableWidgetItem(value); + if (isNum) + twi->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + setItem(row, h, twi); } } @@ -852,3 +685,54 @@ bool QgsAttributeTable::checkSelectionChanges() return true; } } + +void QgsAttributeTable::attributeValueChanged(int fid, int idx, const QVariant &value) +{ + if( !rowIdMap.contains(fid) ) + return; + + if( !mAttrIdxMap.contains(idx) ) + return; + + item( rowIdMap[fid], mAttrIdxMap[idx] )->setText( value.toString() ); +} + +void QgsAttributeTable::featureDeleted(int fid) +{ + if( !rowIdMap.contains(fid) ) + return; + + int row = rowIdMap[fid]; + + removeRow(row); + + for(QMap::iterator it=rowIdMap.begin(); it!=rowIdMap.end(); it++) + if( it.value() > row ) + rowIdMap[ it.key() ]--; +} + +void QgsAttributeTable::addAttribute(int attr, const QgsField &fld) +{ + QTableWidgetItem *twi = new QTableWidgetItem( fld.name() ); + twi->setData( AttributeIndex, attr ); + twi->setData( AttributeName, fld.name() ); + twi->setData( AttributeType, fld.type() ); + + insertColumn( columnCount() ); + setHorizontalHeaderItem(columnCount()-1, twi); + + mAttrIdxMap.insert(attr, columnCount()-1); +} + +void QgsAttributeTable::deleteAttribute(int attr) +{ + int column = mAttrIdxMap[attr]; + + removeColumn( column ); + mAttrIdxMap.remove(attr); + for(QMap::iterator it=mAttrIdxMap.begin(); it!=mAttrIdxMap.end(); it++) + { + if( it.value()>column ) + mAttrIdxMap[ it.key() ]--; + } +} diff --git a/src/app/qgsattributetable.h b/src/app/qgsattributetable.h index bdd3ab69589..75228f3d80d 100644 --- a/src/app/qgsattributetable.h +++ b/src/app/qgsattributetable.h @@ -21,6 +21,7 @@ #include "qgsattributeaction.h" #include "qgsvectorlayer.h" +#include "qgsfield.h" #include #include @@ -31,18 +32,21 @@ *@author Gary E.Sherman */ +class QgsAttributeTable; + class QgsAttributeTableItemDelegate: public QItemDelegate { Q_OBJECT public: - QgsAttributeTableItemDelegate(const QgsFieldMap & fields, QObject * parent = 0); + QgsAttributeTableItemDelegate(QgsAttributeTable *table, QObject * parent = 0); QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const; + private: - const QgsFieldMap & mFields; + QgsAttributeTable *mTable; }; -class QgsAttributeTable:public QTableWidget +class QgsAttributeTable : public QTableWidget { Q_OBJECT @@ -50,9 +54,17 @@ class QgsAttributeTable:public QTableWidget QgsAttributeTable(QWidget * parent = 0); ~QgsAttributeTable(); + enum { + AttributeIndex = Qt::UserRole, + AttributeName = Qt::UserRole+1, + AttributeType = Qt::UserRole+2, + }; + void setReadOnly(bool b); void setColumnReadOnly(int col, bool ro); - /**Inserts the feature with the specified id into rowIdMap. This function has to be called (e.g. from QgsShapeFileLayer) when a row is inserted into the table*/ + + /* Inserts the feature with the specified id into rowIdMap. This function has to be called + (e.g. from QgsShapeFileLayer) when a row is inserted into the table */ void insertFeatureId(int id, int row); /**Selects the row which belongs to the feature with the specified id*/ void selectRowWithId(int id); @@ -66,38 +78,11 @@ class QgsAttributeTable:public QTableWidget bool edited() const {return mEdited;} /**Switches editing mode on and off*/ void setEditable(bool enabled){mEditable=enabled;} - /**Adds an attribute to the table (but does not commit it yet) - @param name attribute name - @param type attribute type - @return false in case of a name conflict, true in case of success*/ - bool addAttribute(const QString& name, const QString& type); - /**Deletes an attribute (but does not commit it) - @param name attribute name*/ - void deleteAttribute(const QString& name); /** Copies the selected rows to the clipboard Deprecated: See QgisApp::editCopy() instead */ void copySelectedRows(); - /** - Attempts to commit any changes to disk. Returns the result of the attempt. - If a commit fails, the in-memory changes are left alone. - - This allows editing to continue if the commit failed on e.g. a - disallowed value in a Postgres database - the user can re-edit and try - again. - - Delegates to QgsVectorLayer to decide, which changes - belong to not commited features or to commited ones. - - */ - bool commitChanges(QgsVectorLayer* layer); - - /**Discard all changes and restore - the state before editing was started*/ - bool rollBack(QgsVectorLayer* layer); - /**Fills the contents of a provider into this table*/ - void fillTable(QgsVectorLayer* layer); /**Swaps the selected rows such that the selected ones are on the top of the table*/ void bringSelectedToTop(); /** Selects rows with chosen feature IDs */ @@ -107,38 +92,42 @@ class QgsAttributeTable:public QTableWidget /** Shows all rows */ void showAllRows(); + /**Fills the contents of a provider into this table*/ + void fillTable(QgsVectorLayer *layer); + void addAttribute(int idx, const QgsField &fld); + void deleteAttribute(int idx); + public slots: void columnClicked(int col); void rowClicked(int row); + // Called when the user chooses an item on the popup menu void popupItemSelected(QAction * menuAction); + void attributeValueChanged(int fid, int idx, const QVariant &value); + void featureDeleted(int fid); + protected slots: void handleChangedSelections(); - /**Writes changed values to 'mChangedValues'*/ - void storeChangedValue(int row, int column); protected: /**Flag telling if the ctrl-button or the shift-button is pressed*/ bool lockKeyPressed; /**Search tree to find a row corresponding to a feature id*/ QMap rowIdMap; + /**Map attribute index to columns*/ + QMap mAttrIdxMap; bool mEditable; /**True if table has been edited and contains uncommited changes*/ bool mEdited; - /**Map containing the added attributes. The key is the attribute name - and the value the attribute type*/ - QgsNewAttributesMap mAddedAttributes; - /**Set containing the attribute names of deleted attributes*/ - QSet mDeletedAttributes; - /**Nested map containing the changed attribute values. The int is the feature id, - the first QString the attribute name and the second QString the new value*/ - QMap > mChangedValues; /**Stors the numbers of the last selected rows. This is used to check for selection changes before emit repaintRequested()*/ std::set mLastSelectedRows; - /**Compares the content of two cells either alphanumeric or numeric. If 'ascending' is true, -1 means s1 is less, 0 equal, 1 greater. If 'ascending' is false, -1 means s1 is more, 0 equal, 1 greater. This method is used mainly to sort a column*/ + /* Compares the content of two cells either alphanumeric or numeric. + If 'ascending' is true, -1 means s1 is less, 0 equal, 1 greater. + If 'ascending' is false, -1 means s1 is more, 0 equal, 1 greater. + This method is used mainly to sort a column*/ int compareItems(QString s1, QString s2, bool ascending, bool alphanumeric); void keyPressEvent(QKeyEvent* ev); void keyReleaseEvent(QKeyEvent* ev); @@ -146,16 +135,15 @@ class QgsAttributeTable:public QTableWidget void qsort(int lower, int upper, int col, bool ascending, bool alphanumeric); /**Called when the user requests a popup menu*/ void contextMenuEvent(QContextMenuEvent* event); - /**Clears mAddedAttributes, mDeletedAttributes and mChangedValues*/ - void clearEditingStructures(); /**Removes the column belonging to an attribute from the table @name attribut name*/ void removeAttrColumn(const QString& name); /** puts attributes of feature to the chosen table row */ - void putFeatureInTable(int row, QgsFeature& fet); + void putFeatureInTable(int row, const QgsFeature& fet); void mouseReleaseEvent(QMouseEvent* e); - /**This function compares the current selection and the selection of the last repaint. Returns true if there are differences in the selection. - Also, mLastSelectedRows is updated*/ + /**This function compares the current selection and the selection of the last repaint. + Returns true if there are differences in the selection. + Also, mLastSelectedRows is updated*/ bool checkSelectionChanges(); signals: @@ -165,8 +153,6 @@ class QgsAttributeTable:public QTableWidget void selectionRemoved(bool); /**Is emitted when a set of related selection and deselection signals have been emitted*/ void repaintRequested(); - /**Is emitted when a attribute of a added feature is changed*/ - void featureAttributeChanged(int row, int column); private: void swapRows(int row1, int row2); @@ -179,10 +165,10 @@ class QgsAttributeTable:public QTableWidget QgsAttributeTableItemDelegate *mDelegate; - QgsFieldMap mFields; - // Track previous columm for QTableView sortIndicator wrong direction workaround int mPreviousSortIndicatorColumn; + + QgsVectorLayer *mLayer; }; #endif diff --git a/src/app/qgsattributetabledisplay.cpp b/src/app/qgsattributetabledisplay.cpp index 03724600150..a1fa2f015ef 100644 --- a/src/app/qgsattributetabledisplay.cpp +++ b/src/app/qgsattributetabledisplay.cpp @@ -21,8 +21,6 @@ #include "qgisapp.h" #include "qgsapplication.h" -#include "qgsaddattrdialog.h" -#include "qgsdelattrdialog.h" #include "qgsfeature.h" #include "qgsfield.h" #include "qgslogger.h" @@ -39,249 +37,174 @@ #include #include #include +#include -QgsAttributeTableDisplay::QgsAttributeTableDisplay(QgsVectorLayer* layer, QgisApp * qgisApp) +QgsAttributeTableDisplay::QgsAttributeTableDisplay(QgsVectorLayer* layer) : QDialog(0, Qt::Window), mLayer(layer), - mQgisApp(qgisApp) + mDock(NULL) { setupUi(this); restorePosition(); setTheme(); + + mToggleEditingButton->setEnabled( layer->getDataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ); + mToggleEditingButton->setChecked( layer->isEditable() ); + connect(mRemoveSelectionButton, SIGNAL(clicked()), this, SLOT(removeSelection())); connect(mSelectedToTopButton, SIGNAL(clicked()), this, SLOT(selectedToTop())); connect(mInvertSelectionButton, SIGNAL(clicked()), this, SLOT(invertSelection())); connect(mCopySelectedRowsButton, SIGNAL(clicked()), this, SLOT(copySelectedRowsToClipboard())); connect(mZoomMapToSelectedRowsButton, SIGNAL(clicked()), this, SLOT(zoomMapToSelectedRows())); - connect(mAddAttributeButton, SIGNAL(clicked()), this, SLOT(addAttribute())); - connect(mDeleteAttributeButton, SIGNAL(clicked()), this, SLOT(deleteAttributes())); connect(mSearchButton, SIGNAL(clicked()), this, SLOT(search())); connect(mSearchShowResults, SIGNAL(activated(int)), this, SLOT(searchShowResultsChanged(int))); connect(btnAdvancedSearch, SIGNAL(clicked()), this, SLOT(advancedSearch())); connect(buttonBox, SIGNAL(helpRequested()), this, SLOT(showHelp())); - connect(buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), - this, SLOT(close())); - connect(tblAttributes, SIGNAL(featureAttributeChanged(int,int)), this, SLOT(changeFeatureAttribute(int,int))); - - //disable those buttons until start editing has been pressed and provider supports it - mAddAttributeButton->setEnabled(false); - mDeleteAttributeButton->setEnabled(false); + connect(buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close())); - int cap=layer->getDataProvider()->capabilities(); - if((cap&QgsVectorDataProvider::ChangeAttributeValues) - ||(cap&QgsVectorDataProvider::AddAttributes) - ||(cap&QgsVectorDataProvider::DeleteAttributes)) - { - btnEdit->setEnabled(true); - } - else - { - btnEdit->setEnabled(false); - } + connect(mToggleEditingButton, SIGNAL(clicked()), this, SLOT(toggleEditing())); + connect(this, SIGNAL(editingToggled(QgsMapLayer *)), QgisApp::instance(), SLOT(toggleEditing(QgsMapLayer *))); + + // etablish connection to table + connect(tblAttributes, SIGNAL(cellChanged(int, int)), this, SLOT(changeFeatureAttribute(int,int))); + + // etablish connections to layer + connect(mLayer, SIGNAL(layerDeleted()), this, SLOT(close())); + + connect(mLayer, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + + connect(mLayer, SIGNAL(editingStarted()), this, SLOT(editingToggled())); + connect(mLayer, SIGNAL(editingStopped()), this, SLOT(editingToggled())); + + connect(mLayer, SIGNAL(attributeAdded(int)), this, SLOT(attributeAdded(int))); + connect(mLayer, SIGNAL(attributeDeleted(int)), this, SLOT(attributeDeleted(int))); + + connect(mLayer, SIGNAL(attributeValueChanged(int,int,const QVariant &)), + tblAttributes, SLOT(attributeValueChanged(int,int,const QVariant &)) ); + + connect(mLayer, SIGNAL(featureDeleted(int)), + tblAttributes, SLOT(featureDeleted(int))); + + // etablish connections between table and vector layer + connect(tblAttributes, SIGNAL(selected(int, bool)), mLayer, SLOT(select(int, bool))); + connect(tblAttributes, SIGNAL(selectionRemoved(bool)), mLayer, SLOT(removeSelection(bool))); + connect(tblAttributes, SIGNAL(repaintRequested()), mLayer, SLOT(triggerRepaint())); // fill in mSearchColumns with available columns - QgsVectorDataProvider* provider = mLayer->getDataProvider(); - if (provider) + const QgsFieldMap& xfields = mLayer->pendingFields(); + QgsFieldMap::const_iterator fldIt; + for (fldIt = xfields.constBegin(); fldIt != xfields.constEnd(); ++fldIt) { - const QgsFieldMap& xfields = provider->fields(); - QgsFieldMap::const_iterator fldIt; - for (fldIt = xfields.constBegin(); fldIt != xfields.constEnd(); ++fldIt) - { - mSearchColumns->addItem(fldIt->name()); - } + mSearchColumns->addItem(fldIt->name()); } // TODO: create better labels mSearchShowResults->addItem(tr("select")); mSearchShowResults->addItem(tr("select and bring to top")); mSearchShowResults->addItem(tr("show only matching")); + + QSettings mySettings; + bool myDockFlag = mySettings.value("/qgis/dockAttributeTable",false).toBool(); + if (myDockFlag ) + { + mDock = new QDockWidget(tr("Attribute table - ") + layer->name(), QgisApp::instance()); + mDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea); + mDock->setWidget(this); + QgisApp::instance()->addDockWidget(Qt::BottomDockWidgetArea, mDock); + buttonBox->setVisible(false); + } + + setWindowTitle(tr("Attribute table - ") + layer->name()); } QgsAttributeTableDisplay::~QgsAttributeTableDisplay() { + smTables.remove(mLayer); } -QgsAttributeTable *QgsAttributeTableDisplay::table() + +void QgsAttributeTableDisplay::closeEvent(QCloseEvent *ev) { - return tblAttributes; + saveWindowLocation(); + ev->ignore(); + delete this; } + +void QgsAttributeTableDisplay::fillTable() +{ + tblAttributes->fillTable( mLayer ); + tblAttributes->setReadOnly( !mLayer->isEditable() ); + + selectionChanged(); + + // Give the table the most recent copy of the actions for this layer. + setAttributeActions(*mLayer->actions()); +} + +void QgsAttributeTableDisplay::toggleEditing() +{ + emit editingToggled(mLayer); +} + +void QgsAttributeTableDisplay::setAttributeActions(const QgsAttributeAction &action) +{ + tblAttributes->setAttributeActions(action); +} + +void QgsAttributeTableDisplay::selectRowsWithId(const QgsFeatureIds &ids) +{ + tblAttributes->selectRowsWithId(ids); +} + void QgsAttributeTableDisplay::setTheme() { - mAddAttributeButton->setIcon(QgisApp::getThemeIcon("/mActionNewAttribute.png")); mRemoveSelectionButton->setIcon(QgisApp::getThemeIcon("/mActionUnselectAttributes.png")); mSelectedToTopButton->setIcon(QgisApp::getThemeIcon("/mActionSelectedToTop.png")); mInvertSelectionButton->setIcon(QgisApp::getThemeIcon("/mActionInvertSelection.png")); mCopySelectedRowsButton->setIcon(QgisApp::getThemeIcon("/mActionCopySelected.png")); mZoomMapToSelectedRowsButton->setIcon(QgisApp::getThemeIcon("/mActionZoomToSelected.png")); - mAddAttributeButton->setIcon(QgisApp::getThemeIcon("/mActionNewAttribute.png")); - mDeleteAttributeButton->setIcon(QgisApp::getThemeIcon("/mActionDeleteAttribute.png")); - btnEdit->setIcon(QgisApp::getThemeIcon("/mActionToggleEditing.png")); + mToggleEditingButton->setIcon(QgisApp::getThemeIcon("/mActionToggleEditing.png")); } -void QgsAttributeTableDisplay::setTitle(QString title) +void QgsAttributeTableDisplay::editingToggled() { - setWindowTitle(title); -} - -void QgsAttributeTableDisplay::deleteAttributes() -{ - QgsDelAttrDialog dialog(table()->horizontalHeader()); - if(dialog.exec()==QDialog::Accepted) - { - const std::list* attlist=dialog.selectedAttributes(); - for(std::list::const_iterator iter=attlist->begin();iter!=attlist->end();++iter) - { - table()->deleteAttribute(*iter); - } - } -} - -void QgsAttributeTableDisplay::addAttribute() -{ - QgsAddAttrDialog dialog(mLayer->getDataProvider(), this); - if(dialog.exec()==QDialog::Accepted) - { - if(!table()->addAttribute(dialog.name(),dialog.type())) - { - QMessageBox::information(this,tr("Name conflict"),tr("The attribute could not be inserted. The name already exists in the table.")); - } - } -} - -void QgsAttributeTableDisplay::startEditing() -{ - QgsVectorDataProvider* provider=mLayer->getDataProvider(); - bool editing=false; - - if(provider) - { - if(provider->capabilities()&QgsVectorDataProvider::AddAttributes) - { - mAddAttributeButton->setEnabled(true); - editing=true; - } - if(provider->capabilities()&QgsVectorDataProvider::DeleteAttributes) - { - - mDeleteAttributeButton->setEnabled(true); - editing=true; - } - if(provider->capabilities()&QgsVectorDataProvider::ChangeAttributeValues) - { - table()->setReadOnly(false); - table()->setColumnReadOnly(0,true);//id column is not editable - editing=true; - } - if(editing) - { - btnEdit->setText(tr("Stop editing")); - buttonBox->button(QDialogButtonBox::Close)->setEnabled(false); - //make the dialog modal when in editable - //otherwise map editing and table editing - //may disturb each other - //hide(); - //setModal(true); - //show(); - } - else - { - //revert button - QMessageBox::information(this,tr("Editing not permitted"),tr("The data provider is read only, editing is not allowed.")); - btnEdit->setChecked(false); - } - } -} - -void QgsAttributeTableDisplay::on_btnEdit_toggled(bool theFlag) -{ - if (theFlag) - { - startEditing(); - } - else - { - stopEditing(); - } -} - -void QgsAttributeTableDisplay::stopEditing() -{ - if(table()->edited()) - { - //commit or roll back? - QMessageBox::StandardButton commit=QMessageBox::information(this,tr("Stop editing"), - tr("Do you want to save the changes?"), - QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - if(commit==QMessageBox::Save) - { - if(!table()->commitChanges(mLayer)) - { - QMessageBox::information(this,tr("Error"),tr("Could not commit changes - changes are still pending")); - return; - } - } - else if(commit == QMessageBox::Discard) - { - table()->rollBack(mLayer); - } - else //cancel - { - return; - } - } - btnEdit->setText(tr("Start editing")); - buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); - mAddAttributeButton->setEnabled(false); - mDeleteAttributeButton->setEnabled(false); - table()->setReadOnly(true); - //make this dialog modeless again - hide(); - setModal(false); - show(); + mToggleEditingButton->setChecked( mLayer->isEditable() ); + tblAttributes->setReadOnly( !mLayer->isEditable() ); } void QgsAttributeTableDisplay::selectedToTop() { - table()->bringSelectedToTop(); + tblAttributes->bringSelectedToTop(); } void QgsAttributeTableDisplay::invertSelection() { - if(mLayer) - { - QApplication::setOverrideCursor(Qt::WaitCursor); - mLayer->invertSelection(); - QApplication::restoreOverrideCursor(); - } + if(!mLayer) + return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + mLayer->invertSelection(); + QApplication::restoreOverrideCursor(); } void QgsAttributeTableDisplay::removeSelection() { - table()->clearSelection(); - mLayer->triggerRepaint(); + tblAttributes->clearSelection(); + mLayer->triggerRepaint(); } void QgsAttributeTableDisplay::copySelectedRowsToClipboard() { - // Deprecated - // table()->copySelectedRows(); - - // Use the Application's copy method instead - mQgisApp->editCopy(mLayer); + QgisApp::instance()->editCopy(mLayer); } void QgsAttributeTableDisplay::zoomMapToSelectedRows() { - mQgisApp->zoomToSelected(); + QgisApp::instance()->zoomToSelected(); } void QgsAttributeTableDisplay::search() { - // if selected field is numeric, numeric comparison will be used - // else attributes containing entered text will be matched - - QgsVectorDataProvider* provider = mLayer->getDataProvider(); - int item = mSearchColumns->currentIndex(); - QVariant::Type type = provider->fields()[item].type(); + int type = tblAttributes->item(0, mSearchColumns->currentIndex())->data(QgsAttributeTable::AttributeType).toInt(); bool numeric = (type == QVariant::Int || type == QVariant::Double); QString str; @@ -314,25 +237,25 @@ void QgsAttributeTableDisplay::searchShowResultsChanged(int item) if (item == 2) // show only matching { - table()->showRowsWithId(mSearchIds); + tblAttributes->showRowsWithId(mSearchIds); } else { // make sure that all rows are shown - table()->showAllRows(); + tblAttributes->showAllRows(); // select matching mLayer->setSelectedFeatures(mSearchIds); if (item == 1) // select matching and bring to top - table()->bringSelectedToTop(); + tblAttributes->bringSelectedToTop(); } QApplication::restoreOverrideCursor(); } -void QgsAttributeTableDisplay::doSearch(const QString& searchString) +void QgsAttributeTableDisplay::doSearch(QString searchString) { mSearchString = searchString; @@ -343,6 +266,7 @@ void QgsAttributeTableDisplay::doSearch(const QString& searchString) QMessageBox::critical(this, tr("Search string parsing error"), search.parserErrorMsg()); return; } + QgsSearchTreeNode* searchTree = search.tree(); if (searchTree == NULL) { @@ -354,24 +278,19 @@ void QgsAttributeTableDisplay::doSearch(const QString& searchString) QApplication::setOverrideCursor(Qt::WaitCursor); - // TODO: need optimized getNextFeature which won't extract geometry - // or search by traversing table ... which one is quicker? - QgsFeature fet; - QgsVectorDataProvider* provider = mLayer->getDataProvider(); mSearchIds.clear(); - const QgsFieldMap& fields = provider->fields(); - QgsAttributeList all = provider->allAttributesList(); - - provider->select(all, QgsRect(), false); - while (provider->getNextFeature(fet)) + mLayer->select(mLayer->pendingAllAttributesList(), true, false); + + QgsFeature f; + while( mLayer->getNextFeature(f) ) { - if (searchTree->checkAgainst(fields, fet.attributeMap())) + if (searchTree->checkAgainst(mLayer->pendingFields(), f.attributeMap())) { - mSearchIds.insert(fet.featureId()); + mSearchIds << f.featureId(); } - - // check if there were errors during evaulating + + // check if there were errors during evaluating if (searchTree->hasError()) break; } @@ -386,22 +305,13 @@ void QgsAttributeTableDisplay::doSearch(const QString& searchString) // update table searchShowResultsChanged(mSearchShowResults->currentIndex()); - + QString str; if (mSearchIds.size()) str.sprintf(tr("Found %d matching features.","", mSearchIds.size()).toUtf8(), mSearchIds.size()); else str = tr("No matching features found."); QMessageBox::information(this, tr("Search results"), str); - -} - -void QgsAttributeTableDisplay::closeEvent(QCloseEvent* ev) -{ - saveWindowLocation(); - ev->ignore(); - emit deleted(); - delete this; } void QgsAttributeTableDisplay::restorePosition() @@ -423,16 +333,77 @@ void QgsAttributeTableDisplay::showHelp() void QgsAttributeTableDisplay::changeFeatureAttribute(int row, int column) { - QgsFeatureList &flist = mLayer->addedFeatures(); - - int id = table()->item(row,0)->text().toInt(); - - int i; - for(i=0; iitem(row,column)->text()); + if( !mLayer->isEditable() ) + return; + + mLayer->changeAttributeValue( + tblAttributes->item(row,0)->text().toInt(), + tblAttributes->horizontalHeaderItem(column)->data(QgsAttributeTable::AttributeIndex).toInt(), + tblAttributes->item(row, column)->text(), + false + ); +} + +QMap QgsAttributeTableDisplay::smTables; + +QgsAttributeTableDisplay *QgsAttributeTableDisplay::attributeTable(QgsVectorLayer *layer) +{ + if(!layer) + return NULL; + + if( smTables.contains(layer) ) { + QgsAttributeTableDisplay *td = smTables[layer]; + td->setAttributeActions(*layer->actions()); + td->raise(); + + return td; + } + + QgsAttributeTableDisplay *td = new QgsAttributeTableDisplay(layer); + if(!td) + return NULL; + + // display the attribute table + QApplication::setOverrideCursor(Qt::WaitCursor); + + try + { + td->fillTable(); + } + catch(std::bad_alloc& ba) + { + Q_UNUSED(ba); + QMessageBox::critical(0, tr("bad_alloc exception"), tr("Filling the attribute table has been stopped because there was no more virtual memory left")); + delete td; + td=NULL; + } + + QApplication::restoreOverrideCursor(); + + if(!td) + return NULL; + + smTables[layer] = td; + td->show(); + + return td; +} + +void QgsAttributeTableDisplay::selectionChanged() +{ + // select rows which should be selected + selectRowsWithId( mLayer->selectedFeaturesIds() ); +} + +void QgsAttributeTableDisplay::attributeAdded(int attr) +{ + tblAttributes->addAttribute(attr, mLayer->pendingFields()[attr]); +} + +void QgsAttributeTableDisplay::attributeDeleted(int attr) +{ + tblAttributes->deleteAttribute(attr); } diff --git a/src/app/qgsattributetabledisplay.h b/src/app/qgsattributetabledisplay.h index c3444813975..a682926d263 100644 --- a/src/app/qgsattributetabledisplay.h +++ b/src/app/qgsattributetabledisplay.h @@ -22,47 +22,41 @@ #include "ui_qgsattributetablebase.h" - +class QDockWidget; class QgsAttributeTable; class QgsVectorLayer; class QgisApp; +class QgsAttributeActions; /** *@author Gary E.Sherman */ -class QgsAttributeTableDisplay:public QDialog, private Ui::QgsAttributeTableBase +class QgsAttributeTableDisplay : public QDialog, private Ui::QgsAttributeTableBase { Q_OBJECT public: - /** - \param qgisApp This should be the QgisApp that spawned this table. - Otherwise the Copy button on this QgsAttributeTableDisplay - will not work. - */ - QgsAttributeTableDisplay(QgsVectorLayer* layer, QgisApp * qgisApp); + static QgsAttributeTableDisplay *attributeTable(QgsVectorLayer *layer); + ~QgsAttributeTableDisplay(); - QgsAttributeTable *table(); - void setTitle(QString title); + void fillTable(); + protected: + QgsAttributeTableDisplay(QgsVectorLayer* layer); + QgsVectorLayer* mLayer; - QgisApp * mQgisApp; + void doSearch(QString searchString); + void setAttributeActions(const QgsAttributeAction &actions); + void selectRowsWithId(const QgsFeatureIds &ids); - void doSearch(const QString& searchString); - - virtual void closeEvent(QCloseEvent* ev); + virtual void closeEvent(QCloseEvent *ev); /** array of feature IDs that match last searched condition */ QgsFeatureIds mSearchIds; protected slots: - void deleteAttributes(); - void addAttribute(); - void on_btnEdit_toggled(bool theFlag); - void startEditing(); - void stopEditing(); void selectedToTop(); void invertSelection(); void removeSelection(); @@ -72,12 +66,18 @@ class QgsAttributeTableDisplay:public QDialog, private Ui::QgsAttributeTableBase void advancedSearch(); void searchShowResultsChanged(int item); void showHelp(); + void toggleEditing(); + + void attributeAdded(int idx); + void attributeDeleted(int idx); public slots: void changeFeatureAttribute(int row, int column); - + void editingToggled(); + void selectionChanged(); + signals: - void deleted(); + void editingToggled(QgsMapLayer *); private: /** Set the icon theme for this dialog */ @@ -88,7 +88,11 @@ class QgsAttributeTableDisplay:public QDialog, private Ui::QgsAttributeTableBase QString mSearchString; + QDockWidget *mDock; + static const int context_id = 831088384; + + static QMap smTables; }; #endif diff --git a/src/app/qgsbookmarks.cpp b/src/app/qgsbookmarks.cpp index 2ba467caf36..d894e102d52 100644 --- a/src/app/qgsbookmarks.cpp +++ b/src/app/qgsbookmarks.cpp @@ -227,14 +227,12 @@ void QgsBookmarks::zoomToBookmark() QString xmax = QString::fromUtf8((const char *)sqlite3_column_text(ppStmt, 2)); QString ymax = QString::fromUtf8((const char *)sqlite3_column_text(ppStmt, 3)); // set the extent to the bookmark - dynamic_cast(mParent)->setExtent(QgsRect(xmin.toDouble(), + QgisApp::instance()->setExtent(QgsRect(xmin.toDouble(), ymin.toDouble(), xmax.toDouble(), ymax.toDouble())); // redraw the map - dynamic_cast(mParent)->getMapCanvas()->refresh(); - - + QgisApp::instance()->getMapCanvas()->refresh(); } } diff --git a/src/app/qgsclipboard.h b/src/app/qgsclipboard.h index cf49507e9a4..31fdd26dd0e 100644 --- a/src/app/qgsclipboard.h +++ b/src/app/qgsclipboard.h @@ -23,11 +23,9 @@ #include #include -class QgsFeature; -class QgsField; +#include "qgsfield.h" +#include "qgsfeature.h" -typedef QList QgsFeatureList; -typedef QMap QgsFieldMap; /** @@ -74,18 +72,18 @@ public: * when it's done with it. */ QgsFeatureList copyOf(); - + /* * Clears the internal clipboard. */ void clear(); - + /* * Inserts a copy of the feature on the internal clipboard. */ void insert( QgsFeature& feature ); - - + + private: /** QGIS-internal vector feature clipboard. diff --git a/src/app/qgsdbsourceselect.cpp b/src/app/qgsdbsourceselect.cpp index efc6cc5cdcf..3d783fc97a5 100644 --- a/src/app/qgsdbsourceselect.cpp +++ b/src/app/qgsdbsourceselect.cpp @@ -38,8 +38,8 @@ email : sherman at mrcc.com #include #include -QgsDbSourceSelect::QgsDbSourceSelect(QgisApp *app, Qt::WFlags fl) - : QDialog(app, fl), mColumnTypeThread(NULL), qgisApp(app), pd(0) +QgsDbSourceSelect::QgsDbSourceSelect(QWidget *parent, Qt::WFlags fl) + : QDialog(parent, fl), mColumnTypeThread(NULL), pd(0) { setupUi(this); btnAdd->setEnabled(false); diff --git a/src/app/qgsdbsourceselect.h b/src/app/qgsdbsourceselect.h index f66fc1a9e3a..f9ea9abc4e3 100644 --- a/src/app/qgsdbsourceselect.h +++ b/src/app/qgsdbsourceselect.h @@ -54,7 +54,7 @@ class QgsDbSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase public: //! Constructor - QgsDbSourceSelect(QgisApp *app, Qt::WFlags fl = QgisGui::ModalDialogFlags); + QgsDbSourceSelect(QWidget *parent = 0, Qt::WFlags fl = QgisGui::ModalDialogFlags); //! Destructor ~QgsDbSourceSelect(); //! Opens the create connection dialog to build a new connection @@ -136,8 +136,6 @@ class QgsDbSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase QStringList m_selectedTables; // Storage for the range of layer type icons QMap > mLayerIcons; - //! Pointer to the qgis application mainwindow - QgisApp *qgisApp; PGconn *pd; static const int context_id = 939347163; //! Model that acts as datasource for mTableTreeWidget diff --git a/src/app/qgsidentifyresults.cpp b/src/app/qgsidentifyresults.cpp index 97c2557be76..cb9ca4f1003 100644 --- a/src/app/qgsidentifyresults.cpp +++ b/src/app/qgsidentifyresults.cpp @@ -186,6 +186,15 @@ void QgsIdentifyResults::addDerivedAttribute(QTreeWidgetItem * fnode, QString fi new QTreeWidgetItem(daRootNode, labels); } +void QgsIdentifyResults::addEdit(QTreeWidgetItem * fnode, int id) +{ + QStringList labels; + labels << "edit" << QString::number(id); + QTreeWidgetItem *item = new QTreeWidgetItem(fnode, labels ); + + item->setIcon ( 0, QgisApp::getThemeIcon("/mIconEditable.png") ); +} + void QgsIdentifyResults::addAction(QTreeWidgetItem * fnode, int id, QString field, QString value) { QStringList labels; @@ -198,7 +207,7 @@ void QgsIdentifyResults::addAction(QTreeWidgetItem * fnode, int id, QString fiel /** Add a feature node to the list */ QTreeWidgetItem *QgsIdentifyResults::addNode(QString label) { - return (new QTreeWidgetItem(lstResults, QStringList(label))); + return new QTreeWidgetItem(lstResults, QStringList(label)); } void QgsIdentifyResults::setTitle(QString title) @@ -251,15 +260,21 @@ void QgsIdentifyResults::setActions( const QgsAttributeAction& actions ) void QgsIdentifyResults::clicked ( QTreeWidgetItem *item ) { - if ( !item ) return; + if ( !item ) + return; - if ( item->text(2) != "action" ) return; + if ( item->text(2) == "action" ) + { + int id = item->text(3).toInt(); - int id = item->text(3).toInt(); + extractAllItemData(item); - extractAllItemData(item); - - mActions.doAction(id, mValues, mClickedOnValue); + mActions.doAction(id, mValues, mClickedOnValue); + } + else if( item->text(0) == "edit" ) + { + emit editFeature( item->text(1).toInt() ); + } } void QgsIdentifyResults::on_buttonHelp_clicked() { @@ -325,7 +340,7 @@ void QgsIdentifyResults::extractAllItemData(QTreeWidgetItem* item) parent = child; mValues.clear(); - + // For the code below we // need to do the comparison on the text strings rather than the // pointers because if the user clicked on the parent, we need @@ -341,27 +356,27 @@ void QgsIdentifyResults::extractAllItemData(QTreeWidgetItem* item) if (parent->child(j)->text(0) == mDerivedLabel ) { for (int k = 0; k < parent->child(j)->childCount(); ++k) { - mValues.push_back( - std::make_pair(mDerivedLabel + "." - + parent->child(j)->child(k)->text(0), - parent->child(j)->child(k)->text(1))); + mValues.push_back( + std::make_pair(mDerivedLabel + "." + + parent->child(j)->child(k)->text(0), + parent->child(j)->child(k)->text(1))); - if (item == parent->child(j)->child(k)) - { - mClickedOnValue = valuesIndex; - } + if (item == parent->child(j)->child(k)) + { + mClickedOnValue = valuesIndex; + } - valuesIndex++; + valuesIndex++; } } else // do the actual feature attributes { mValues.push_back(std::make_pair(parent->child(j)->text(0), - parent->child(j)->text(1))); + parent->child(j)->text(1))); if (item == parent->child(j)) { - mClickedOnValue = valuesIndex; + mClickedOnValue = valuesIndex; } valuesIndex++; diff --git a/src/app/qgsidentifyresults.h b/src/app/qgsidentifyresults.h index c16ec218ac0..3a91f6639e7 100644 --- a/src/app/qgsidentifyresults.h +++ b/src/app/qgsidentifyresults.h @@ -57,6 +57,9 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase /** Add an action to the feature display node */ void addAction(QTreeWidgetItem *parent, int id, QString field, QString value); + /** Add an edit action to the feature display node */ + void addEdit(QTreeWidgetItem *parent, int id); + /** Add a feature node to the feature display */ QTreeWidgetItem * addNode(QString label); /** Set the title for the identify results dialog */ @@ -85,6 +88,7 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase signals: void selectedFeatureChanged(int featureId); + void editFeature(int featureId); public slots: @@ -110,26 +114,27 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase private: - QgsAttributeAction mActions; - int mClickedOnValue; - QMenu* mActionPopup; - std::vector > mValues; - static const int context_id = 689216579; - int mCurrentFeatureId; - QString mDerivedLabel; + bool mEditable; + QgsAttributeAction mActions; + int mClickedOnValue; + QMenu* mActionPopup; + std::vector > mValues; + static const int context_id = 689216579; + int mCurrentFeatureId; + QString mDerivedLabel; - /** - Keeps track of what derived-attribute (e.g. Length, Area) - root nodes have been generated for each feature in this widget. + /** + Keeps track of what derived-attribute (e.g. Length, Area) + root nodes have been generated for each feature in this widget. - First item: Feature root node - Second item: Derived-attribute root node for that feature - */ - std::map mDerivedAttributeRootNodes; + First item: Feature root node + Second item: Derived-attribute root node for that feature + */ + std::map mDerivedAttributeRootNodes; - // Convenience function to populate mValues with all of the item names and - // values for a item, including the derived ones. - void extractAllItemData(QTreeWidgetItem* item); + // Convenience function to populate mValues with all of the item names and + // values for a item, including the derived ones. + void extractAllItemData(QTreeWidgetItem* item); }; #endif diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index c3e67da3da6..32d10d67a92 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -42,419 +42,420 @@ QgsMapToolAddFeature::~QgsMapToolAddFeature() void QgsMapToolAddFeature::canvasReleaseEvent(QMouseEvent * e) { QgsVectorLayer *vlayer = dynamic_cast (mCanvas->currentLayer()); - + if (!vlayer) - { - QMessageBox::information(0, QObject::tr("Not a vector layer"), - QObject::tr("The current layer is not a vector layer")); - return; - } + { + QMessageBox::information(0, QObject::tr("Not a vector layer"), + QObject::tr("The current layer is not a vector layer")); + return; + } QGis::WKBTYPE layerWKBType = vlayer->geometryType(); //no support for adding features to 2.5D types yet if(layerWKBType == QGis::WKBLineString25D || layerWKBType == QGis::WKBPolygon25D || \ - layerWKBType == QGis::WKBMultiLineString25D || layerWKBType == QGis::WKBPoint25D || layerWKBType == QGis::WKBMultiPoint25D) - { - QMessageBox::critical(0, QObject::tr("2.5D shape type not supported"), QObject::tr("Adding features to 2.5D shapetypes is not supported yet")); - delete mRubberBand; - mRubberBand = NULL; - mCapturing = FALSE; - mCaptureList.clear(); - mCanvas->refresh(); - return; - } - + layerWKBType == QGis::WKBMultiLineString25D || layerWKBType == QGis::WKBPoint25D || layerWKBType == QGis::WKBMultiPoint25D) + { + QMessageBox::critical(0, QObject::tr("2.5D shape type not supported"), QObject::tr("Adding features to 2.5D shapetypes is not supported yet")); + delete mRubberBand; + mRubberBand = NULL; + mCapturing = FALSE; + mCaptureList.clear(); + mCanvas->refresh(); + return; + } + QgsVectorDataProvider* provider = vlayer->getDataProvider(); - + if(!(provider->capabilities() & QgsVectorDataProvider::AddFeatures)) - { - QMessageBox::information(0, QObject::tr("Layer cannot be added to"), - QObject::tr("The data provider for this layer does not support the addition of features.")); - return; - } - + { + QMessageBox::information(0, QObject::tr("Layer cannot be added to"), + QObject::tr("The data provider for this layer does not support the addition of features.")); + return; + } + if (!vlayer->isEditable()) - { - QMessageBox::information(0, QObject::tr("Layer not editable"), - QObject::tr("Cannot edit the vector layer. To make it editable, go to the file item " - "of the layer, right click and check 'Allow Editing'.")); - return; - } - + { + QMessageBox::information(0, QObject::tr("Layer not editable"), + QObject::tr("Cannot edit the vector layer. To make it editable, go to the file item " + "of the layer, right click and check 'Allow Editing'.")); + return; + } + // POINT CAPTURING if (mTool == CapturePoint) + { + //check we only use this tool for point/multipoint layers + if(vlayer->vectorType() != QGis::Point) { - //check we only use this tool for point/multipoint layers - if(vlayer->vectorType() != QGis::Point) - { - QMessageBox::information(0, QObject::tr("Wrong editing tool"), - QObject::tr("Cannot apply the 'capture point' tool on this vector layer")); - return; - } + QMessageBox::information(0, QObject::tr("Wrong editing tool"), + QObject::tr("Cannot apply the 'capture point' tool on this vector layer")); + return; + } - - QgsPoint idPoint; //point in map coordinates - QList snapResults; - QgsPoint savePoint; //point in layer coordinates - - if(mSnapper.snapToBackgroundLayers(e->pos(), snapResults) == 0) + + QgsPoint idPoint; //point in map coordinates + QList snapResults; + QgsPoint savePoint; //point in layer coordinates + + if(mSnapper.snapToBackgroundLayers(e->pos(), snapResults) == 0) + { + idPoint = snapPointFromResults(snapResults, e->pos()); + try { - idPoint = snapPointFromResults(snapResults, e->pos()); - try - { - savePoint = toLayerCoords(vlayer, idPoint); - QgsDebugMsg("savePoint = " + savePoint.stringRep()); - } - catch(QgsCsException &cse) - { - Q_UNUSED(cse); - QMessageBox::information(0, QObject::tr("Coordinate transform error"), \ - QObject::tr("Cannot transform the point to the layers coordinate system")); - return; - } + savePoint = toLayerCoords(vlayer, idPoint); + QgsDebugMsg("savePoint = " + savePoint.stringRep()); + } + catch(QgsCsException &cse) + { + Q_UNUSED(cse); + QMessageBox::information(0, QObject::tr("Coordinate transform error"), \ + QObject::tr("Cannot transform the point to the layers coordinate system")); + return; } - - // emit signal - QgisApp can catch it and save point position to clipboard - // FIXME: is this still actual or something old that's not used anymore? - //emit xyClickCoordinates(idPoint); - - //only do the rest for provider with feature addition support - //note that for the grass provider, this will return false since - //grass provider has its own mechanism of feature addition - if(provider->capabilities() & QgsVectorDataProvider::AddFeatures) - { - QgsFeature* f = new QgsFeature(0,"WKBPoint"); - - int size = 0; - char end=QgsApplication::endian(); - unsigned char *wkb = NULL; - int wkbtype = 0; - double x = savePoint.x(); - double y = savePoint.y(); - - if(layerWKBType == QGis::WKBPoint) - { - size=1+sizeof(int)+2*sizeof(double); - wkb = new unsigned char[size]; - wkbtype=QGis::WKBPoint; - memcpy(&wkb[0],&end,1); - memcpy(&wkb[1],&wkbtype, sizeof(int)); - memcpy(&wkb[5], &x, sizeof(double)); - memcpy(&wkb[5]+sizeof(double), &y, sizeof(double)); - } - else if(layerWKBType == QGis::WKBMultiPoint) - { - size = 2+3*sizeof(int)+2*sizeof(double); - wkb = new unsigned char[size]; - wkbtype=QGis::WKBMultiPoint; - int position = 0; - memcpy(&wkb[position], &end, 1); - position += 1; - memcpy(&wkb[position], &wkbtype, sizeof(int)); - position += sizeof(int); - int npoint = 1; - memcpy(&wkb[position], &npoint, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &end, 1); - position += 1; - int pointtype = QGis::WKBPoint; - memcpy(&wkb[position],&pointtype, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &x, sizeof(double)); - position += sizeof(double); - memcpy(&wkb[position], &y, sizeof(double)); - } - - f->setGeometryAndOwnership(&wkb[0],size); - // add the fields to the QgsFeature - const QgsFieldMap fields=provider->fields(); - for(QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it) - { - f->addAttribute(it.key(), provider->getDefaultValue(it.key()) ); } - - // show the dialog to enter attribute values - QgsAttributeDialog * mypDialog = new QgsAttributeDialog( vlayer, f ); - if (mypDialog->exec()) + + // emit signal - QgisApp can catch it and save point position to clipboard + // FIXME: is this still actual or something old that's not used anymore? + //emit xyClickCoordinates(idPoint); + + //only do the rest for provider with feature addition support + //note that for the grass provider, this will return false since + //grass provider has its own mechanism of feature addition + if(provider->capabilities() & QgsVectorDataProvider::AddFeatures) { - qDebug("Adding feature to layer"); - vlayer->addFeature(*f); + QgsFeature* f = new QgsFeature(0,"WKBPoint"); + + int size = 0; + char end=QgsApplication::endian(); + unsigned char *wkb = NULL; + int wkbtype = 0; + double x = savePoint.x(); + double y = savePoint.y(); + + if(layerWKBType == QGis::WKBPoint) + { + size=1+sizeof(int)+2*sizeof(double); + wkb = new unsigned char[size]; + wkbtype=QGis::WKBPoint; + memcpy(&wkb[0],&end,1); + memcpy(&wkb[1],&wkbtype, sizeof(int)); + memcpy(&wkb[5], &x, sizeof(double)); + memcpy(&wkb[5]+sizeof(double), &y, sizeof(double)); + } + else if(layerWKBType == QGis::WKBMultiPoint) + { + size = 2+3*sizeof(int)+2*sizeof(double); + wkb = new unsigned char[size]; + wkbtype=QGis::WKBMultiPoint; + int position = 0; + memcpy(&wkb[position], &end, 1); + position += 1; + memcpy(&wkb[position], &wkbtype, sizeof(int)); + position += sizeof(int); + int npoint = 1; + memcpy(&wkb[position], &npoint, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &end, 1); + position += 1; + int pointtype = QGis::WKBPoint; + memcpy(&wkb[position],&pointtype, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &x, sizeof(double)); + position += sizeof(double); + memcpy(&wkb[position], &y, sizeof(double)); + } + + f->setGeometryAndOwnership(&wkb[0],size); + // add the fields to the QgsFeature + const QgsFieldMap fields=vlayer->pendingFields(); + for(QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it) + { + f->addAttribute(it.key(), provider->getDefaultValue(it.key()) ); + } + + // show the dialog to enter attribute values + QgsAttributeDialog * mypDialog = new QgsAttributeDialog( vlayer, f ); + if (mypDialog->exec()) + { + qDebug("Adding feature to layer"); + vlayer->addFeature(*f); + } + else + { + qDebug("Adding feature to layer failed"); + delete f; + } + delete mypDialog; + mCanvas->refresh(); } - else - { - qDebug("Adding feature to layer failed"); - delete f; - } - delete mypDialog; - mCanvas->refresh(); - } - - } + + } else if (mTool == CaptureLine || mTool == CapturePolygon) + { + //check we only use the line tool for line/multiline layers + if(mTool == CaptureLine && vlayer->vectorType() != QGis::Line) { - //check we only use the line tool for line/multiline layers - if(mTool == CaptureLine && vlayer->vectorType() != QGis::Line) - { - QMessageBox::information(0, QObject::tr("Wrong editing tool"), - QObject::tr("Cannot apply the 'capture line' tool on this vector layer")); - return; - } - - //check we only use the polygon tool for polygon/multipolygon layers - if(mTool == CapturePolygon && vlayer->vectorType() != QGis::Polygon) - { - QMessageBox::information(0, QObject::tr("Wrong editing tool"), - QObject::tr("Cannot apply the 'capture polygon' tool on this vector layer")); - return; - } + QMessageBox::information(0, QObject::tr("Wrong editing tool"), + QObject::tr("Cannot apply the 'capture line' tool on this vector layer")); + return; + } - //add point to list and to rubber band - int error = addVertex(e->pos()); - if(error == 1) - { - //current layer is not a vector layer - return; - } - else if (error == 2) - { - //problem with coordinate transformation - QMessageBox::information(0, QObject::tr("Coordinate transform error"), \ - QObject::tr("Cannot transform the point to the layers coordinate system")); - return; - } - - if (e->button() == Qt::LeftButton) - { - mCapturing = TRUE; - } - else if (e->button() == Qt::RightButton) - { - // End of string - - mCapturing = FALSE; - - delete mRubberBand; - mRubberBand = NULL; - - //lines: bail out if there are not at least two vertices - if(mTool == CaptureLine && mCaptureList.size() < 2) - { - mCaptureList.clear(); - return; - } - - //polygons: bail out if there are not at least two vertices - if(mTool == CapturePolygon && mCaptureList.size() < 3) - { - mCaptureList.clear(); - return; - } - - //create QgsFeature with wkb representation - QgsFeature* f = new QgsFeature(0,"WKBLineString"); - unsigned char* wkb; - int size; - char end=QgsApplication::endian(); - - if(mTool == CaptureLine) - { - if(layerWKBType == QGis::WKBLineString) - { - size=1+2*sizeof(int)+2*mCaptureList.size()*sizeof(double); - wkb= new unsigned char[size]; - int wkbtype=QGis::WKBLineString; - int length=mCaptureList.size(); - memcpy(&wkb[0],&end,1); - memcpy(&wkb[1],&wkbtype, sizeof(int)); - memcpy(&wkb[1+sizeof(int)],&length, sizeof(int)); - int position=1+2*sizeof(int); - double x,y; - for(QList::iterator it=mCaptureList.begin();it!=mCaptureList.end();++it) - { - QgsPoint savePoint = *it; - x = savePoint.x(); - y = savePoint.y(); - - memcpy(&wkb[position],&x,sizeof(double)); - position+=sizeof(double); - - memcpy(&wkb[position],&y,sizeof(double)); - position+=sizeof(double); - } - } - else if(layerWKBType == QGis::WKBMultiLineString) - { - size = 1+2*sizeof(int)+1+2*sizeof(int)+2*mCaptureList.size()*sizeof(double); - wkb= new unsigned char[size]; - int position = 0; - int wkbtype=QGis::WKBMultiLineString; - memcpy(&wkb[position], &end, 1); - position += 1; - memcpy(&wkb[position], &wkbtype, sizeof(int)); - position += sizeof(int); - int nlines = 1; - memcpy(&wkb[position], &nlines, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &end, 1); - position += 1; - int linewkbtype = QGis::WKBLineString; - memcpy(&wkb[position], &linewkbtype, sizeof(int)); - position += sizeof(int); - int length=mCaptureList.size(); - memcpy(&wkb[position], &length, sizeof(int)); - position += sizeof(int); - double x,y; - for(QList::iterator it=mCaptureList.begin();it!=mCaptureList.end();++it) - { - QgsPoint savePoint = *it; - x = savePoint.x(); - y = savePoint.y(); - - memcpy(&wkb[position],&x,sizeof(double)); - position+=sizeof(double); - - memcpy(&wkb[position],&y,sizeof(double)); - position+=sizeof(double); - } - } - else - { - QMessageBox::critical(0, QObject::tr("Error"), QObject::tr("Cannot add feature. Unknown WKB type")); - return; //unknown wkbtype - } - f->setGeometryAndOwnership(&wkb[0],size); - } - else // polygon - { - if(layerWKBType == QGis::WKBPolygon) - { - size=1+3*sizeof(int)+2*(mCaptureList.size()+1)*sizeof(double); - wkb= new unsigned char[size]; - int wkbtype=QGis::WKBPolygon; - int length=mCaptureList.size()+1;//+1 because the first point is needed twice - int numrings=1; - memcpy(&wkb[0],&end,1); - memcpy(&wkb[1],&wkbtype, sizeof(int)); - memcpy(&wkb[1+sizeof(int)],&numrings,sizeof(int)); - memcpy(&wkb[1+2*sizeof(int)],&length, sizeof(int)); - int position=1+3*sizeof(int); - double x,y; - QList::iterator it; - for(it=mCaptureList.begin();it!=mCaptureList.end();++it) - { - QgsPoint savePoint = *it; - x = savePoint.x(); - y = savePoint.y(); - - memcpy(&wkb[position],&x,sizeof(double)); - position+=sizeof(double); - - memcpy(&wkb[position],&y,sizeof(double)); - position+=sizeof(double); - } - // close the polygon - it=mCaptureList.begin(); - QgsPoint savePoint = *it; - x = savePoint.x(); - y = savePoint.y(); - - memcpy(&wkb[position],&x,sizeof(double)); - position+=sizeof(double); - - memcpy(&wkb[position],&y,sizeof(double)); - } - else if(layerWKBType == QGis::WKBMultiPolygon) - { - size = 2+5*sizeof(int)+2*(mCaptureList.size()+1)*sizeof(double); - wkb = new unsigned char[size]; - int wkbtype = QGis::WKBMultiPolygon; - int polygontype = QGis::WKBPolygon; - int length = mCaptureList.size()+1;//+1 because the first point is needed twice - int numrings = 1; - int numpolygons = 1; - int position = 0; //pointer position relative to &wkb[0] - memcpy(&wkb[position],&end,1); - position += 1; - memcpy(&wkb[position],&wkbtype, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &numpolygons, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &end, 1); - position += 1; - memcpy(&wkb[position], &polygontype, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &numrings, sizeof(int)); - position += sizeof(int); - memcpy(&wkb[position], &length, sizeof(int)); - position += sizeof(int); - double x,y; - QList::iterator it; - for(it=mCaptureList.begin();it!=mCaptureList.end();++it)//add the captured points to the polygon - { - QgsPoint savePoint = *it; - x = savePoint.x(); - y = savePoint.y(); - - memcpy(&wkb[position],&x,sizeof(double)); - position+=sizeof(double); - - memcpy(&wkb[position],&y,sizeof(double)); - position+=sizeof(double); - } - // close the polygon - it=mCaptureList.begin(); - QgsPoint savePoint = *it; - x = savePoint.x(); - y = savePoint.y(); - memcpy(&wkb[position],&x,sizeof(double)); - position+=sizeof(double); - memcpy(&wkb[position],&y,sizeof(double)); - } - else - { - QMessageBox::critical(0, QObject::tr("Error"), QObject::tr("Cannot add feature. Unknown WKB type")); - return; //unknown wkbtype - } - f->setGeometryAndOwnership(&wkb[0],size); - //is automatic polygon intersection removal activated? - int avoidPolygonIntersections = QgsProject::instance()->readNumEntry("Digitizing", "/AvoidPolygonIntersections", 0); - - if(avoidPolygonIntersections != 0) - { - if(vlayer->removePolygonIntersections(f->geometry()) != 0) - { - QMessageBox::critical(0, QObject::tr("Error"), QObject::tr("Could not remove polygon intersection")); - } - } - } - - // add the fields to the QgsFeature - const QgsFieldMap fields = provider->fields(); - for(QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it) - { - f->addAttribute(it.key(), provider->getDefaultValue(it.key())); - } - - QgsAttributeDialog * mypDialog = new QgsAttributeDialog( vlayer, f ); - if (mypDialog->exec()) + //check we only use the polygon tool for polygon/multipolygon layers + if(mTool == CapturePolygon && vlayer->vectorType() != QGis::Polygon) { - if(vlayer->addFeature(*f)) + QMessageBox::information(0, QObject::tr("Wrong editing tool"), + QObject::tr("Cannot apply the 'capture polygon' tool on this vector layer")); + return; + } + + //add point to list and to rubber band + int error = addVertex(e->pos()); + if(error == 1) + { + //current layer is not a vector layer + return; + } + else if (error == 2) + { + //problem with coordinate transformation + QMessageBox::information(0, QObject::tr("Coordinate transform error"), \ + QObject::tr("Cannot transform the point to the layers coordinate system")); + return; + } + + if (e->button() == Qt::LeftButton) + { + mCapturing = TRUE; + } + else if (e->button() == Qt::RightButton) + { + // End of string + + mCapturing = FALSE; + + delete mRubberBand; + mRubberBand = NULL; + + //lines: bail out if there are not at least two vertices + if(mTool == CaptureLine && mCaptureList.size() < 2) { - //add points to other features to keep topology up-to-date - int topologicalEditing = QgsProject::instance()->readNumEntry("Digitizing", "/TopologicalEditing", 0); - if(topologicalEditing) + mCaptureList.clear(); + return; + } + + //polygons: bail out if there are not at least two vertices + if(mTool == CapturePolygon && mCaptureList.size() < 3) + { + mCaptureList.clear(); + return; + } + + //create QgsFeature with wkb representation + QgsFeature* f = new QgsFeature(0,"WKBLineString"); + unsigned char* wkb; + int size; + char end=QgsApplication::endian(); + + if(mTool == CaptureLine) + { + if(layerWKBType == QGis::WKBLineString) { - vlayer->addTopologicalPoints(f->geometry()); + size=1+2*sizeof(int)+2*mCaptureList.size()*sizeof(double); + wkb= new unsigned char[size]; + int wkbtype=QGis::WKBLineString; + int length=mCaptureList.size(); + memcpy(&wkb[0],&end,1); + memcpy(&wkb[1],&wkbtype, sizeof(int)); + memcpy(&wkb[1+sizeof(int)],&length, sizeof(int)); + int position=1+2*sizeof(int); + double x,y; + for(QList::iterator it=mCaptureList.begin();it!=mCaptureList.end();++it) + { + QgsPoint savePoint = *it; + x = savePoint.x(); + y = savePoint.y(); + + memcpy(&wkb[position],&x,sizeof(double)); + position+=sizeof(double); + + memcpy(&wkb[position],&y,sizeof(double)); + position+=sizeof(double); + } + } + else if(layerWKBType == QGis::WKBMultiLineString) + { + size = 1+2*sizeof(int)+1+2*sizeof(int)+2*mCaptureList.size()*sizeof(double); + wkb= new unsigned char[size]; + int position = 0; + int wkbtype=QGis::WKBMultiLineString; + memcpy(&wkb[position], &end, 1); + position += 1; + memcpy(&wkb[position], &wkbtype, sizeof(int)); + position += sizeof(int); + int nlines = 1; + memcpy(&wkb[position], &nlines, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &end, 1); + position += 1; + int linewkbtype = QGis::WKBLineString; + memcpy(&wkb[position], &linewkbtype, sizeof(int)); + position += sizeof(int); + int length=mCaptureList.size(); + memcpy(&wkb[position], &length, sizeof(int)); + position += sizeof(int); + double x,y; + for(QList::iterator it=mCaptureList.begin();it!=mCaptureList.end();++it) + { + QgsPoint savePoint = *it; + x = savePoint.x(); + y = savePoint.y(); + + memcpy(&wkb[position],&x,sizeof(double)); + position+=sizeof(double); + + memcpy(&wkb[position],&y,sizeof(double)); + position+=sizeof(double); + } + } + else + { + QMessageBox::critical(0, QObject::tr("Error"), QObject::tr("Cannot add feature. Unknown WKB type")); + return; //unknown wkbtype + } + f->setGeometryAndOwnership(&wkb[0],size); + } + else // polygon + { + if(layerWKBType == QGis::WKBPolygon) + { + size=1+3*sizeof(int)+2*(mCaptureList.size()+1)*sizeof(double); + wkb= new unsigned char[size]; + int wkbtype=QGis::WKBPolygon; + int length=mCaptureList.size()+1;//+1 because the first point is needed twice + int numrings=1; + memcpy(&wkb[0],&end,1); + memcpy(&wkb[1],&wkbtype, sizeof(int)); + memcpy(&wkb[1+sizeof(int)],&numrings,sizeof(int)); + memcpy(&wkb[1+2*sizeof(int)],&length, sizeof(int)); + int position=1+3*sizeof(int); + double x,y; + QList::iterator it; + for(it=mCaptureList.begin();it!=mCaptureList.end();++it) + { + QgsPoint savePoint = *it; + x = savePoint.x(); + y = savePoint.y(); + + memcpy(&wkb[position],&x,sizeof(double)); + position+=sizeof(double); + + memcpy(&wkb[position],&y,sizeof(double)); + position+=sizeof(double); + } + // close the polygon + it=mCaptureList.begin(); + QgsPoint savePoint = *it; + x = savePoint.x(); + y = savePoint.y(); + + memcpy(&wkb[position],&x,sizeof(double)); + position+=sizeof(double); + + memcpy(&wkb[position],&y,sizeof(double)); + } + else if(layerWKBType == QGis::WKBMultiPolygon) + { + size = 2+5*sizeof(int)+2*(mCaptureList.size()+1)*sizeof(double); + wkb = new unsigned char[size]; + int wkbtype = QGis::WKBMultiPolygon; + int polygontype = QGis::WKBPolygon; + int length = mCaptureList.size()+1;//+1 because the first point is needed twice + int numrings = 1; + int numpolygons = 1; + int position = 0; //pointer position relative to &wkb[0] + memcpy(&wkb[position],&end,1); + position += 1; + memcpy(&wkb[position],&wkbtype, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &numpolygons, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &end, 1); + position += 1; + memcpy(&wkb[position], &polygontype, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &numrings, sizeof(int)); + position += sizeof(int); + memcpy(&wkb[position], &length, sizeof(int)); + position += sizeof(int); + double x,y; + QList::iterator it; + for(it=mCaptureList.begin();it!=mCaptureList.end();++it)//add the captured points to the polygon + { + QgsPoint savePoint = *it; + x = savePoint.x(); + y = savePoint.y(); + + memcpy(&wkb[position],&x,sizeof(double)); + position+=sizeof(double); + + memcpy(&wkb[position],&y,sizeof(double)); + position+=sizeof(double); + } + // close the polygon + it=mCaptureList.begin(); + QgsPoint savePoint = *it; + x = savePoint.x(); + y = savePoint.y(); + memcpy(&wkb[position],&x,sizeof(double)); + position+=sizeof(double); + memcpy(&wkb[position],&y,sizeof(double)); + } + else + { + QMessageBox::critical(0, QObject::tr("Error"), QObject::tr("Cannot add feature. Unknown WKB type")); + return; //unknown wkbtype + } + f->setGeometryAndOwnership(&wkb[0],size); + + //is automatic polygon intersection removal activated? + int avoidPolygonIntersections = QgsProject::instance()->readNumEntry("Digitizing", "/AvoidPolygonIntersections", 0); + + if(avoidPolygonIntersections != 0) + { + if(vlayer->removePolygonIntersections(f->geometry()) != 0) + { + QMessageBox::critical(0, QObject::tr("Error"), QObject::tr("Could not remove polygon intersection")); + } } } - } - delete f; - delete mypDialog; - - // delete the elements of mCaptureList - mCaptureList.clear(); - mCanvas->refresh(); - } - } + + // add the fields to the QgsFeature + const QgsFieldMap fields = vlayer->pendingFields(); + for(QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it) + { + f->addAttribute(it.key(), provider->getDefaultValue(it.key())); + } + + QgsAttributeDialog * mypDialog = new QgsAttributeDialog( vlayer, f ); + if (mypDialog->exec()) + { + if(vlayer->addFeature(*f)) + { + //add points to other features to keep topology up-to-date + int topologicalEditing = QgsProject::instance()->readNumEntry("Digitizing", "/TopologicalEditing", 0); + if(topologicalEditing) + { + vlayer->addTopologicalPoints(f->geometry()); + } + } + } + delete f; + delete mypDialog; + + // delete the elements of mCaptureList + mCaptureList.clear(); + mCanvas->refresh(); + } + } } diff --git a/src/app/qgsmaptoolidentify.cpp b/src/app/qgsmaptoolidentify.cpp index e6facb26d4c..622d9b36cf8 100644 --- a/src/app/qgsmaptoolidentify.cpp +++ b/src/app/qgsmaptoolidentify.cpp @@ -31,6 +31,7 @@ #include "qgsspatialrefsys.h" #include "qgsvectordataprovider.h" #include "qgsvectorlayer.h" +#include "qgsattributedialog.h" #include #include @@ -73,7 +74,7 @@ void QgsMapToolIdentify::canvasReleaseEvent(QMouseEvent * e) return; } - QgsMapLayer* layer = mCanvas->currentLayer(); + mLayer = mCanvas->currentLayer(); // delete rubber band if there was any delete mRubberBand; @@ -81,31 +82,31 @@ void QgsMapToolIdentify::canvasReleaseEvent(QMouseEvent * e) // call identify method for selected layer - if (layer) + if (mLayer) { // In the special case of the WMS provider, // coordinates are sent back to the server as pixel coordinates // not the layer's native CRS. So identify on screen coordinates! if ( - (layer->type() == QgsMapLayer::RASTER) + (mLayer->type() == QgsMapLayer::RASTER) && - (dynamic_cast(layer)->providerKey() == "wms") + (dynamic_cast(mLayer)->providerKey() == "wms") ) { - identifyRasterWmsLayer(dynamic_cast(layer), QgsPoint(e->x(), e->y()) ); + identifyRasterWmsLayer( QgsPoint(e->x(), e->y()) ); } else { // convert screen coordinates to map coordinates QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates(e->x(), e->y()); - if (layer->type() == QgsMapLayer::VECTOR) + if (mLayer->type() == QgsMapLayer::VECTOR) { - identifyVectorLayer(dynamic_cast(layer), idPoint); + identifyVectorLayer(idPoint); } - else if (layer->type() == QgsMapLayer::RASTER) + else if (mLayer->type() == QgsMapLayer::RASTER) { - identifyRasterLayer(dynamic_cast(layer), idPoint); + identifyRasterLayer(idPoint); } else { @@ -127,8 +128,9 @@ void QgsMapToolIdentify::canvasReleaseEvent(QMouseEvent * e) } -void QgsMapToolIdentify::identifyRasterLayer(QgsRasterLayer* layer, const QgsPoint& point) +void QgsMapToolIdentify::identifyRasterLayer(const QgsPoint& point) { + QgsRasterLayer *layer = dynamic_cast(mLayer); if (!layer) return; @@ -140,7 +142,7 @@ void QgsMapToolIdentify::identifyRasterLayer(QgsRasterLayer* layer, const QgsPoi QgsAttributeAction aa; mResults = new QgsIdentifyResults(aa, mCanvas->window()); mResults->setAttribute(Qt::WA_DeleteOnClose); - // Be informed when the dialog box is closed so that we can stop using it. + // Be informed when the dialog box is closed so that we can stop using it. connect(mResults, SIGNAL(accepted()), this, SLOT(resultsDialogGone())); connect(mResults, SIGNAL(rejected()), this, SLOT(resultsDialogGone())); mResults->restorePosition(); @@ -166,8 +168,9 @@ void QgsMapToolIdentify::identifyRasterLayer(QgsRasterLayer* layer, const QgsPoi } -void QgsMapToolIdentify::identifyRasterWmsLayer(QgsRasterLayer* layer, const QgsPoint& point) +void QgsMapToolIdentify::identifyRasterWmsLayer(const QgsPoint& point) { + QgsRasterLayer *layer = dynamic_cast(mLayer); if (!layer) { return; @@ -193,7 +196,7 @@ void QgsMapToolIdentify::identifyRasterWmsLayer(QgsRasterLayer* layer, const Qgs if(xMinView < xMinLayer) { - i = (int)(point.x() - (xMinLayer - xMinView) / mupp); + i = (int)(point.x() - (xMinLayer - xMinView) / mupp); } else { @@ -214,7 +217,7 @@ void QgsMapToolIdentify::identifyRasterWmsLayer(QgsRasterLayer* layer, const Qgs if (text.isEmpty()) { - showError(layer); + showError(); return; } @@ -225,8 +228,9 @@ void QgsMapToolIdentify::identifyRasterWmsLayer(QgsRasterLayer* layer, const Qgs viewer->showMessage(); // deletes itself on close } -void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoint& point) +void QgsMapToolIdentify::identifyVectorLayer(const QgsPoint& point) { + QgsVectorLayer *layer = dynamic_cast(mLayer); if (!layer) return; @@ -250,8 +254,7 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi //QgsFeature feat; QgsAttributeAction& actions = *layer->actions(); QString fieldIndex = layer->displayField(); - QgsVectorDataProvider* dataProvider = layer->getDataProvider(); - const QgsFieldMap& fields = dataProvider->fields(); + const QgsFieldMap& fields = layer->pendingFields(); // init distance/area calculator QgsDistanceArea calc; @@ -259,6 +262,22 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi calc.setEllipsoid(ellipsoid); calc.setSourceSRS(layer->srs().srsid()); + mFeatureList.clear(); + QApplication::setOverrideCursor(Qt::WaitCursor); + + layer->select(layer->pendingAllAttributesList(), r, true); + QgsFeature f; + while( layer->getNextFeature(f) ) + mFeatureList << f; + + QApplication::restoreOverrideCursor(); + + if( layer->isEditable() && mFeatureList.size()==1 ) + { + editFeature(mFeatureList[0]); + return; + } + // display features falling within the search radius if(!mResults) { @@ -268,6 +287,8 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi connect(mResults, SIGNAL(accepted()), this, SLOT(resultsDialogGone())); connect(mResults, SIGNAL(rejected()), this, SLOT(resultsDialogGone())); connect(mResults, SIGNAL(selectedFeatureChanged(int)), this, SLOT(highlightFeature(int))); + connect(mResults, SIGNAL(editFeature(int)), this, SLOT(editFeature(int))); + // restore the identify window position and show it mResults->restorePosition(); } @@ -281,12 +302,9 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi QApplication::setOverrideCursor(Qt::WaitCursor); int lastFeatureId = 0; + QgsFeatureList::iterator f_it = mFeatureList.begin(); - QList featureList; - layer->featuresInRectangle(r, featureList, true, true); - QList::iterator f_it = featureList.begin(); - - for(; f_it != featureList.end(); ++f_it) + for(; f_it != mFeatureList.end(); ++f_it) { featureCount++; @@ -294,6 +312,10 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi featureNode->setData(0, Qt::UserRole, QVariant(f_it->featureId())); // save feature id lastFeatureId = f_it->featureId(); featureNode->setText(0, fieldIndex); + + if( layer->isEditable() ) + mResults->addEdit( featureNode, f_it->featureId() ); + const QgsAttributeMap& attr = f_it->attributeMap(); for (QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it) @@ -340,13 +362,12 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi mResults->addDerivedAttribute(featureNode, "Y", str); } - // Add actions + // Add actions QgsAttributeAction::aIter iter = actions.begin(); for (register int i = 0; iter != actions.end(); ++iter, ++i) { mResults->addAction( featureNode, i, QObject::tr("action"), iter->name() ); } - } QgsDebugMsg("Feature count on identify: " + QString::number(featureCount)); @@ -354,7 +375,7 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi //also test the not commited features //todo: eliminate copy past code mResults->setTitle(layer->name() + " - " + QString::number(featureCount) + QObject::tr(" features found")); - if (featureCount == 1) + if (featureCount == 1) { mResults->showAllAttributes(); mResults->setTitle(layer->name() + " - " + QObject::tr(" 1 feature found") ); @@ -369,228 +390,14 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi { QString title = layer->name(); title += QString( tr("- %1 features found","Identify results window title",featureCount) ).arg(featureCount); - mResults->setTitle(title); + mResults->setTitle(title); } QApplication::restoreOverrideCursor(); mResults->show(); } -#if 0 //MH: old state of the function -void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoint& point) -{ - if (!layer) - return; - - // load identify radius from settings - QSettings settings; - double identifyValue = settings.value("/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS).toDouble(); - QString ellipsoid = settings.readEntry("/qgis/measure/ellipsoid", "WGS84"); - - // create the search rectangle - double searchRadius = mCanvas->extent().width() * (identifyValue/100.0); - - QgsRect r; - r.setXmin(point.x() - searchRadius); - r.setXmax(point.x() + searchRadius); - r.setYmin(point.y() - searchRadius); - r.setYmax(point.y() + searchRadius); - - r = toLayerCoords(layer, r); - - int featureCount = 0; - QgsFeature feat; - QgsAttributeAction& actions = *layer->actions(); - QString fieldIndex = layer->displayField(); - QgsVectorDataProvider* dataProvider = layer->getDataProvider(); - QgsAttributeList allAttributes = dataProvider->allAttributesList(); - const QgsFieldMap& fields = dataProvider->fields(); - - dataProvider->select(allAttributes, r, true, true); - - // init distance/area calculator - QgsDistanceArea calc; - calc.setProjectionsEnabled(mCanvas->projectionsEnabled()); // project? - calc.setEllipsoid(ellipsoid); - calc.setSourceSRS(layer->srs().srsid()); - - if ( !layer->isEditable() ) - { - // display features falling within the search radius - if(!mResults) - { - mResults = new QgsIdentifyResults(actions, mCanvas->window()); - mResults->setAttribute(Qt::WA_DeleteOnClose); - // Be informed when the dialog box is closed so that we can stop using it. - connect(mResults, SIGNAL(accepted()), this, SLOT(resultsDialogGone())); - connect(mResults, SIGNAL(rejected()), this, SLOT(resultsDialogGone())); - connect(mResults, SIGNAL(selectedFeatureChanged(int)), this, SLOT(highlightFeature(int))); - // restore the identify window position and show it - mResults->restorePosition(); - } - else - { - mResults->raise(); - mResults->clear(); - mResults->setActions(actions); - } - - QApplication::setOverrideCursor(Qt::WaitCursor); - - int lastFeatureId = 0; - - QTreeWidgetItem *click = mResults->addNode(tr("(clicked coordinate)")); - click->setText(1, point.stringRep()); - - while (dataProvider->getNextFeature(feat)) - { - featureCount++; - - QTreeWidgetItem* featureNode = mResults->addNode("foo"); - featureNode->setData(0, Qt::UserRole, QVariant(feat.featureId())); // save feature id - lastFeatureId = feat.featureId(); - featureNode->setText(0, fieldIndex); - const QgsAttributeMap& attr = feat.attributeMap(); - - for (QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it) - { - //QgsDebugMsg(it->fieldName() + " == " + fieldIndex); - - if (fields[it.key()].name() == fieldIndex) - { - featureNode->setText(1, it->toString()); - } - mResults->addAttribute(featureNode, fields[it.key()].name(), it->toString()); - } - - // Calculate derived attributes and insert: - // measure distance or area depending on geometry type - if (layer->vectorType() == QGis::Line) - { - double dist = calc.measure(feat.geometry()); - QString str = calc.textUnit(dist, 3, mCanvas->mapUnits(), false); - mResults->addDerivedAttribute(featureNode, QObject::tr("Length"), str); - } - else if (layer->vectorType() == QGis::Polygon) - { - double area = calc.measure(feat.geometry()); - QString str = calc.textUnit(area, 3, mCanvas->mapUnits(), true); - mResults->addDerivedAttribute(featureNode, QObject::tr("Area"), str); - } - - // Add actions - QgsAttributeAction::aIter iter = actions.begin(); - for (register int i = 0; iter != actions.end(); ++iter, ++i) - { - mResults->addAction( featureNode, i, QObject::tr("action"), iter->name() ); - } - - } - - QgsDebugMsg("Feature count on identify: " + QString::number(featureCount)); - - //also test the not commited features //todo: eliminate copy past code - - mResults->setTitle(layer->name() + " - " + QString::number(featureCount) + QObject::tr(" features found")); - if (featureCount == 1) - { - mResults->showAllAttributes(); - mResults->setTitle(layer->name() + " - " + QObject::tr(" 1 feature found") ); - highlightFeature(lastFeatureId); - } - else if (featureCount == 0) - { - mResults->setTitle(layer->name() + " - " + QObject::tr("No features found") ); - mResults->setMessage ( QObject::tr("No features found"), QObject::tr("No features were found in the active layer at the point you clicked") ); - } - else - { - QString title = layer->name(); - title += QString( tr("- %1 features found","Identify results window title",featureCount) ).arg(featureCount); - mResults->setTitle(title); - } - QApplication::restoreOverrideCursor(); - - mResults->show(); - } - else // ( layer->isEditable() ) - { - // Edit attributes - // TODO: what to do if more features were selected? - nearest? - QgsChangedAttributesMap& changedAttributes = layer->changedAttributes(); - - QApplication::setOverrideCursor(Qt::WaitCursor); - - if (dataProvider->getNextFeature(feat)) - { - // these are the values to populate the dialog with - // start off with list of committed attribute values - QgsAttributeMap old = feat.attributeMap(); - - // Test if this feature already changed since the last commit - - QgsChangedAttributesMap::iterator it = changedAttributes.find(feat.featureId()); - if ( it != changedAttributes.end() ) - { - // Yes, this feature already has in-memory attribute changes - - // go through and apply the modified-but-not-committed values - QgsAttributeMap oldattr = *it; - int index=0; - for (QgsAttributeMap::const_iterator oldit = old.begin(); oldit != old.end(); ++oldit) - { - QgsAttributeMap::iterator ait = oldattr.find( oldit.key() ); - if ( ait != oldattr.end() ) - { - // replace the committed value with the - // modified-but-not-committed value - old[index] = *ait; - } - - ++index; - } - } - - QApplication::restoreOverrideCursor(); - - // Show the attribute value editing dialog - QgsAttributeDialog ad( dataProvider->fields(), old ); - - if (ad.exec() == QDialog::Accepted) - { - - int i = 0; - for (QgsAttributeMap::const_iterator oldit = old.begin(); oldit != old.end(); ++oldit, ++i) - { - // only apply changed values if they were edited by the user - if (ad.isDirty(i)) - { - QgsDebugMsg("found a changed attribute: " + QString::number(i) + " = " + ad.value(i)); - - QgsAttributeMap& chattr = changedAttributes[ feat.featureId() ]; - chattr[i] = ad.value(i); - - // propagate "dirtyness" to the layer - layer->setModified(); - } - } - - } - } - else - { - QApplication::restoreOverrideCursor(); - QMessageBox::information(0, tr("No features found"), - tr("

No features were found within the search radius. " - "Note that it is currently not possible to use the " - "identify tool on unsaved features.

")); - } - } -} -#endif - - -void QgsMapToolIdentify::showError(QgsMapLayer * mapLayer) +void QgsMapToolIdentify::showError() { // QMessageBox::warning( // this, @@ -600,10 +407,10 @@ void QgsMapToolIdentify::showError(QgsMapLayer * mapLayer) // ); QgsMessageViewer * mv = new QgsMessageViewer(); - mv->setWindowTitle( mapLayer->errorCaptionString() ); + mv->setWindowTitle( mLayer->errorCaptionString() ); mv->setMessageAsPlainText( - QObject::tr("Could not identify objects on") + " " + mapLayer->name() + " " + QObject::tr("because") + ":\n" + - mapLayer->errorString() + QObject::tr("Could not identify objects on") + " " + mLayer->name() + " " + QObject::tr("because") + ":\n" + + mLayer->errorString() ); mv->exec(); // deletes itself on close } @@ -625,7 +432,7 @@ void QgsMapToolIdentify::deactivate() void QgsMapToolIdentify::highlightFeature(int featureId) { - QgsVectorLayer* layer = dynamic_cast(mCanvas->currentLayer()); + QgsVectorLayer* layer = dynamic_cast(mLayer); if (!layer) return; @@ -653,3 +460,41 @@ void QgsMapToolIdentify::highlightFeature(int featureId) mRubberBand->show(); } } + +void QgsMapToolIdentify::editFeature(int featureId) +{ + for(QgsFeatureList::iterator it=mFeatureList.begin(); it!=mFeatureList.end(); it++) + { + if( it->featureId() == featureId ) + { + editFeature( *it ); + break; + } + } +} + +void QgsMapToolIdentify::editFeature(QgsFeature &f) +{ + QgsVectorLayer* layer = dynamic_cast(mLayer); + if (!layer) + return; + + if (!layer->isEditable() ) + return; + + QgsAttributeMap src = f.attributeMap(); + + QgsAttributeDialog *ad = new QgsAttributeDialog(layer, &f); + if (ad->exec()) + { + const QgsAttributeMap &dst = f.attributeMap(); + + for(QgsAttributeMap::const_iterator it=dst.begin(); it!=dst.end(); it++) + { + if( !src.contains( it.key() ) || it.value()!=src[it.key()] ) + layer->changeAttributeValue( f.featureId(), it.key(), it.value().toString() ); + } + } + delete ad; + mCanvas->refresh(); +} diff --git a/src/app/qgsmaptoolidentify.h b/src/app/qgsmaptoolidentify.h index 8af1ea40143..e6b6d7064fe 100644 --- a/src/app/qgsmaptoolidentify.h +++ b/src/app/qgsmaptoolidentify.h @@ -19,6 +19,7 @@ #include "qgsmaptool.h" #include "qgspoint.h" +#include "qgsfeature.h" #include @@ -60,6 +61,9 @@ class QgsMapToolIdentify : public QgsMapTool public slots: //! creates rubberband on top of the feature to highlight it void highlightFeature(int featureId); + + //! edit a feature + void editFeature(int featureId); private: @@ -68,7 +72,7 @@ class QgsMapToolIdentify : public QgsMapTool * * \param point[in] The coordinate (as the CRS of the raster layer) */ - void identifyRasterLayer(QgsRasterLayer* layer, const QgsPoint& point); + void identifyRasterLayer(const QgsPoint& point); /** * \brief function for identifying a pixel in a OGC WMS raster layer @@ -78,18 +82,20 @@ class QgsMapToolIdentify : public QgsMapTool * \note WMS Servers prefer to receive coordinates in image space not CRS space, therefore * this special variant of identifyRasterLayer. */ - void identifyRasterWmsLayer(QgsRasterLayer* layer, const QgsPoint& point); + void identifyRasterWmsLayer(const QgsPoint& point); /** * \brief function for identifying features at a coordinate in a vector layer * * \param point[in] The coordinate (as the CRS of the vector layer) */ - void identifyVectorLayer(QgsVectorLayer* layer, const QgsPoint& point); + void identifyVectorLayer(const QgsPoint& point); //! show whatever error is exposed by the QgsMapLayer. - void showError(QgsMapLayer * mapLayer); + void showError(); + //! edit a feature + void editFeature(QgsFeature &f); //! Pointer to the identify results dialog for name/value pairs QgsIdentifyResults *mResults; @@ -97,6 +103,11 @@ class QgsMapToolIdentify : public QgsMapTool //! Rubber band for highlighting identified feature QgsRubberBand* mRubberBand; + QgsMapLayer *mLayer; + + //! list of identified features + QgsFeatureList mFeatureList; + private slots: // Let us know when the QgsIdentifyResults dialog box has been closed void resultsDialogGone(); diff --git a/src/app/qgsmaptoolmovefeature.cpp b/src/app/qgsmaptoolmovefeature.cpp index 5f8e5c788a0..71c36a4898c 100644 --- a/src/app/qgsmaptoolmovefeature.cpp +++ b/src/app/qgsmaptoolmovefeature.cpp @@ -56,65 +56,65 @@ void QgsMapToolMoveFeature::canvasPressEvent(QMouseEvent * e) QgsVectorLayer* vlayer = currentVectorLayer(); if(!vlayer) - { - return; - } + { + return; + } if(!vlayer->isEditable()) - { - QMessageBox::information(0, QObject::tr("Layer not editable"), \ - QObject::tr("Cannot edit the vector layer. To make it editable, go to the file item " \ - "of the layer, right click and check 'Allow Editing'.")); - return; - } + { + QMessageBox::information(0, QObject::tr("Layer not editable"), + QObject::tr("Cannot edit the vector layer. To make it editable, go to the file item " + "of the layer, right click and check 'Allow Editing'.")); + return; + } //find first geometry under mouse cursor and store iterator to it QgsPoint layerCoords = toLayerCoords((QgsMapLayer*)vlayer, e->pos()); QSettings settings; double searchRadius = settings.value("/qgis/digitizing/search_radius_vertex_edit", 10).toDouble(); - QgsRect selectRect(layerCoords.x()-searchRadius, layerCoords.y()-searchRadius, \ - layerCoords.x()+searchRadius, layerCoords.y()+searchRadius); + QgsRect selectRect(layerCoords.x()-searchRadius, layerCoords.y()-searchRadius, + layerCoords.x()+searchRadius, layerCoords.y()+searchRadius); - QList featureList; - vlayer->featuresInRectangle(selectRect, featureList, true, false); - - if(featureList.size() > 0) + vlayer->select(QgsAttributeList(), selectRect, true); + + //find the closest feature + QgsGeometry* pointGeometry = QgsGeometry::fromPoint(layerCoords); + if(!pointGeometry) + { + return; + } + + double minDistance = std::numeric_limits::max(); + + QgsFeature cf; + QgsFeature f; + while( vlayer->getNextFeature(f) ) + { + if(f.geometry()) { - //find the closest feature - QgsGeometry* pointGeometry = QgsGeometry::fromPoint(layerCoords); - if(!pointGeometry) - { - return; - } - - QList::iterator closestFeatureIt; - double minDistance = std::numeric_limits::max(); - double currentDistance; - - QList::iterator it = featureList.begin(); - for(; it != featureList.end(); ++it) - { - if(it->geometry()) - { - currentDistance = pointGeometry->distance(*(it->geometry())); - if(currentDistance < minDistance) - { - minDistance = currentDistance; - closestFeatureIt = it; - } - } - } - - mStartPointMapCoords = toMapCoords(e->pos()); - mMovedFeature = closestFeatureIt->featureId(); //todo: take the closest feature, not the first one... - mRubberBand = createRubberBand(); - mRubberBand->setToGeometry(closestFeatureIt->geometry(), *vlayer); - mRubberBand->setColor(Qt::red); - mRubberBand->setWidth(2); - mRubberBand->show(); - - delete pointGeometry; + double currentDistance = pointGeometry->distance(*f.geometry()); + if(currentDistance < minDistance) + { + minDistance = currentDistance; + cf = f; + } } + } + + if( minDistance==std::numeric_limits::max() ) + { + return; + } + + mStartPointMapCoords = toMapCoords(e->pos()); + mMovedFeature = cf.featureId(); //todo: take the closest feature, not the first one... + mRubberBand = createRubberBand(); + mRubberBand->setToGeometry( cf.geometry(), *vlayer); + mRubberBand->setColor(Qt::red); + mRubberBand->setWidth(2); + mRubberBand->show(); + + delete pointGeometry; } void QgsMapToolMoveFeature::canvasReleaseEvent(QMouseEvent * e) diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index a414eac1055..addfc72b05c 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -49,7 +49,6 @@ QgsOptions::QgsOptions(QWidget *parent, Qt::WFlags fl) : connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(this, SIGNAL(accepted()), this, SLOT(saveOptions())); - qparent = parent; // read the current browser and set it QSettings settings; #ifdef QGISDEBUG @@ -116,6 +115,7 @@ QgsOptions::QgsOptions(QWidget *parent, Qt::WFlags fl) : chkAddedVisibility->setChecked(settings.value("/qgis/new_layers_visible",true).toBool()); cbxLegendClassifiers->setChecked(settings.value("/qgis/showLegendClassifiers",false).toBool()); cbxHideSplash->setChecked(settings.value("/qgis/hideSplash",false).toBool()); + cbxAttributeTableDocked->setChecked(settings.value("/qgis/dockAttributeTable",false).toBool()); //set the colour for selections int myRed = settings.value("/qgis/default_selection_color_red",255).toInt(); @@ -239,7 +239,7 @@ void QgsOptions::themeChanged(const QString &newThemeName) { // Slot to change the theme as user scrolls through the choices QString newt = newThemeName; - ((QgisApp*)qparent)->setTheme(newt); + QgisApp::instance()->setTheme(newt); } QString QgsOptions::theme() { @@ -260,6 +260,7 @@ void QgsOptions::saveOptions() settings.setValue("/Map/identifyRadius", spinBoxIdentifyValue->value()); settings.setValue("/qgis/showLegendClassifiers",cbxLegendClassifiers->isChecked()); settings.setValue("/qgis/hideSplash",cbxHideSplash->isChecked()); + settings.setValue("/qgis/dockAttributeTable",cbxAttributeTableDocked->isChecked()); settings.setValue("/qgis/new_layers_visible",chkAddedVisibility->isChecked()); settings.setValue("/qgis/enable_anti_aliasing",chkAntiAliasing->isChecked()); settings.setValue("/qgis/use_qimage_to_render", !(chkUseQPixmap->isChecked())); diff --git a/src/app/qgsoptions.h b/src/app/qgsoptions.h index a121964d70c..a5e7eb6ee80 100644 --- a/src/app/qgsoptions.h +++ b/src/app/qgsoptions.h @@ -93,9 +93,6 @@ class QgsOptions :public QDialog, private Ui::QgsOptionsBase // QStringList i18nList(); - //! Pointer to our parent - QWidget *qparent; - //!Global default projection used for new layers added that have no projection long mGlobalSRSID; diff --git a/src/app/qgsserversourceselect.cpp b/src/app/qgsserversourceselect.cpp index ba6a22cb691..cc633b03855 100644 --- a/src/app/qgsserversourceselect.cpp +++ b/src/app/qgsserversourceselect.cpp @@ -43,10 +43,9 @@ static long DEFAULT_WMS_EPSG = 4326; // WGS 84 -QgsServerSourceSelect::QgsServerSourceSelect(QgisApp * app, QWidget * parent, Qt::WFlags fl) +QgsServerSourceSelect::QgsServerSourceSelect(QWidget * parent, Qt::WFlags fl) : QDialog(parent, fl), m_Epsg(DEFAULT_WMS_EPSG), - qgisApp(app), mWmsProvider(0) { setupUi(this); diff --git a/src/app/qgsserversourceselect.h b/src/app/qgsserversourceselect.h index 311bb680148..1c6bf60f939 100644 --- a/src/app/qgsserversourceselect.h +++ b/src/app/qgsserversourceselect.h @@ -44,7 +44,7 @@ class QgsServerSourceSelect : public QDialog, private Ui::QgsServerSourceSelectB public: //! Constructor - QgsServerSourceSelect(QgisApp *app, QWidget *parent = 0, Qt::WFlags fl = QgisGui::ModalDialogFlags); + QgsServerSourceSelect(QWidget *parent = 0, Qt::WFlags fl = QgisGui::ModalDialogFlags); //! Destructor ~QgsServerSourceSelect(); //! Populate the connection list combo box @@ -173,9 +173,6 @@ private: //! The mime type, the text to use in the button and a unique number QMap > m_PotentialFormats; - //! Pointer to the qgis application mainwindow - QgisApp *qgisApp; - //! The widget that controls the image format radio buttons QButtonGroup* m_imageFormatGroup; QHBoxLayout* m_imageFormatLayout; diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 60105ef2f53..b86832f7e15 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -19,6 +19,7 @@ #include +#include "qgisapp.h" #include "qgsapplication.h" #include "qgsattributeactiondialog.h" #include "qgscontexthelp.h" @@ -32,7 +33,6 @@ #include "qgsproject.h" #include "qgssinglesymboldialog.h" #include "qgsuniquevaluedialog.h" -#include "qgsvectordataprovider.h" #include "qgsvectorlayer.h" #include "qgsvectorlayerproperties.h" #include "qgsconfig.h" @@ -48,15 +48,18 @@ #include #include #include +#include +#include #if QT_VERSION < 0x040300 #define toPlainText() text() #endif -QgsVectorLayerProperties::QgsVectorLayerProperties(QgsVectorLayer * lyr, - QWidget * parent, - Qt::WFlags fl) +QgsVectorLayerProperties::QgsVectorLayerProperties( + QgsVectorLayer *lyr, + QWidget * parent, + Qt::WFlags fl) : QDialog(parent, fl), layer(lyr), mRendererDialog(0) @@ -66,6 +69,21 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(QgsVectorLayer * lyr, connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); connect(this, SIGNAL(accepted()), this, SLOT(apply())); + connect(mAddAttributeButton, SIGNAL(clicked()), this, SLOT(addAttribute())); + connect(mDeleteAttributeButton, SIGNAL(clicked()), this, SLOT(deleteAttribute())); + + connect(mToggleEditingButton, SIGNAL(clicked()), this, SLOT(toggleEditing())); + connect(this, SIGNAL(toggleEditing(QgsMapLayer*)), + QgisApp::instance(), SLOT(toggleEditing(QgsMapLayer*))); + + connect(layer, SIGNAL(editingStarted()), this, SLOT(editingToggled())); + connect(layer, SIGNAL(editingStopped()), this, SLOT(editingToggled())); + connect(layer, SIGNAL(attributeAdded(int)), this, SLOT(attributeAdded(int))); + connect(layer, SIGNAL(attributeDeleted(int)), this, SLOT(attributeDeleted(int))); + + mAddAttributeButton->setIcon(QgisApp::getThemeIcon("/mActionNewAttribute.png")); + mDeleteAttributeButton->setIcon(QgisApp::getThemeIcon("/mActionDeleteAttribute.png")); + mToggleEditingButton->setIcon(QgisApp::getThemeIcon("/mActionToggleEditing.png")); // Create the Label dialog tab QVBoxLayout *layout = new QVBoxLayout( labelOptionsFrame ); @@ -73,18 +91,31 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(QgsVectorLayer * lyr, labelDialog = new QgsLabelDialog ( layer->label(), labelOptionsFrame); layout->addWidget( labelDialog ); labelOptionsFrame->setLayout(layout); - connect(labelDialog, SIGNAL(labelSourceSet()), - this, SLOT(setLabelCheckBox())); + connect(labelDialog, SIGNAL(labelSourceSet()), this, SLOT(setLabelCheckBox())); // Create the Actions dialog tab - QgsVectorDataProvider *dp = dynamic_cast(layer->getDataProvider()); QVBoxLayout *actionLayout = new QVBoxLayout( actionOptionsFrame ); actionLayout->setMargin(0); - QgsFieldMap fields = dp->fields(); - actionDialog = new QgsAttributeActionDialog ( layer->actions(), fields, - actionOptionsFrame ); + const QgsFieldMap &fields = layer->pendingFields(); + actionDialog = new QgsAttributeActionDialog ( layer->actions(), fields, actionOptionsFrame ); actionLayout->addWidget( actionDialog ); + tblAttributes->setColumnCount(8); + tblAttributes->setRowCount( fields.size() ); + tblAttributes->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr("id") ) ); + tblAttributes->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr("name") ) ); + tblAttributes->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr("type") ) ); + tblAttributes->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr("length") ) ); + tblAttributes->setHorizontalHeaderItem( 4, new QTableWidgetItem( tr("precision") ) ); + tblAttributes->setHorizontalHeaderItem( 5, new QTableWidgetItem( tr("comment") ) ); + tblAttributes->setHorizontalHeaderItem( 6, new QTableWidgetItem( tr("edit widget") ) ); + tblAttributes->setHorizontalHeaderItem( 7, new QTableWidgetItem( tr("values") ) ); + + tblAttributes->setSelectionBehavior( QAbstractItemView::SelectRows ); + tblAttributes->setSelectionMode( QAbstractItemView::MultiSelection ); + + loadRows(); + reset(); if(layer->getDataProvider())//enable spatial index button group if supported by provider { @@ -95,18 +126,169 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(QgsVectorLayer * lyr, } } + updateButtons(); + leSpatialRefSys->setText(layer->srs().proj4String()); leSpatialRefSys->setCursorPosition(0); connect(sliderTransparency, SIGNAL(valueChanged(int)), this, SLOT(sliderTransparency_valueChanged(int))); + tabWidget->setCurrentIndex(0); } // QgsVectorLayerProperties ctor +void QgsVectorLayerProperties::loadRows() +{ + const QgsFieldMap &fields = layer->pendingFields(); + + int row=0; + for(QgsFieldMap::const_iterator it=fields.begin(); it!=fields.end(); it++, row++) + setRow( row, it.key(), it.value() ); + + tblAttributes->resizeColumnsToContents(); +} + +void QgsVectorLayerProperties::setRow(int row, int idx, const QgsField &field) +{ + tblAttributes->setItem(row, 0, new QTableWidgetItem( QString::number(idx) ) ); + tblAttributes->setItem(row, 1, new QTableWidgetItem( field.name() ) ); + tblAttributes->setItem(row, 2, new QTableWidgetItem( field.typeName() ) ); + tblAttributes->setItem(row, 3, new QTableWidgetItem( QString::number( field.length() ) ) ); + tblAttributes->setItem(row, 4, new QTableWidgetItem( QString::number( field.precision() ) ) ); + tblAttributes->setItem(row, 5, new QTableWidgetItem( field.comment() ) ); + + for(int i=0; i<6; i++) + tblAttributes->item(row, i)->setFlags( tblAttributes->item(row, i)->flags() & ~Qt::ItemIsEditable ); + + QComboBox *cb = new QComboBox(); + cb->addItem( tr("line edit"), QgsVectorLayer::LineEdit); + cb->addItem( tr("unique values"), QgsVectorLayer::UniqueValues); + cb->addItem( tr("unique values (editable)"), QgsVectorLayer::UniqueValuesEditable); + cb->addItem( tr("value map"), QgsVectorLayer::ValueMap); + cb->addItem( tr("classification"), QgsVectorLayer::Classification); + cb->addItem( tr("range"), QgsVectorLayer::Range); + cb->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); + cb->setCurrentIndex( layer->editType( idx ) ); + + tblAttributes->setCellWidget(row, 6, cb ); + + if( layer->editType(idx)==QgsVectorLayer::ValueMap ) + { + // TODO: create a gui for value maps + QStringList mapList; + QMap &map = layer->valueMap( idx ); + for(QMap::iterator mit=map.begin(); mit!=map.end(); mit++) + { + QgsDebugMsg( QString("idx:%1 key:%2 value:%3").arg( idx ).arg( mit.key() ).arg( mit.value().toString() ) ); + if( mit.value().isNull() ) + mapList << mit.key(); + else + mapList << QString( "%1=%2" ).arg( mit.key() ).arg( mit.value().toString() ); + } + + tblAttributes->setItem(row, 7, new QTableWidgetItem( mapList.join(";") ) ); + } else if( layer->editType(idx)==QgsVectorLayer::Range ) { + tblAttributes->setItem( + row, 7, + new QTableWidgetItem( QString("%1;%2;%3") + .arg( layer->range(idx).mMin.toString() ) + .arg( layer->range(idx).mMax.toString() ) + .arg( layer->range(idx).mStep.toString() ) + ) + ); + } +} + + QgsVectorLayerProperties::~QgsVectorLayerProperties() { - disconnect(labelDialog, SIGNAL(labelSourceSet()), - this, SLOT(setLabelCheckBox())); + disconnect(labelDialog, SIGNAL(labelSourceSet()), this, SLOT(setLabelCheckBox())); } + +void QgsVectorLayerProperties::toggleEditing() +{ + emit toggleEditing(layer); +} + +void QgsVectorLayerProperties::attributeAdded(int idx) +{ + const QgsFieldMap &fields = layer->pendingFields(); + int row = tblAttributes->rowCount(); + tblAttributes->insertRow(row); + setRow(row, idx, fields[idx]); + tblAttributes->setCurrentCell(row, idx, QItemSelectionModel::NoUpdate); +} + + +void QgsVectorLayerProperties::attributeDeleted(int idx) +{ + for(int i=0; irowCount(); i++) + { + if( tblAttributes->item(i, 0)->text().toInt()==idx ) + { + tblAttributes->removeRow(i); + break; + } + } +} + +void QgsVectorLayerProperties::addAttribute() +{ + QgsAddAttrDialog dialog(layer->getDataProvider(), this); + if(dialog.exec()==QDialog::Accepted) + { + if(!addAttribute(dialog.name(),dialog.type())) + { + QMessageBox::information(this,tr("Name conflict"),tr("The attribute could not be inserted. The name already exists in the table.")); + } + } +} + +bool QgsVectorLayerProperties::addAttribute(QString name, QString type) +{ + QgsDebugMsg("inserting attribute " + name + " of type " + type ); + return layer->addAttribute( name, type ); +} + +void QgsVectorLayerProperties::deleteAttribute() +{ + QList items = tblAttributes->selectedItems(); + QList idxs; + + for(QList::const_iterator it=items.begin(); it!=items.end(); it++) + { + if( (*it)->column()==0 ) + idxs << (*it)->text().toInt(); + } + + for(QList::const_iterator it=idxs.begin(); it!=idxs.end(); it++) + layer->deleteAttribute(*it); +} + +void QgsVectorLayerProperties::editingToggled() +{ + if( layer->isEditable() ) + loadRows(); + + updateButtons(); +} + +void QgsVectorLayerProperties::updateButtons() +{ + if ( layer->isEditable() ) + { + int cap = layer->getDataProvider()->capabilities(); + mAddAttributeButton->setEnabled( cap & QgsVectorDataProvider::AddAttributes ); + mDeleteAttributeButton->setEnabled( cap & QgsVectorDataProvider::DeleteAttributes ); + mToggleEditingButton->setChecked( true ); + } + else + { + mAddAttributeButton->setEnabled( false ); + mDeleteAttributeButton->setEnabled( false ); + mToggleEditingButton->setChecked( false ); + } +} + void QgsVectorLayerProperties::sliderTransparency_valueChanged(int theValue) { //set the transparency percentage label to a suitable value @@ -167,8 +349,6 @@ void QgsVectorLayerProperties::reset( void ) "layer is shown here. This is currently only supported for PostgreSQL " "layers. To enter or modify the query, click on the Query Builder button")); - //we are dealing with a pg layer here so that we can enable the sql box - QgsVectorDataProvider *dp = dynamic_cast(layer->getDataProvider()); //see if we are dealing with a pg layer here if(layer->providerType() == "postgres") { @@ -188,7 +368,7 @@ void QgsVectorLayerProperties::reset( void ) } //get field list for display field combo - const QgsFieldMap& myFields = dp->fields(); + const QgsFieldMap& myFields = layer->pendingFields(); for (QgsFieldMap::const_iterator it = myFields.begin(); it != myFields.end(); ++it) { displayFieldComboBox->addItem( it->name() ); @@ -249,7 +429,7 @@ void QgsVectorLayerProperties::reset( void ) SLOT(alterLayerDialog(const QString &))); // reset fields in label dialog - layer->label()->setFields ( layer->getDataProvider()->fields() ); + layer->label()->setFields ( layer->pendingFields() ); //set the metadata contents QString myStyle = QgsApplication::reportStyleSheet(); @@ -311,6 +491,69 @@ void QgsVectorLayerProperties::apply() layer->setLabelOn(labelCheckBox->isChecked()); layer->setLayerName(displayName()); + for(int i=0; irowCount(); i++) + { + int idx = tblAttributes->item(i, 0)->text().toInt(); + const QgsField &field = layer->pendingFields()[idx]; + QgsVectorLayer::EditType editType = layer->editType(idx); + + QComboBox *cb = dynamic_cast( tblAttributes->cellWidget(i, 6) ); + if(!cb) + continue; + layer->setEditType( idx, (QgsVectorLayer::EditType) cb->itemData( cb->currentIndex() ).toInt() ); + + if( editType==QgsVectorLayer::ValueMap ) + { + QMap &map = layer->valueMap(idx); + map.clear(); + + QString value = tblAttributes->item(i, 7)->text(); + if( !value.isEmpty() ) + { + QStringList values = tblAttributes->item(i, 7)->text().split(";"); + for(int j=0; jitem(i, 7)->text().split(";"); + + if( values.size()==3 ) { + QVariant min = values[0]; + QVariant max = values[1]; + QVariant step = values[2]; + + if( min.canConvert(field.type()) && + max.canConvert(field.type()) && + step.canConvert(field.type()) ) + { + min.convert( field.type() ); + max.convert( field.type() ); + step.convert( field.type() ); + layer->range(idx) = QgsVectorLayer::RangeData(min,max,step); + } + } + } + } QgsSingleSymbolDialog *sdialog = dynamic_cast < QgsSingleSymbolDialog * >(widgetStackRenderers->currentWidget()); @@ -354,11 +597,8 @@ void QgsVectorLayerProperties::on_pbnQueryBuilder_clicked() // launch the query builder using the PostgreSQL connection // from the provider - // get the data provider - QgsVectorDataProvider *dp = - dynamic_cast(layer->getDataProvider()); - // cast to postgres provider type - QgsPostgresProvider * myPGProvider = (QgsPostgresProvider *) dp; + // cast to postgres provider type + QgsPostgresProvider * myPGProvider = (QgsPostgresProvider *) layer->getDataProvider() ; // FIXME use dynamic cast // create the query builder object using the table name // and postgres connection from the provider QgsDataSourceURI uri(myPGProvider->dataSourceUri()); @@ -541,7 +781,7 @@ QString QgsVectorLayerProperties::getMetadata() } - +#if 0 // // Add the info about each field in the attribute table // @@ -569,8 +809,7 @@ QString QgsVectorLayerProperties::getMetadata() myMetadata += ""; //get info for each field by looping through them - QgsVectorDataProvider *myDataProvider = dynamic_cast(layer->getDataProvider()); - const QgsFieldMap& myFields = myDataProvider->fields(); + const QgsFieldMap& myFields = layer->pendingFields(); for (QgsFieldMap::const_iterator it = myFields.begin(); it != myFields.end(); ++it) { const QgsField& myField = *it; @@ -594,15 +833,17 @@ QString QgsVectorLayerProperties::getMetadata() //close field list myMetadata += ""; //end of nested table +#endif + myMetadata += ""; //end of stats container table row // // Close the table // myMetadata += ""; + myMetadata += ""; return myMetadata; - } diff --git a/src/app/qgsvectorlayerproperties.h b/src/app/qgsvectorlayerproperties.h index a0516051442..093c67df1e4 100644 --- a/src/app/qgsvectorlayerproperties.h +++ b/src/app/qgsvectorlayerproperties.h @@ -23,15 +23,20 @@ #include "ui_qgsvectorlayerpropertiesbase.h" #include "qgisgui.h" #include "qgsrenderer.h" +#include "qgsaddattrdialog.h" +#include "qgsdelattrdialog.h" +#include "qgsfield.h" + +class QgsMapLayer; class QgsAttributeActionDialog; class QgsLabelDialog; class QgsVectorLayer; - -class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPropertiesBase{ - Q_OBJECT - public: +class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPropertiesBase +{ + Q_OBJECT; +public: QgsVectorLayerProperties(QgsVectorLayer *lyr = 0,QWidget *parent = 0, Qt::WFlags fl = QgisGui::ModalDialogFlags); ~QgsVectorLayerProperties(); /**Sets the legend type to "single symbol", "graduated symbol" or "continuous color"*/ @@ -42,19 +47,42 @@ class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPrope /**Sets the attribute that is used in the Identify Results dialog box*/ void setDisplayField(QString name); - public slots: + /**Adds an attribute to the table (but does not commit it yet) + @param name attribute name + @param type attribute type + @return false in case of a name conflict, true in case of success*/ + bool addAttribute(QString name, QString type); + + /**Deletes an attribute (but does not commit it) + @param name attribute name + @return false in case of a non-existing attribute.*/ + bool deleteAttribute(int attr); + +public slots: + void alterLayerDialog(const QString& string); + /** Reset to original (vector layer) values */ void reset(); + /** Get metadata about the layer in nice formatted html */ QString getMetadata(); + /** Set transparency based on slider position */ void sliderTransparency_valueChanged(int theValue); + /** Toggles on the label check box */ void setLabelCheckBox(); + /** Called when apply button is pressed or dialog is accepted */ void apply(); + /** toggle editing of layer */ + void toggleEditing(); + + /** editing of layer was toggled */ + void editingToggled(); + // //methods reimplemented from qt designer base class // @@ -67,13 +95,21 @@ class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPrope void on_pbnSaveDefaultStyle_clicked(); void on_pbnLoadStyle_clicked(); void on_pbnSaveStyleAs_clicked(); - - signals: - - /** emitted when changes to layer were saved to update legend */ - void refreshLegend(QString layerID, bool expandItem); - protected: + void addAttribute(); + void deleteAttribute(); + + void attributeAdded(int idx); + void attributeDeleted(int idx); + +signals: + + /** emitted when changes to layer were saved to update legend */ + void refreshLegend(QString layerID, bool expandItem); + + void toggleEditing(QgsMapLayer *); + +protected: QgsVectorLayer *layer; /**Renderer dialog which is shown*/ QDialog* mRendererDialog; @@ -83,14 +119,19 @@ class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPrope QgsLabelDialog* labelDialog; /**Actions dialog. If apply is pressed, the actions are stored for later use */ QgsAttributeActionDialog* actionDialog; + + void updateButtons(); + void loadRows(); + void setRow(int row, int idx, const QgsField &field); + /**Buffer pixmap which takes the picture of renderers before they are assigned to the vector layer*/ //QPixmap bufferPixmap; static const int context_id = 94000531; + }; inline QString QgsVectorLayerProperties::displayName() { return txtDisplayName->text(); } - #endif diff --git a/src/core/qgsdistancearea.h b/src/core/qgsdistancearea.h index 543e998d6f4..f459e6b4148 100644 --- a/src/core/qgsdistancearea.h +++ b/src/core/qgsdistancearea.h @@ -34,9 +34,7 @@ General purpose distance and area calculator */ class CORE_EXPORT QgsDistanceArea { - public: - //! Constructor QgsDistanceArea(); diff --git a/src/core/qgsfeature.cpp b/src/core/qgsfeature.cpp index ff13ed2b0de..cd08b50b7a0 100644 --- a/src/core/qgsfeature.cpp +++ b/src/core/qgsfeature.cpp @@ -18,20 +18,12 @@ email : sherman at mrcc.com #include "qgsgeometry.h" #include "qgsrect.h" -#include -#include -#ifdef WIN32 -#include -#endif -#include -#include - /** \class QgsFeature * \brief Encapsulates a spatial feature with attributes */ QgsFeature::QgsFeature(int id, QString typeName) - : mFid(id), + : mFid(id), mGeometry(0), mOwnsGeometry(0), mValid(false), @@ -41,101 +33,40 @@ QgsFeature::QgsFeature(int id, QString typeName) // NOOP } -QgsFeature::QgsFeature( QgsFeature const & rhs, - const QgsChangedAttributesMap & changedAttributes, - const QgsGeometryMap & changedGeometries ) - : mFid( rhs.mFid ), - mValid( rhs.mValid ), - mDirty( rhs.mDirty ), - mTypeName( rhs.mTypeName ) - -{ - // copy attributes from rhs feature - mAttributes = rhs.mAttributes; - - if (changedAttributes.contains(mFid)) - { - // get map of changed attributes - const QgsAttributeMap& changed = changedAttributes[mFid]; - - // changet the attributes which were provided in the attribute map - for (QgsAttributeMap::const_iterator it = changed.begin(); it != changed.end(); ++it) - { - changeAttribute(it.key(), it.value()); - } - } - - if (changedGeometries.contains(mFid)) - { - // deep-copy geometry purely from changedGeometries - mGeometry = new QgsGeometry(changedGeometries[mFid]); - mOwnsGeometry = TRUE; - } - else - { - // copy geometry purely from rhs feature - if ( rhs.mGeometry ) - { - mGeometry = new QgsGeometry( *(rhs.mGeometry) ); - mOwnsGeometry = TRUE; - } - else - { - mGeometry = 0; - mOwnsGeometry = FALSE; - } - } - -} - - QgsFeature::QgsFeature( QgsFeature const & rhs ) - : mFid( rhs.mFid ), + : mFid( rhs.mFid ), mAttributes( rhs.mAttributes ), mValid( rhs.mValid ), mDirty( rhs.mDirty ), - mTypeName( rhs.mTypeName ) + mTypeName( rhs.mTypeName ), + mGeometry( 0 ), + mOwnsGeometry( false ) { // copy embedded geometry if ( rhs.mGeometry ) { - mGeometry = new QgsGeometry( *(rhs.mGeometry) ); - mOwnsGeometry = TRUE; + setGeometry( *rhs.mGeometry ); } - else - { - mGeometry = 0; - mOwnsGeometry = FALSE; - } - } QgsFeature & QgsFeature::operator=( QgsFeature const & rhs ) { if ( &rhs == this ) - { return *this; } + return *this; - mFid = rhs.mFid ; - mDirty = rhs.mDirty ; - mAttributes = rhs.mAttributes ; - mValid = rhs.mValid ; + mFid = rhs.mFid; + mDirty = rhs.mDirty; + mAttributes = rhs.mAttributes; + mValid = rhs.mValid; mTypeName = rhs.mTypeName; + mGeometry = 0; + mOwnsGeometry = false; - // copy embedded geometry - delete mGeometry; if ( rhs.mGeometry ) - { - mGeometry = new QgsGeometry( *(rhs.mGeometry) ); - mOwnsGeometry = TRUE; - } - else - { - mGeometry = 0; - mOwnsGeometry = FALSE; - } - + setGeometry( *rhs.mGeometry ); + return *this; } // QgsFeature::operator=( QgsFeature const & rhs ) @@ -144,13 +75,9 @@ QgsFeature & QgsFeature::operator=( QgsFeature const & rhs ) //! Destructor QgsFeature::~QgsFeature() { - // Destruct the attached geometry only if we still own it. - if ( (mOwnsGeometry) && (mGeometry) ) - { + if ( mOwnsGeometry && mGeometry ) delete mGeometry; - } - } /** @@ -197,17 +124,15 @@ void QgsFeature::changeAttribute(int field, QVariant attr) mAttributes[field] = attr; } - -QgsGeometry * QgsFeature::geometry() +QgsGeometry *QgsFeature::geometry() { return mGeometry; } - -QgsGeometry * QgsFeature::geometryAndOwnership() +QgsGeometry *QgsFeature::geometryAndOwnership() { - mOwnsGeometry = FALSE; - + mOwnsGeometry = false; + return mGeometry; } @@ -218,7 +143,6 @@ QgsGeometry * QgsFeature::geometryAndOwnership() void QgsFeature::setFeatureId(int id) { mFid = id; - } @@ -239,45 +163,29 @@ void QgsFeature::setTypeName(QString typeName) void QgsFeature::setGeometry(const QgsGeometry& geom) { - // Destruct the attached geometry only if we still own it, before assigning new one. - if ( (mOwnsGeometry) && (mGeometry) ) - { - delete mGeometry; - mGeometry = 0; - } - - mGeometry = new QgsGeometry(geom); - mOwnsGeometry = TRUE; + setGeometry( new QgsGeometry(geom) ); } void QgsFeature::setGeometry(QgsGeometry* geom) { // Destruct the attached geometry only if we still own it, before assigning new one. - if ( (mOwnsGeometry) && (mGeometry) ) + if ( mOwnsGeometry && mGeometry ) { delete mGeometry; mGeometry = 0; } - + mGeometry = geom; - mOwnsGeometry = TRUE; + mOwnsGeometry = true; } /** Set the pointer to the feature geometry */ void QgsFeature::setGeometryAndOwnership(unsigned char *geom, size_t length) { - // Destruct the attached geometry only if we still own it, before assigning new one. - if ( (mOwnsGeometry) && (mGeometry) ) - { - delete mGeometry; - mGeometry = 0; - } - - mGeometry = new QgsGeometry(); - mGeometry->setWkbAndOwnership(geom, length); - mOwnsGeometry = TRUE; - + QgsGeometry *g = new QgsGeometry(); + g->setWkbAndOwnership(geom, length); + setGeometry(g); } @@ -298,5 +206,5 @@ bool QgsFeature::isDirty() const void QgsFeature::resetDirty() { - mDirty = FALSE; + mDirty = false; } diff --git a/src/core/qgsfeature.h b/src/core/qgsfeature.h index 8cfd5d6c382..d241d29966a 100644 --- a/src/core/qgsfeature.h +++ b/src/core/qgsfeature.h @@ -20,10 +20,11 @@ email : sherman at mrcc.com #include #include #include +#include class QgsGeometry; class QgsRect; - +class QgsFeature; // key = field index, value = field value typedef QMap QgsAttributeMap; @@ -37,6 +38,7 @@ typedef QMap QgsGeometryMap; // key = field index, value = field name typedef QMap QgsFieldNameMap; +typedef QList QgsFeatureList; /** * @class QgsFeature - Feature attribute class. @@ -44,25 +46,12 @@ typedef QMap QgsFieldNameMap; * * @author Gary E.Sherman */ -class CORE_EXPORT QgsFeature { - +class CORE_EXPORT QgsFeature +{ public: - //! Constructor QgsFeature(int id = 0, QString typeName = "" ); - /** create a copy of this feature in its uncommitted state. - To do this, you also pass in a reference to the feature's - layer's uncommitted attribute and geometry changes. - The resulting feature will have those changes applied. - - This is useful in the cut/copy routine, where you'd - want a copy of the "current" feature, not the on-disk feature. - */ - QgsFeature( const QgsFeature & rhs, - const QgsChangedAttributesMap & changedAttributes, - const QgsGeometryMap & changedGeometries ); - /** copy ctor needed due to internal pointer */ QgsFeature( QgsFeature const & rhs ); @@ -140,17 +129,17 @@ class CORE_EXPORT QgsFeature { * You would normally do this after it's saved to permanent storage (e.g. disk, an ACID-compliant database) */ void resetDirty(); - + /** * Get the geometry object associated with this feature */ - QgsGeometry * geometry(); + QgsGeometry *geometry(); /** * Get the geometry object associated with this feature * The caller assumes responsibility for the QgsGeometry*'s destruction. */ - QgsGeometry * geometryAndOwnership(); + QgsGeometry *geometryAndOwnership(); /** Set this feature's geometry from another QgsGeometry object (deep copy) */ @@ -174,17 +163,17 @@ class CORE_EXPORT QgsFeature { /** map of attributes accessed by field index */ QgsAttributeMap mAttributes; - + /** pointer to geometry in binary WKB format This is usually set by a call to OGRGeometry::exportToWkb() */ QgsGeometry* mGeometry; - + /** Indicator if the mGeometry is owned by this QgsFeature. If so, this QgsFeature takes responsibility for the mGeometry's destruction. - */ - bool mOwnsGeometry; + */ + bool mOwnsGeometry; //! Flag to indicate if this feature is valid // TODO: still applies? [MD] @@ -200,4 +189,5 @@ class CORE_EXPORT QgsFeature { }; // class QgsFeature + #endif diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index cce55f616fe..1f610a4ee3d 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -145,4 +145,7 @@ private: }; // class QgsField +// key = field index, value=field data +typedef QMap QgsFieldMap; + #endif diff --git a/src/core/qgslabel.cpp b/src/core/qgslabel.cpp index 7a0de926619..a21c4de17f1 100644 --- a/src/core/qgslabel.cpp +++ b/src/core/qgslabel.cpp @@ -482,7 +482,7 @@ QgsLabelAttributes *QgsLabel::layerAttributes ( void ) void QgsLabel::labelPoint ( std::vector& points, QgsFeature & feature ) { - QgsGeometry* geometry = feature.geometry(); + QgsGeometry *geometry = feature.geometry(); unsigned char *geom = geometry->wkbBuffer(); size_t geomlen = geometry->wkbSize(); QGis::WKBTYPE wkbType = geometry->wkbType(); diff --git a/src/core/qgslabel.h b/src/core/qgslabel.h index ba2881b2ac1..72b30ce8c44 100644 --- a/src/core/qgslabel.h +++ b/src/core/qgslabel.h @@ -83,7 +83,7 @@ public: void renderLabel ( QPainter* painter, const QgsRect& viewExtent, const QgsCoordinateTransform* coordTransform, const QgsMapToPixel *transform, - QgsFeature &feature, bool selected, QgsLabelAttributes *classAttributes=0, double sizeScale = 1.); + QgsFeature &feature, bool selected, QgsLabelAttributes *classAttributes=0, double sizeScale = 1.); /** Reads the renderer configuration from an XML file @param rnode the DOM node to read @@ -132,7 +132,7 @@ private: int width, int height, int alignment); /** Get label point for simple feature in map units */ - void labelPoint ( std::vector&, QgsFeature & feature ); + void labelPoint ( std::vector&, QgsFeature &feature ); /** Get label point for the given feature in wkb format. */ unsigned char* labelPoint( QgsPoint& point, unsigned char* wkb, size_t wkblen); diff --git a/src/core/qgslogger.h b/src/core/qgslogger.h index dbcd581cfe7..6440015a3c8 100644 --- a/src/core/qgslogger.h +++ b/src/core/qgslogger.h @@ -57,32 +57,32 @@ class CORE_EXPORT QgsLogger static void debug(const QString& var, double val, int debuglevel = 1, const char* file = NULL, const char* function = NULL, int line = -1); /**Prints out a variable/value pair for types with overloaded operator<<*/ - template static void debug(const QString& var, T val, const char* file = 0, const char* function = 0, \ + template static void debug(const QString& var, T val, const char* file = 0, const char* function = 0, int line = -1, int debuglevel = 1) + { + const char* dfile = debugFile(); + if(dfile) //exit if QGIS_DEBUG_FILE is set and the message comes from the wrong file { - const char* dfile = debugFile(); - if(dfile) //exit if QGIS_DEBUG_FILE is set and the message comes from the wrong file - { - if(!file || strcmp(dfile, file) != 0) - { - return; - } - } - std::ostringstream os; - os << var.toLocal8Bit().data() << " = " << val; - if(line == -1) - { - qDebug("%s: (%s) %s", file, function, os.str().c_str()); - } - else - { -#if defined(_MSC_VER) - qDebug("%s(%d): (%s) %s", file, line, function, os.str().c_str()); -#else - qDebug("%s: %d: (%s) %s", file, line, function, os.str().c_str()); -#endif - } + if(!file || strcmp(dfile, file) != 0) + { + return; + } } + std::ostringstream os; + os << var.toLocal8Bit().data() << " = " << val; + if(line == -1) + { + qDebug("%s: (%s) %s", file, function, os.str().c_str()); + } + else + { +#if defined(_MSC_VER) + qDebug("%s(%d): (%s) %s", file, line, function, os.str().c_str()); +#else + qDebug("%s: %d: (%s) %s", file, line, function, os.str().c_str()); +#endif + } + } /**Goes to qWarning*/ static void warning(const QString& msg); diff --git a/src/core/qgssearchtreenode.h b/src/core/qgssearchtreenode.h index c386adb1848..36292fed9da 100644 --- a/src/core/qgssearchtreenode.h +++ b/src/core/qgssearchtreenode.h @@ -24,11 +24,10 @@ #include #include -class QgsSearchTreeValue; -class QgsField; +#include +#include -typedef QMap QgsFieldMap; -typedef QMap QgsAttributeMap; +class QgsSearchTreeValue; /** * QgsSearchTreeNode diff --git a/src/core/qgssnapper.cpp b/src/core/qgssnapper.cpp index cdf9cf391b1..2a4ff63413b 100644 --- a/src/core/qgssnapper.cpp +++ b/src/core/qgssnapper.cpp @@ -24,7 +24,7 @@ #include -QgsSnapper::QgsSnapper(QgsMapRenderer* mapRender): mMapRenderer(mapRender) +QgsSnapper::QgsSnapper(QgsMapRenderer* mapRenderer): mMapRenderer(mapRenderer) { } diff --git a/src/core/qgsvectordataprovider.cpp b/src/core/qgsvectordataprovider.cpp index c0b8376a294..1f4a0f54c28 100644 --- a/src/core/qgsvectordataprovider.cpp +++ b/src/core/qgsvectordataprovider.cpp @@ -68,7 +68,7 @@ QString QgsVectorDataProvider::dataComment() const return QString(); } -bool QgsVectorDataProvider::addFeatures(QgsFeatureList & flist) +bool QgsVectorDataProvider::addFeatures(QgsFeatureList &flist) { return false; } @@ -220,7 +220,7 @@ QString QgsVectorDataProvider::capabilitiesString() const int QgsVectorDataProvider::indexFromFieldName(const QString& fieldName) const { - const QgsFieldMap& theFields = fields(); + const QgsFieldMap &theFields = fields(); for (QgsFieldMap::const_iterator it = theFields.begin(); it != theFields.end(); ++it) { @@ -248,7 +248,7 @@ void QgsVectorDataProvider::setFetchFeaturesWithoutGeom(bool fetch) mFetchFeaturesWithoutGeom = fetch; } -const QSet& QgsVectorDataProvider::supportedNativeTypes() const +const QgsNativeTypeMap &QgsVectorDataProvider::supportedNativeTypes() const { return mSupportedNativeTypes; } @@ -261,15 +261,15 @@ QVariant QgsVectorDataProvider::minValue(int index) QgsDebugMsg("Warning: access requested to invalid field index: " + QString::number(index)); return QVariant(); } - + if (mCacheMinMaxDirty) { fillMinMaxCache(); } - + if (!mCacheMinValues.contains(index)) return QVariant(); - + return mCacheMinValues[index]; } @@ -280,7 +280,7 @@ QVariant QgsVectorDataProvider::maxValue(int index) QgsDebugMsg("Warning: access requested to invalid field index: " + QString::number(index)); return QVariant(); } - + if (mCacheMinMaxDirty) { fillMinMaxCache(); diff --git a/src/core/qgsvectordataprovider.h b/src/core/qgsvectordataprovider.h index 6f73bc78508..93b9b3a56ae 100644 --- a/src/core/qgsvectordataprovider.h +++ b/src/core/qgsvectordataprovider.h @@ -26,9 +26,11 @@ class QTextCodec; //QGIS Includes #include "qgis.h" #include "qgsdataprovider.h" - #include "qgsvectorlayer.h" +#include "qgsfield.h" +typedef QMap QgsNewAttributesMap; +typedef QMap QgsNativeTypeMap; /** Base class for vector data providers */ @@ -137,7 +139,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider * Return a map of indexes with field names for this layer * @return map of fields */ - virtual const QgsFieldMap & fields() const = 0; + virtual const QgsFieldMap &fields() const = 0; /** * Return a short comment for the data that this provider is @@ -181,14 +183,14 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider * Adds a list of features * @return true in case of success and false in case of failure */ - virtual bool addFeatures(QgsFeatureList & flist); + virtual bool addFeatures(QgsFeatureList &flist); /** * Deletes a feature * @param id list containing feature ids to delete * @return true in case of success and false in case of failure */ - virtual bool deleteFeatures(const QgsFeatureIds & id); + virtual bool deleteFeatures(const QgsFeatureIds &id); /** * Adds new attributes @@ -246,24 +248,24 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider * Set encoding used for accessing data from layer */ virtual void setEncoding(const QString& e); - + /** * Get encoding which is used for accessing data */ QString encoding() const; - + /** * Returns the index of a field name or -1 if the field does not exist */ int indexFromFieldName(const QString& fieldName) const; - + /** * Return list of indexes to fetch all attributes in getNextFeature() */ virtual QgsAttributeList allAttributesList(); /**Returns the names of the numerical types*/ - const QSet& supportedNativeTypes() const; + const QgsNativeTypeMap &supportedNativeTypes() const; /** * Set whether provider should return also features that don't have @@ -272,13 +274,12 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider void setFetchFeaturesWithoutGeom(bool fetch); protected: - + void fillMinMaxCache(); - + bool mCacheMinMaxDirty; QMap mCacheMinValues, mCacheMaxValues; - /** Encoding */ QTextCodec* mEncoding; @@ -292,7 +293,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider QgsAttributeList mAttributesToFetch; /**The names of the providers native types*/ - QSet mSupportedNativeTypes; + QgsNativeTypeMap mSupportedNativeTypes; }; #endif diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 4e9c174a5a7..117928d339e 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -219,7 +219,7 @@ bool QgsVectorFileWriter::addFeature(QgsFeature& feature) } // build geometry from WKB - QgsGeometry* geom = feature.geometry(); + QgsGeometry *geom = feature.geometry(); if (geom->wkbType() != mWkbType) { @@ -234,7 +234,7 @@ bool QgsVectorFileWriter::addFeature(QgsFeature& feature) OGRGeometryH mGeom2 = createEmptyGeometry(geom->wkbType()); - OGRErr err = OGR_G_ImportFromWkb(mGeom2,geom->wkbBuffer(), geom->wkbSize()); + OGRErr err = OGR_G_ImportFromWkb(mGeom2, geom->wkbBuffer(), geom->wkbSize()); if (err != OGRERR_NONE) { QgsDebugMsg("Failed to import geometry from WKB: " + QString::number(err)); @@ -247,7 +247,7 @@ bool QgsVectorFileWriter::addFeature(QgsFeature& feature) } else { - OGRErr err = OGR_G_ImportFromWkb(mGeom,geom->wkbBuffer(), geom->wkbSize()); + OGRErr err = OGR_G_ImportFromWkb(mGeom, geom->wkbBuffer(), geom->wkbSize()); if (err != OGRERR_NONE) { QgsDebugMsg("Failed to import geometry from WKB: " + QString::number(err)); diff --git a/src/core/qgsvectorfilewriter.h b/src/core/qgsvectorfilewriter.h index ab4b8e18b88..c3d772dc421 100644 --- a/src/core/qgsvectorfilewriter.h +++ b/src/core/qgsvectorfilewriter.h @@ -21,8 +21,7 @@ #define _QGSVECTORFILEWRITER_H_ #include "qgsvectorlayer.h" - -#include +#include "qgsfield.h" typedef void *OGRDataSourceH; typedef void *OGRLayerH; @@ -41,7 +40,7 @@ class QTextCodec; class CORE_EXPORT QgsVectorFileWriter { public: - + enum WriterError { NoError = 0, @@ -64,36 +63,36 @@ class CORE_EXPORT QgsVectorFileWriter const QgsFieldMap& fields, QGis::WKBTYPE geometryType, const QgsSpatialRefSys* srs); - + /** checks whether there were any errors in constructor */ WriterError hasError(); - + /** add feature to the currently opened shapefile */ bool addFeature(QgsFeature& feature); - + /** close opened shapefile for writing */ ~QgsVectorFileWriter(); - + /** Delete a shapefile (and its accompanying shx / dbf / prf) * @param QString theFileName - /path/to/file.shp * @return bool true if the file was deleted successfully */ static bool deleteShapeFile(QString theFileName); protected: - + OGRGeometryH createEmptyGeometry(QGis::WKBTYPE wkbType); - + OGRDataSourceH mDS; OGRLayerH mLayer; OGRGeometryH mGeom; - + QgsFieldMap mFields; - + /** contains error value if construction was not successfull */ WriterError mError; QTextCodec* mCodec; - + /** geometry type which is being used */ QGis::WKBTYPE mWkbType; }; diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 4e82ee59c38..7d68f6a4d30 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -8,7 +8,7 @@ begin : Oct 29, 2003 copyright : (C) 2003 by Gary E.Sherman email : sherman at mrcc.com - + ***************************************************************************/ /*************************************************************************** @@ -97,10 +97,12 @@ QgsVectorLayer::QgsVectorLayer(QString vectorLayerPath, mModified(false), mRenderer(0), mLabel(0), - mLabelOn(false) + mLabelOn(false), + mMaxUpdatedIndex(-1), + mFetching(false) { mActions = new QgsAttributeAction; - + // if we're given a provider type, try to create and bind one to this layer if ( ! mProviderKey.isEmpty() ) { @@ -111,7 +113,6 @@ QgsVectorLayer::QgsVectorLayer(QString vectorLayerPath, // check if there is a default style / propertysheet defined // for this layer and if so apply it // - // if ( loadDefaultStyleFlag ) { bool defaultLoadedFlag = false; @@ -148,6 +149,8 @@ QgsVectorLayer::~QgsVectorLayer() { QgsDebugMsg("In QgsVectorLayer destructor"); + emit layerDeleted(); + mValid=false; if (mRenderer) @@ -156,7 +159,7 @@ QgsVectorLayer::~QgsVectorLayer() } // delete the provider object delete mDataProvider; - + delete mLabel; // Destroy any cached geometries and clear the references to them @@ -218,15 +221,14 @@ void QgsVectorLayer::setDisplayField(QString fldName) } else { - QgsFieldMap fields = mDataProvider->fields(); + const QgsFieldMap &fields = pendingFields(); int fieldsSize = fields.size(); - for (QgsFieldMap::iterator it = fields.begin(); it != fields.end(); ++it) + for (QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it) { - QString fldName = it.value().name(); QgsDebugMsg("Checking field " + fldName + " of " + QString::number(fieldsSize) + " total"); - + // Check the fields and keep the first one that matches. // We assume that the user has organized the data with the // more "interesting" field names first. As such, name should @@ -304,39 +306,23 @@ void QgsVectorLayer::drawLabels(QPainter * p, const QgsRect& viewExtent, const Q QgsDebugMsg("Selecting features based on view extent"); int featureCount = 0; - // select the records in the extent. The provider sets a spatial filter - // and sets up the selection set for retrieval - mDataProvider->select(attributes, viewExtent); try { - QgsFeature fet; + // select the records in the extent. The provider sets a spatial filter + // and sets up the selection set for retrieval + select(attributes, viewExtent, true); - // main render loop - while(mDataProvider->getNextFeature(fet)) + QgsFeature fet; + while( getNextFeature(fet) ) { - // don't render labels of deleted features - if (!mDeletedFeatureIds.contains(fet.featureId())) + if(mRenderer->willRenderFeature(&fet)) { - if(mRenderer->willRenderFeature(&fet)) - { - bool sel = mSelectedFeatureIds.contains(fet.featureId()); - mLabel->renderLabel ( p, viewExtent, ct, - theMapToPixelTransform, fet, sel, 0, scale); - } + bool sel = mSelectedFeatureIds.contains(fet.featureId()); + mLabel->renderLabel( p, viewExtent, ct, theMapToPixelTransform, fet, sel, 0, scale); } featureCount++; } - - // render labels of not-commited features - for (QgsFeatureList::iterator it = mAddedFeatures.begin(); it != mAddedFeatures.end(); ++it) - { - if(mRenderer->willRenderFeature(&(*it))) - { - bool sel = mSelectedFeatureIds.contains((*it).featureId()); - mLabel->renderLabel ( p, viewExtent, ct, theMapToPixelTransform, *it, sel, 0, scale); - } - } } catch (QgsCsException &e) { @@ -347,7 +333,7 @@ void QgsVectorLayer::drawLabels(QPainter * p, const QgsRect& viewExtent, const Q #ifdef QGISDEBUG QgsLogger::debug("Total features processed", featureCount, 1, __FILE__, __FUNCTION__, __LINE__); #endif - + // XXX Something in our draw event is triggering an additional draw event when resizing [TE 01/26/06] // XXX Calling this will begin processing the next draw event causing image havoc and recursion crashes. //qApp->processEvents(); @@ -356,7 +342,8 @@ void QgsVectorLayer::drawLabels(QPainter * p, const QgsRect& viewExtent, const Q } -unsigned char* QgsVectorLayer::drawLineString(unsigned char* feature, +unsigned char* QgsVectorLayer::drawLineString( + unsigned char *feature, QPainter* p, const QgsMapToPixel* mtp, const QgsCoordinateTransform* ct, @@ -366,21 +353,21 @@ unsigned char* QgsVectorLayer::drawLineString(unsigned char* feature, unsigned int wkbType = *((int*)(feature+1)); unsigned int nPoints = *((int*)ptr); ptr = feature + 9; - + bool hasZValue = (wkbType == QGis::WKBLineString25D); std::vector x(nPoints); std::vector y(nPoints); std::vector z(nPoints, 0.0); - - // Extract the points from the WKB format into the x and y vectors. + + // Extract the points from the WKB format into the x and y vectors. for (register unsigned int i = 0; i < nPoints; ++i) { x[i] = *((double *) ptr); ptr += sizeof(double); y[i] = *((double *) ptr); ptr += sizeof(double); - + if (hasZValue) // ignore Z value ptr += sizeof(double); } @@ -395,8 +382,9 @@ unsigned char* QgsVectorLayer::drawLineString(unsigned char* feature, // Look through the x and y coordinates and see if there are any // that need trimming. If one is found, there's no need to look at - // the rest of them so end the loop at that point. + // the rest of them so end the loop at that point. for (register unsigned int i = 0; i < nPoints; ++i) + { if (std::abs(x[i]) > QgsClipper::maxX || std::abs(y[i]) > QgsClipper::maxY) { @@ -404,6 +392,7 @@ unsigned char* QgsVectorLayer::drawLineString(unsigned char* feature, nPoints = x.size(); // trimming may change nPoints. break; } + } #endif // set up QPolygonF class with transformed points @@ -413,20 +402,21 @@ unsigned char* QgsVectorLayer::drawLineString(unsigned char* feature, pa[i].setX(x[i]); pa[i].setY(y[i]); } - + #ifdef QGISDEBUGVERBOSE // this is only used for verbose debug output for (int i = 0; i < pa.size(); ++i) - { - QgsDebugMsgLevel("pa" + QString::number(pa.point(i).x()), 2); - QgsDebugMsgLevel("pa" + QString::number(pa.point(i).y()), 2); - } + { + QgsDebugMsgLevel("pa" + QString::number(pa.point(i).x()), 2); + QgsDebugMsgLevel("pa" + QString::number(pa.point(i).y()), 2); + } #endif // The default pen gives bevelled joins between segements of the // polyline, which is good enough for the moment. //preserve a copy of the pen before we start fiddling with it QPen pen = p->pen(); // to be kept original + // // experimental alpha transparency // 255 = opaque @@ -436,39 +426,37 @@ unsigned char* QgsVectorLayer::drawLineString(unsigned char* feature, //only set transparency from layer level if renderer does not provide //transparency on class level if(!mRenderer->usesTransparency()) - { - myColor.setAlpha(mTransparencyLevel); - } + { + myColor.setAlpha(mTransparencyLevel); + } myTransparentPen.setColor(myColor); p->setPen(myTransparentPen); p->drawPolyline(pa); // draw vertex markers if in editing mode, but only to the main canvas - if ( - (mEditable) && - (drawingToEditingCanvas) - ) + if ( mEditable && drawingToEditingCanvas ) { QgsVectorLayer::VertexMarkerType markerType = currentVertexMarkerType(); - + std::vector::const_iterator xIt; std::vector::const_iterator yIt; for(xIt = x.begin(), yIt = y.begin(); xIt != x.end(); ++xIt, ++yIt) - { - drawVertexMarker((int)(*xIt), (int)(*yIt), *p, markerType); - } + { + drawVertexMarker((int)(*xIt), (int)(*yIt), *p, markerType); } + } //restore the pen p->setPen(pen); - + return ptr; } -unsigned char* QgsVectorLayer::drawPolygon(unsigned char* feature, - QPainter* p, - const QgsMapToPixel* mtp, - const QgsCoordinateTransform* ct, +unsigned char *QgsVectorLayer::drawPolygon( + unsigned char *feature, + QPainter *p, + const QgsMapToPixel *mtp, + const QgsCoordinateTransform *ct, bool drawingToEditingCanvas) { typedef std::pair, std::vector > ringType; @@ -493,26 +481,19 @@ unsigned char* QgsVectorLayer::drawPolygon(unsigned char* feature, ringsType rings; // Set pointer to the first ring - unsigned char* ptr = feature + 1 + 2 * sizeof(int); + unsigned char* ptr = feature + 1 + 2 * sizeof(int); for (register unsigned int idx = 0; idx < numRings; idx++) { unsigned int nPoints = *((int*)ptr); - ringTypePtr ring = new ringType(std::vector(nPoints), - std::vector(nPoints)); + ringTypePtr ring = new ringType(std::vector(nPoints), std::vector(nPoints)); ptr += 4; // create a dummy vector for the z coordinate std::vector zVector(nPoints, 0.0); // Extract the points from the WKB and store in a pair of // vectors. - /* -#ifdef QGISDEBUG -std::cerr << "Points for ring " << idx << " (" -<< nPoints << " points)\n"; -#endif -*/ for (register unsigned int jdx = 0; jdx < nPoints; jdx++) { ring->first[jdx] = *((double *) ptr); @@ -522,17 +503,10 @@ std::cerr << "Points for ring " << idx << " (" if (hasZValue) ptr += sizeof(double); - - /* -#ifdef QGISDEBUG -std::cerr << jdx << ": " -<< ring->first[jdx] << ", " << ring->second[jdx] << '\n'; -#endif -*/ } // If ring has fewer than two points, what is it then? // Anyway, this check prevents a crash - if (nPoints < 1) + if (nPoints < 1) { QgsDebugMsg("Ring has only " + QString::number(nPoints) + " points! Skipping this ring."); continue; @@ -545,26 +519,16 @@ std::cerr << jdx << ": " // Look through the x and y coordinates and see if there are any // that need trimming. If one is found, there's no need to look at - // the rest of them so end the loop at that point. + // the rest of them so end the loop at that point. for (register unsigned int i = 0; i < nPoints; ++i) { if (std::abs(ring->first[i]) > QgsClipper::maxX || std::abs(ring->second[i]) > QgsClipper::maxY) { QgsClipper::trimFeature(ring->first, ring->second, false); - /* -#ifdef QGISDEBUG -std::cerr << "Trimmed points (" << ring->first.size() << ")\n"; -for (int i = 0; i < ring->first.size(); ++i) -std::cerr << i << ": " << ring->first[i] -<< ", " << ring->second[i] << '\n'; -#endif -*/ break; } - //std::cout << "POLYGONTRANSFORM: " << ring->first[i] << ", " << ring->second[i] << std::endl; } - #endif // Don't bother keeping the ring if it has been trimmed out of @@ -615,7 +579,7 @@ std::cerr << i << ": " << ring->first[i] } #ifdef QGISDEBUGVERBOSE - // this is only for verbose debug output -- no optimzation is + // this is only for verbose debug output -- no optimzation is // needed :) QgsDebugMsg("Pixel points are:"); for (int i = 0; i < pa.size(); ++i) @@ -688,17 +652,13 @@ std::cerr << i << ": " << ring->first[i] // // draw the polygon - // + // p->drawPath(path); // draw vertex markers if in editing mode, but only to the main canvas - if ( - (mEditable) && - (drawingToEditingCanvas) - ) + if ( mEditable && drawingToEditingCanvas ) { - QgsVectorLayer::VertexMarkerType markerType = currentVertexMarkerType(); for(int i = 0; i < path.elementCount(); ++i) @@ -725,7 +685,7 @@ bool QgsVectorLayer::draw(QgsRenderContext& renderContext) QSettings settings; mUpdateThreshold = settings.value("Map/updateThreshold", 0).toInt(); //draw ( p, viewExtent, theMapToPixelTransform, ct, drawingToEditingCanvas, 1., 1.); - + if (mRenderer) { // painter is active (begin has been called @@ -734,7 +694,7 @@ bool QgsVectorLayer::draw(QgsRenderContext& renderContext) 2. read WKB for a feature 3. transform 4. draw - */ + */ QPen pen; /*Pointer to a marker image*/ @@ -744,124 +704,84 @@ bool QgsVectorLayer::draw(QgsRenderContext& renderContext) /* We set this to the symbolScale, and if it is NOT changed, */ /* we don't have to do another scaling here */ double markerScaleFactor = renderContext.rasterScaleFactor(); - + if(mEditable) { // Destroy all cached geometries and clear the references to them deleteCachedGeometries(); } - mDataProvider->updateFeatureCount(); - int totalFeatures = mDataProvider->featureCount(); - int featureCount = 0; - QgsFeature fet; - - QgsAttributeList attributes = mRenderer->classificationAttributes(); - mDataProvider->select(attributes, renderContext.extent()); + updateFeatureCount(); + int totalFeatures = pendingFeatureCount(); + int featureCount = 0; - try + QgsFeature fet; + QgsAttributeList attributes = mRenderer->classificationAttributes(); + select(attributes, renderContext.extent(), true); + + try { - while (mDataProvider->getNextFeature(fet)) + while( getNextFeature(fet) ) { - if(renderContext.renderingStopped()) - { - break; - } + if(renderContext.renderingStopped()) + break; #ifndef Q_WS_MAC //MH: disable this on Mac for now to avoid problems with resizing - if(mUpdateThreshold > 0 && 0 == featureCount % mUpdateThreshold) - { - emit screenUpdateRequested(); - emit drawingProgress(featureCount, totalFeatures); - qApp->processEvents(); - } - else if(featureCount % 1000 == 0) - { - emit drawingProgress(featureCount, totalFeatures); - qApp->processEvents(); - } + if(mUpdateThreshold > 0 && 0 == featureCount % mUpdateThreshold) + { + emit screenUpdateRequested(); + emit drawingProgress(featureCount, totalFeatures); + qApp->processEvents(); + } + else if(featureCount % 1000 == 0) + { + emit drawingProgress(featureCount, totalFeatures); + qApp->processEvents(); + } #endif //Q_WS_MAC if (mEditable) { - // Cache this for the use of (e.g.) modifying the feature's geometry. - // mCachedGeometries[fet->featureId()] = fet->geometryAndOwnership(); - - if (mDeletedFeatureIds.contains(fet.featureId())) - { - continue; //dont't draw feature marked as deleted - } - - if (mChangedGeometries.contains(fet.featureId())) - { - // substitute the committed geometry with the modified one - fet.setGeometry( mChangedGeometries[ fet.featureId() ] ); - } - // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry. mCachedGeometries[fet.featureId()] = *fet.geometry(); } - + // check if feature is selected // only show selections of the current layer // TODO: create a mechanism to let layer know whether it's current layer or not [MD] - bool sel; - if ( - /*(mLegend->currentLayer() == this) && TODO!*/ - (mSelectedFeatureIds.contains(fet.featureId())) - ) - { - sel = TRUE; - } - else - { - sel = FALSE; - } + bool sel = mSelectedFeatureIds.contains(fet.featureId()); //QgsDebugMsg(QString("markerScale before renderFeature(): %1").arg(markerScaleFactor)); - // markerScalerFactore reflects the wanted scaling of the marker - mRenderer->renderFeature(renderContext.painter(), fet, &marker, sel, renderContext.scaleFactor(), renderContext.rasterScaleFactor()); + // markerScalerFactore reflects the wanted scaling of the marker + mRenderer->renderFeature( + renderContext.painter(), + fet, + &marker, + sel, + renderContext.scaleFactor(), + renderContext.rasterScaleFactor()); // markerScalerFactore now reflects the actual scaling of the marker that the render performed. //QgsDebugMsg(QString("markerScale after renderFeature(): %1").arg(markerScaleFactor)); //double scale = renderContext.scaleFactor() / markerScaleFactor; - drawFeature(renderContext.painter() , fet, &(renderContext.mapToPixel()), renderContext.coordTransform(), &marker, renderContext.scaleFactor(), renderContext.rasterScaleFactor(), renderContext.drawEditingInformation()); + drawFeature( + renderContext.painter(), + fet, + &renderContext.mapToPixel(), + renderContext.coordTransform(), + &marker, + renderContext.scaleFactor(), + renderContext.rasterScaleFactor(), + renderContext.drawEditingInformation()); ++featureCount; } - - // also draw the not yet commited features - if (mEditable) - { - QgsFeatureList::iterator it = mAddedFeatures.begin(); - for(; it != mAddedFeatures.end(); ++it) - { - bool sel = mSelectedFeatureIds.contains((*it).featureId()); - //QgsDebugMsg(QString("markerScale before renderFeature(): %1").arg(markerScaleFactor)); - // markerScalerFactore reflects the wanted scaling of the marker - mRenderer->renderFeature(renderContext.painter(), *it, &marker, sel, renderContext.scaleFactor(), renderContext.rasterScaleFactor()); - // markerScalerFactore now reflects the actual scaling of the marker that the render performed. - //QgsDebugMsg(QString("markerScale after renderFeature(): %1").arg(markerScaleFactor)); - - //double scale = renderContext.scaleFactor() / markerScaleFactor; - - if (mChangedGeometries.contains((*it).featureId())) - { - (*it).setGeometry( mChangedGeometries[(*it).featureId() ] ); - } - - // give a deep copy of the geometry to mCachedGeometry because it will be erased at each redraw - mCachedGeometries.insert((*it).featureId(), QgsGeometry(*((*it).geometry())) ); - drawFeature(renderContext.painter(), *it, &(renderContext.mapToPixel()), renderContext.coordTransform(), &marker, renderContext.scaleFactor(), renderContext.rasterScaleFactor(), renderContext.drawEditingInformation()); - } - } - } catch (QgsCsException &cse) { QString msg("Failed to transform a point while drawing a feature of type '" - + fet.typeName() + "'. Ignoring this feature."); + + fet.typeName() + "'. Ignoring this feature."); msg += cse.what(); QgsLogger::warning(msg); } @@ -902,7 +822,7 @@ void QgsVectorLayer::drawVertexMarker(int x, int y, QPainter& p, QgsVectorLayer: void QgsVectorLayer::select(int number, bool emitSignal) { mSelectedFeatureIds.insert(number); - + if (emitSignal) { emit selectionChanged(); @@ -913,52 +833,38 @@ void QgsVectorLayer::select(QgsRect & rect, bool lock) { // normalize the rectangle rect.normalize(); - + if (lock == false) { removeSelection(FALSE); // don't emit signal } //select all the elements - QList selectedList; - featuresInRectangle(rect, selectedList, true, false); //should finally be false, false - QList::const_iterator select_it = selectedList.constBegin(); - for(; select_it != selectedList.constEnd(); ++select_it) - { - select(select_it->featureId(), false); // don't emit signal (not to redraw it everytime) - } - + select(QgsAttributeList(), rect, false); + + QgsFeature f; + while( getNextFeature(f) ) + { + select(f.featureId(), false); // don't emit signal (not to redraw it everytime) + } + emit selectionChanged(); // now emit signal to redraw layer } void QgsVectorLayer::invertSelection() { // copy the ids of selected features to tmp - QgsFeatureIds tmp; - for(QgsFeatureIds::iterator iter = mSelectedFeatureIds.begin(); iter != mSelectedFeatureIds.end(); ++iter) - { - tmp.insert(*iter); - } + QgsFeatureIds tmp = mSelectedFeatureIds; removeSelection(FALSE); // don't emit signal + select(QgsAttributeList(), QgsRect(), true); + QgsFeature fet; - mDataProvider->select(); - - while (mDataProvider->getNextFeature(fet)) + while ( getNextFeature(fet) ) { - // don't select deleted features - if (!mDeletedFeatureIds.contains(fet.featureId())) - { - select(fet.featureId(), FALSE); // don't emit signal - } - } - - // consider also newly added features - for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - select((*iter).featureId(), FALSE); // don't emit signal + select(fet.featureId(), false); // don't emit signal } for(QgsFeatureIds::iterator iter = tmp.begin(); iter != tmp.end(); ++iter) @@ -978,7 +884,7 @@ void QgsVectorLayer::removeSelection(bool emitSignal) } void QgsVectorLayer::triggerRepaint() -{ +{ emit repaintRequested(); } @@ -1084,11 +990,14 @@ QgsRect QgsVectorLayer::boundingBoxOfSelected() } QgsRect r, retval; - QgsFeature fet; - mDataProvider->select(); + + + select(QgsAttributeList(), QgsRect(), true); retval.setMinimal(); - while (mDataProvider->getNextFeature(fet)) + + QgsFeature fet; + while ( getNextFeature(fet) ) { if (mSelectedFeatureIds.contains(fet.featureId())) { @@ -1100,16 +1009,6 @@ QgsRect QgsVectorLayer::boundingBoxOfSelected() } } - // also go through the not commited features - for(QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - if(mSelectedFeatureIds.contains((*iter).featureId())) - { - r = (*iter).geometry()->boundingBox(); - retval.combineExtentWith(&r); - } - } - if (retval.width() == 0.0 || retval.height() == 0.0) { // If all of the features are at the one point, buffer the @@ -1141,7 +1040,7 @@ QgsRect QgsVectorLayer::boundingBoxOfSelected() long QgsVectorLayer::featureCount() const { - if ( ! mDataProvider ) + if ( !mDataProvider ) { QgsLogger::warning(" QgsVectorLayer::featureCount() invoked with null mDataProvider"); return 0; @@ -1152,7 +1051,7 @@ long QgsVectorLayer::featureCount() const long QgsVectorLayer::updateFeatureCount() const { - if ( ! mDataProvider ) + if ( !mDataProvider ) { QgsLogger::warning(" QgsVectorLayer::updateFeatureCount() invoked with null mDataProvider"); return 0; @@ -1163,50 +1062,43 @@ long QgsVectorLayer::updateFeatureCount() const void QgsVectorLayer::updateExtents() { mLayerExtent.setMinimal(); - - if(mDataProvider) - { - if(mDeletedFeatureIds.isEmpty()) - { - // get the extent of the layer from the provider - // but only when there are some features already - if (mDataProvider->featureCount() != 0) - { - QgsRect r = mDataProvider->extent(); - mLayerExtent.combineExtentWith(&r); - } - } - else - { - QgsFeature fet; - QgsRect bb; - mDataProvider->select(); - while (mDataProvider->getNextFeature(fet)) - { - if (!mDeletedFeatureIds.contains(fet.featureId())) - { - if (fet.geometry()) - { - bb = fet.geometry()->boundingBox(); - mLayerExtent.combineExtentWith(&bb); - } - } - } + if(!mDataProvider) + QgsLogger::warning(" QgsVectorLayer::updateExtents() invoked with null mDataProvider"); + + if( mDeletedFeatureIds.isEmpty() ) + { + // get the extent of the layer from the provider + // but only when there are some features already + if (mDataProvider->featureCount() != 0) + { + QgsRect r = mDataProvider->extent(); + mLayerExtent.combineExtentWith(&r); } + + for(QgsFeatureList::iterator it=mAddedFeatures.begin(); it!=mAddedFeatures.end(); it++) + { + QgsRect r = it->geometry()->boundingBox(); + mLayerExtent.combineExtentWith(&r); + } + + return; } else { - QgsLogger::warning(" QgsVectorLayer::updateFeatureCount() invoked with null mDataProvider"); + select(QgsAttributeList(), QgsRect(), true); + + QgsFeature fet; + while( getNextFeature(fet) ) + { + if (fet.geometry()) + { + QgsRect bb = fet.geometry()->boundingBox(); + mLayerExtent.combineExtentWith(&bb); + } + } } - // also consider the not commited features - for(QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - QgsRect bb = iter->geometry()->boundingBox(); - mLayerExtent.combineExtentWith(&bb); - } - if (mLayerExtent.xMin() > mLayerExtent.xMax() && mLayerExtent.yMin() > mLayerExtent.yMax()) { // special case when there are no features in provider nor any added @@ -1234,198 +1126,253 @@ void QgsVectorLayer::setSubsetString(QString subset) QgsLogger::warning(" QgsVectorLayer::setSubsetString() invoked with null mDataProvider"); return; } - + mDataProvider->setSubsetString(subset); // get the updated data source string from the provider mDataSource = mDataProvider->dataSourceUri(); updateExtents(); - } -int QgsVectorLayer::featuresInRectangle(const QgsRect& searchRect, QList& features, bool fetchGeometries, bool fetchAttributes) +void QgsVectorLayer::updateFeatureAttributes(QgsFeature &f) +{ + if( mChangedAttributeValues.contains(f.featureId()) ) { + const QgsAttributeMap &map = mChangedAttributeValues[f.featureId()]; + for(QgsAttributeMap::const_iterator it=map.begin(); it!=map.end(); it++) + f.changeAttribute(it.key(), it.value()); + } +} + +void QgsVectorLayer::updateFeatureGeometry(QgsFeature &f) +{ + if( mChangedGeometries.contains(f.featureId()) ) + f.setGeometry( mChangedGeometries[f.featureId()] ); +} + + +void QgsVectorLayer::select(QgsAttributeList attributes, QgsRect rect, bool fetchGeometries) { if(!mDataProvider) - { - return 1; - } + return; - QSet alreadyExamined; - QgsGeometry* currentGeomPointer = 0; - - //first search all changed geometries - QgsGeometryMap::iterator changedIt; - for(changedIt = mChangedGeometries.begin(); changedIt != mChangedGeometries.end(); ++changedIt) - { - alreadyExamined.insert(changedIt.key()); + mFetching = true; + mFetchRect = rect; + mFetchAttributes = attributes; + mFetchGeometry = fetchGeometries; - if(mDeletedFeatureIds.contains(changedIt.key())) - { - continue; - } + mFetchConsidered = mDeletedFeatureIds; - if(changedIt->intersects(searchRect)) - { - QgsFeature newFeature(changedIt.key()); - if(fetchGeometries) - { - currentGeomPointer = new QgsGeometry(changedIt.value()); - newFeature.setGeometry(currentGeomPointer); - } - if(fetchAttributes) - { - if(changedIt.key()<0) - { - //The feature is in mAddedFeature's list because its id<0 - bool findMyFeature = false; - for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - if(iter->featureId()==changedIt.key()) - { - findMyFeature = true; - newFeature.setAttributeMap(iter->attributeMap()); - break; - } - } - if(!findMyFeature) - { - QgsLogger::warning("No attribute for the feature"); - } - } - else - { - QgsFeature tmpFeature; - mDataProvider->getFeatureAtId(changedIt.key(), tmpFeature, false, mDataProvider->allAttributesList()); - newFeature.setAttributeMap(tmpFeature.attributeMap()); - } - } - features.push_back(newFeature); - } - } - - //then the added features - for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - if(alreadyExamined.contains(iter->featureId())) - { - continue; - } - - if(iter->geometry() && iter->geometry()->intersects(searchRect)) - { - QgsFeature newFeature(iter->featureId()); - if(fetchGeometries) - { - currentGeomPointer = new QgsGeometry(*iter->geometry()); - newFeature.setGeometry(currentGeomPointer); - } - if(fetchAttributes) - { - newFeature.setAttributeMap(iter->attributeMap()); - - } - features.push_back(newFeature); - } - } + if( mEditable ) + { + mFetchAddedFeaturesIt = mAddedFeatures.begin(); + if( mFetchGeometry ) + mFetchChangedGeomIt = mChangedGeometries.begin(); + } //look in the normal features of the provider - if(mDataProvider) - { - mDataProvider->select(mDataProvider->allAttributesList(), searchRect, fetchGeometries, true); - } + if( mFetchAttributes.size()>0 ) + { + mDataProvider->select(mFetchAttributes, rect, fetchGeometries, true); + } else + { + mDataProvider->select(QgsAttributeList(), rect, fetchGeometries, true); + } +} + +bool QgsVectorLayer::getNextFeature(QgsFeature &f) +{ + if(!mFetching) + return false; + + if( mEditable ) + { + if( !mFetchRect.isEmpty() ) { - mDataProvider->select(QgsAttributeList(), searchRect, fetchGeometries, true); + // check if changed geometries are in rectangle + for(; mFetchChangedGeomIt!=mChangedGeometries.end(); mFetchChangedGeomIt++) + { + int fid = mFetchChangedGeomIt.key(); + + if( mFetchConsidered.contains( fid ) ) + // skip deleted features + continue; + + mFetchConsidered << fid; + + if( !mFetchChangedGeomIt->intersects(mFetchRect) ) + // skip changed geometries not in rectangle and don't check again + continue; + + f.setFeatureId( fid ); + + if(mFetchGeometry) + f.setGeometry( mFetchChangedGeomIt.value() ); + + if( mFetchAttributes.size()>0 ) + { + if( fid<0 ) + { + // fid<0 => in mAddedFeatures + bool found = false; + + for (QgsFeatureList::iterator it=mAddedFeatures.begin(); it!=mAddedFeatures.end(); it++) + { + if( fid!=it->featureId() ) + { + found = true; + f.setAttributeMap( it->attributeMap() ); + break; + } + } + + if(!found) + QgsLogger::warning( QString("No attributes for the added feature %1 found").arg(f.featureId()) ); + } + else + { + // retrieve attributes from provider + QgsFeature tmp; + mDataProvider->getFeatureAtId(fid, tmp, false, mDataProvider->allAttributesList()); + updateFeatureAttributes(tmp); + f.setAttributeMap( tmp.attributeMap() ); + } + } + + // return complete feature + mFetchChangedGeomIt++; + return true; + } + + // no more changed geometries } - - QgsFeature f; - while(getDataProvider() && getDataProvider()->getNextFeature(f)) + + for (; mFetchAddedFeaturesIt != mAddedFeatures.end(); mFetchAddedFeaturesIt++) { - if(mChangedGeometries.contains(f.featureId()) || mDeletedFeatureIds.contains(f.featureId())) - { - continue; - } - - QgsFeature newFeature(f.featureId()); - if(fetchGeometries) - { - currentGeomPointer = new QgsGeometry(*(f.geometry())); - newFeature.setGeometry(currentGeomPointer); - } - if(fetchAttributes) - { - newFeature.setAttributeMap(f.attributeMap()); - } - features.push_back(newFeature); + int fid = mFetchAddedFeaturesIt->featureId(); + + if( mFetchConsidered.contains(fid) ) + // must have changed geometry outside rectangle + continue; + + if( !mFetchRect.isEmpty() && + mFetchAddedFeaturesIt->geometry() && + !mFetchAddedFeaturesIt->geometry()->intersects( mFetchRect ) ) + // skip added features not in rectangle + continue; + + f.setFeatureId(fid); + + if(mFetchGeometry) + f.setGeometry( *mFetchAddedFeaturesIt->geometry() ); + + if( mFetchAttributes.size()>0 ) + { + f.setAttributeMap(mFetchAddedFeaturesIt->attributeMap()); + updateFeatureAttributes(f); + } + + mFetchAddedFeaturesIt++; + return true; } - return 0; + + // no more added features + } + + while( getDataProvider()->getNextFeature(f) ) + { + if( mFetchConsidered.contains( f.featureId() ) ) + continue; + + if( mEditable ) + updateFeatureAttributes(f); + + // found it + return true; + } + + mFetching = false; + return false; } int QgsVectorLayer::getFeatureAtId(int featureId, QgsFeature& f, bool fetchGeometries, bool fetchAttributes) { if(!mDataProvider) - { - return 1; - } + return 1; if(mDeletedFeatureIds.contains(featureId)) + return 2; + + if( fetchGeometries && mChangedGeometries.contains(featureId) ) + { + f.setFeatureId(featureId); + f.setGeometry( mChangedGeometries[featureId] ); + + if(fetchAttributes) { - return 2; + if( featureId<0 ) + { + // featureId<0 => in mAddedFeatures + bool found = false; + + for (QgsFeatureList::iterator it=mAddedFeatures.begin(); it!=mAddedFeatures.end(); it++) + { + if( featureId!=it->featureId() ) + { + found = true; + f.setAttributeMap( it->attributeMap() ); + break; + } + } + + if(!found) + QgsLogger::warning( QString("No attributes for the added feature %1 found").arg(f.featureId()) ); + } + else + { + // retrieve attributes from provider + QgsFeature tmp; + mDataProvider->getFeatureAtId(featureId, tmp, false, mDataProvider->allAttributesList()); + updateFeatureAttributes(tmp); + f.setAttributeMap( tmp.attributeMap() ); + } + updateFeatureAttributes(f); } + } - //changed geometries - - QgsGeometryMap::iterator changedIt; - changedIt = mChangedGeometries.find(featureId); - if(changedIt != mChangedGeometries.end()) + //added features + for(QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter!=mAddedFeatures.end(); ++iter) + { + if(iter->featureId() == featureId) { - f.setFeatureId(changedIt.key()); + f.setFeatureId(iter->featureId()); + if(fetchGeometries) - { - f.setGeometry(new QgsGeometry(changedIt.value())); - } + f.setGeometry( *iter->geometry() ); + if(fetchAttributes) - { - QgsFeature tmpFeature; - mDataProvider->getFeatureAtId(changedIt.key(), tmpFeature, false, mDataProvider->allAttributesList()); - f.setAttributeMap(tmpFeature.attributeMap()); - } + f.setAttributeMap(iter->attributeMap()); + return 0; } - - //added features - for(QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - if(iter->featureId() == featureId) - { - f.setFeatureId(iter->featureId()); - if(fetchGeometries) - { - f.setGeometry(*(iter->geometry())); - } - if(fetchAttributes) - { - f.setAttributeMap(iter->attributeMap()); - } - return 0; - } - } + } - //permanent features + // regular features if(fetchAttributes) + { + if(mDataProvider->getFeatureAtId(featureId, f, fetchGeometries, mDataProvider->allAttributesList())) { - if(mDataProvider->getFeatureAtId(featureId, f, fetchGeometries, mDataProvider->allAttributesList())) - { - return 0; - } + updateFeatureAttributes(f); + return 0; } + } else + { + if(mDataProvider->getFeatureAtId(featureId, f, fetchGeometries, QgsAttributeList())) { - if(mDataProvider->getFeatureAtId(featureId, f, fetchGeometries, QgsAttributeList())) - { - return 0; - } + return 0; } - return 3; + } + return 3; } bool QgsVectorLayer::addFeature(QgsFeature& f, bool alsoUpdateExtent) @@ -1436,7 +1383,7 @@ bool QgsVectorLayer::addFeature(QgsFeature& f, bool alsoUpdateExtent) { return false; } - + if(!(mDataProvider->capabilities() & QgsVectorDataProvider::AddFeatures)) { return false; @@ -1447,25 +1394,25 @@ bool QgsVectorLayer::addFeature(QgsFeature& f, bool alsoUpdateExtent) return false; } - //assign a temporary id to the feature (use negative numbers) - addedIdLowWaterMark--; + //assign a temporary id to the feature (use negative numbers) + addedIdLowWaterMark--; - QgsDebugMsg("Assigned feature id " + QString::number(addedIdLowWaterMark)); + QgsDebugMsg("Assigned feature id " + QString::number(addedIdLowWaterMark)); - // Force a feature ID (to keep other functions in QGIS happy, - // providers will use their own new feature ID when we commit the new feature) - // and add to the known added features. - f.setFeatureId(addedIdLowWaterMark); - mAddedFeatures.append(f); - - setModified(TRUE); + // Force a feature ID (to keep other functions in QGIS happy, + // providers will use their own new feature ID when we commit the new feature) + // and add to the known added features. + f.setFeatureId(addedIdLowWaterMark); + mAddedFeatures.append(f); - if (alsoUpdateExtent) - { - updateExtents(); - } + setModified(true); - return true; + if (alsoUpdateExtent) + { + updateExtents(); + } + + return true; } @@ -1475,7 +1422,7 @@ bool QgsVectorLayer::insertVertexBefore(double x, double y, int atFeatureId, int { return false; } - + if (mDataProvider) { @@ -1491,7 +1438,7 @@ bool QgsVectorLayer::insertVertexBefore(double x, double y, int atFeatureId, int mChangedGeometries[atFeatureId].insertVertexBefore(x, y, beforeVertex); - setModified(TRUE, TRUE); // only geometry was changed + setModified(true, true); // only geometry was changed return true; } @@ -1503,9 +1450,9 @@ bool QgsVectorLayer::moveVertexAt(double x, double y, int atFeatureId, int atVer { if (!mEditable) { - return false; + return false; } - + if (mDataProvider) { if (!mChangedGeometries.contains(atFeatureId)) @@ -1520,7 +1467,7 @@ bool QgsVectorLayer::moveVertexAt(double x, double y, int atFeatureId, int atVer mChangedGeometries[atFeatureId].moveVertexAt(x, y, atVertex); - setModified(TRUE, TRUE); // only geometry was changed + setModified(true, true); // only geometry was changed return true; } @@ -1549,7 +1496,7 @@ bool QgsVectorLayer::deleteVertexAt(int atFeatureId, int atVertex) mChangedGeometries[atFeatureId].deleteVertexAt(atVertex); - setModified(TRUE, TRUE); // only geometry was changed + setModified(true, true); // only geometry was changed return true; } @@ -1569,37 +1516,19 @@ bool QgsVectorLayer::deleteSelectedFeatures() return false; } - for(QgsFeatureIds::iterator it = mSelectedFeatureIds.begin(); it != mSelectedFeatureIds.end(); ++it) + if( mSelectedFeatureIds.size()==0 ) + return true; + + while( mSelectedFeatureIds.size()>0 ) { - bool noncommited = FALSE; - // first test, if the feature with this id is a not-commited feature - for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - if (iter->featureId() == *it) - { - noncommited = TRUE; - - // Delete the feature itself - mAddedFeatures.erase(iter); - - break; - } - } - - if (!noncommited) - { - mDeletedFeatureIds.insert(*it); - } + int fid = *mSelectedFeatureIds.begin(); + deleteFeature( fid ); // removes from selection } - if(mSelectedFeatureIds.size()>0) - { - setModified(TRUE); - removeSelection(FALSE); // don't emit signal - triggerRepaint(); - updateExtents(); + emit selectionChanged(); - } + triggerRepaint(); + updateExtents(); return true; } @@ -1611,28 +1540,27 @@ int QgsVectorLayer::addRing(const QList& ring) QgsRect bBox; if(boundingBoxFromPointList(ring, xMin, yMin, xMax, yMax) == 0) - { - bBox.setXmin(xMin); bBox.setYmin(yMin); bBox.setXmax(xMax); bBox.setYmax(yMax); - } + { + bBox.setXmin(xMin); bBox.setYmin(yMin); bBox.setXmax(xMax); bBox.setYmax(yMax); + } else - { - return 3; //ring not valid - } + { + return 3; //ring not valid + } - QList featureList; - featuresInRectangle(bBox, featureList, true, false); - QList::iterator f_it = featureList.begin(); + select(QgsAttributeList(), bBox, true); - for(; f_it != featureList.end(); ++f_it) + QgsFeature f; + while( getNextFeature(f) ) + { + addRingReturnCode = f.geometry()->addRing(ring); + if(addRingReturnCode == 0) { - addRingReturnCode = f_it->geometry()->addRing(ring); - if(addRingReturnCode == 0) - { - mChangedGeometries.insert(f_it->featureId(), *(f_it->geometry())); - setModified(true, true); - break; - } + mChangedGeometries.insert(f.featureId(), *f.geometry()); + setModified(true, true); + break; } + } return addRingReturnCode; } @@ -1640,48 +1568,48 @@ int QgsVectorLayer::addRing(const QList& ring) int QgsVectorLayer::addIsland(const QList& ring) { //number of selected features must be 1 - - if(mSelectedFeatureIds.size() < 1) - { - QgsDebugMsg("Number of selected features <1"); - return 4; - } - else if(mSelectedFeatureIds.size() > 1) - { - QgsDebugMsg("Number of selected features >1"); - return 5; - } - int selectedFeatureId = *(mSelectedFeatureIds.constBegin()); + if(mSelectedFeatureIds.size() < 1) + { + QgsDebugMsg("Number of selected features <1"); + return 4; + } + else if(mSelectedFeatureIds.size() > 1) + { + QgsDebugMsg("Number of selected features >1"); + return 5; + } + + int selectedFeatureId = *mSelectedFeatureIds.constBegin(); //look if geometry of selected feature already contains geometry changes QgsGeometryMap::iterator changedIt = mChangedGeometries.find(selectedFeatureId); if(changedIt != mChangedGeometries.end()) - { - return changedIt->addIsland(ring); - } + { + return changedIt->addIsland(ring); + } //look if id of selected feature belongs to an added feature for(QgsFeatureList::iterator addedIt = mAddedFeatures.begin(); addedIt != mAddedFeatures.end(); ++addedIt) + { + if(addedIt->featureId() == selectedFeatureId) { - if(addedIt->featureId() == selectedFeatureId) - { - return addedIt->geometry()->addIsland(ring); - } + return addedIt->geometry()->addIsland(ring); } + } //else, if must be contained in mCachedGeometries QgsGeometryMap::iterator cachedIt = mCachedGeometries.find(selectedFeatureId); if(cachedIt != mCachedGeometries.end()) + { + int errorCode = cachedIt->addIsland(ring); + if(errorCode == 0) { - int errorCode = cachedIt->addIsland(ring); - if(errorCode == 0) - { - mChangedGeometries.insert(selectedFeatureId, *cachedIt); - setModified(true, true); - } - return errorCode; + mChangedGeometries.insert(selectedFeatureId, *cachedIt); + setModified(true, true); } + return errorCode; + } return 6; //geometry not found } @@ -1691,31 +1619,31 @@ int QgsVectorLayer::translateFeature(int featureId, double dx, double dy) //look if geometry of selected feature already contains geometry changes QgsGeometryMap::iterator changedIt = mChangedGeometries.find(featureId); if(changedIt != mChangedGeometries.end()) - { - return changedIt->translate(dx, dy); - } - + { + return changedIt->translate(dx, dy); + } + //look if id of selected feature belongs to an added feature for(QgsFeatureList::iterator addedIt = mAddedFeatures.begin(); addedIt != mAddedFeatures.end(); ++addedIt) + { + if(addedIt->featureId() == featureId) { - if(addedIt->featureId() == featureId) - { - return addedIt->geometry()->translate(dx, dy); - } + return addedIt->geometry()->translate(dx, dy); } + } //else, if must be contained in mCachedGeometries QgsGeometryMap::iterator cachedIt = mCachedGeometries.find(featureId); if(cachedIt != mCachedGeometries.end()) + { + int errorCode = cachedIt->translate(dx, dy); + if(errorCode == 0) { - int errorCode = cachedIt->translate(dx, dy); - if(errorCode == 0) - { - mChangedGeometries.insert(featureId, *cachedIt); - setModified(true, true); - } - return errorCode; + mChangedGeometries.insert(featureId, *cachedIt); + setModified(true, true); } + return errorCode; + } return 1; //geometry not found } @@ -1730,84 +1658,89 @@ int QgsVectorLayer::splitFeatures(const QList& splitLine, bool topolog QgsFeatureList featureList; const QgsFeatureIds selectedIds = selectedFeaturesIds(); - + if(selectedIds.size() > 0)//consider only the selected features if there is a selection - { - featureList = selectedFeatures(); - } + { + featureList = selectedFeatures(); + } else //else consider all the feature that intersect the bounding box of the split line + { + if(boundingBoxFromPointList(splitLine, xMin, yMin, xMax, yMax) == 0) { - if(boundingBoxFromPointList(splitLine, xMin, yMin, xMax, yMax) == 0) - { - bBox.setXmin(xMin); bBox.setYmin(yMin); bBox.setXmax(xMax); bBox.setYmax(yMax); - } - else - { - return 1; - } - - if(bBox.isEmpty()) - { - //if the bbox is a line, try to make a square out of it - if(bBox.width()==0.0 && bBox.height() > 0) - { - bBox.setXmin(bBox.xMin() - bBox.height()/2); - bBox.setXmax(bBox.xMax() + bBox.height()/2); - } - else if(bBox.height()==0.0 && bBox.width()>0) - { - bBox.setYmin(bBox.yMin() - bBox.width()/2); - bBox.setYmax(bBox.yMax() + bBox.width()/2); - } - else - { - return 2; - } - } - featuresInRectangle(bBox, featureList); + bBox.setXmin(xMin); bBox.setYmin(yMin); bBox.setXmax(xMax); bBox.setYmax(yMax); } - + else + { + return 1; + } + + if(bBox.isEmpty()) + { + //if the bbox is a line, try to make a square out of it + if(bBox.width()==0.0 && bBox.height() > 0) + { + bBox.setXmin(bBox.xMin() - bBox.height()/2); + bBox.setXmax(bBox.xMax() + bBox.height()/2); + } + else if(bBox.height()==0.0 && bBox.width()>0) + { + bBox.setYmin(bBox.yMin() - bBox.width()/2); + bBox.setYmax(bBox.yMax() + bBox.width()/2); + } + else + { + return 2; + } + } + + select(QgsAttributeList(), bBox, true); + + QgsFeature f; + while( getNextFeature(f) ) + featureList << f; + } + QgsFeatureList::iterator select_it = featureList.begin(); for(; select_it != featureList.end(); ++select_it) + { + QList newGeometries; + QgsGeometry* newGeometry = 0; + splitFunctionReturn = select_it->geometry()->splitGeometry(splitLine, newGeometries); + if(splitFunctionReturn == 0) { - QList newGeometries; - QgsGeometry* newGeometry = 0; - splitFunctionReturn = select_it->geometry()->splitGeometry(splitLine, newGeometries); - if(splitFunctionReturn == 0) - { - //change this geometry - mChangedGeometries.insert(select_it->featureId(), *(select_it->geometry())); - - //insert new features - for(int i = 0; i < newGeometries.size(); ++i) - { - newGeometry = newGeometries.at(i); - QgsFeature newFeature; - newFeature.setGeometry(newGeometry); - newFeature.setAttributeMap(select_it->attributeMap()); - newFeatures.append(newFeature); - if(topologicalEditing) //add topological points for new feature - { - addTopologicalPoints(newGeometry); - } - } - setModified(true, true); + //change this geometry + mChangedGeometries.insert(select_it->featureId(), *(select_it->geometry())); - //add topological points for this geometry if necessary - if(topologicalEditing) - { - addTopologicalPoints(select_it->geometry()); - } - } - else if(splitFunctionReturn > 1) //1 means no split but also no error - { - returnCode = 3; - } + //insert new features + for(int i = 0; i < newGeometries.size(); ++i) + { + newGeometry = newGeometries.at(i); + QgsFeature newFeature; + newFeature.setGeometry(newGeometry); + newFeature.setAttributeMap(select_it->attributeMap()); + newFeatures.append(newFeature); + if(topologicalEditing) //add topological points for new feature + { + addTopologicalPoints(newGeometry); + } + } + setModified(true, true); + + //add topological points for this geometry if necessary + if(topologicalEditing) + { + addTopologicalPoints(select_it->geometry()); + } } + else if(splitFunctionReturn > 1) //1 means no split but also no error + { + returnCode = 3; + } + } //now add the new features to this vectorlayer addFeatures(newFeatures, false); - + return returnCode; } @@ -1817,134 +1750,131 @@ int QgsVectorLayer::removePolygonIntersections(QgsGeometry* geom) //first test if geom really has type polygon or multipolygon if(geom->vectorType() != QGis::Polygon) - { - return 1; - } + { + return 1; + } //get bounding box of geom QgsRect geomBBox = geom->boundingBox(); //get list of features that intersect this bounding box - QList featureList; - featuresInRectangle(geomBBox, featureList); - - QList::iterator it = featureList.begin(); - QgsGeometry* currentGeom; + select(QgsAttributeList(), geomBBox, true); - for(; it != featureList.end(); ++it) + QgsFeature f; + while( getNextFeature(f) ) + { + //call geometry->makeDifference for each feature + QgsGeometry *currentGeom = f.geometry(); + if(currentGeom) { - //call geometry->makeDifference for each feature - currentGeom = it->geometry(); - if(currentGeom) - { - if(geom->makeDifference(it->geometry()) != 0) - { - returnValue = 2; - } - } + if( geom->makeDifference(currentGeom)!=0 ) + { + returnValue = 2; + } } + } return returnValue; } int QgsVectorLayer::addTopologicalPoints(QgsGeometry* geom) { if(!geom) - { - return 1; - } + { + return 1; + } int returnVal = 0; QGis::WKBTYPE wkbType = geom->wkbType(); - + switch(wkbType) - { - //line + { + //line case QGis::WKBLineString25D: case QGis::WKBLineString: { - QgsPolyline theLine = geom->asPolyline(); + QgsPolyline theLine = geom->asPolyline(); QgsPolyline::const_iterator line_it = theLine.constBegin(); - for(; line_it != theLine.constEnd(); ++line_it) - { - if(addTopologicalPoints(*line_it) != 0) - { - returnVal = 2; - } - } - break; + for(; line_it != theLine.constEnd(); ++line_it) + { + if(addTopologicalPoints(*line_it) != 0) + { + returnVal = 2; + } + } + break; } //multiline case QGis::WKBMultiLineString25D: case QGis::WKBMultiLineString: { - QgsMultiPolyline theMultiLine = geom->asMultiPolyline(); - QgsPolyline currentPolyline; + QgsMultiPolyline theMultiLine = geom->asMultiPolyline(); + QgsPolyline currentPolyline; - for(int i = 0; i < theMultiLine.size(); ++i) - { - QgsPolyline::const_iterator line_it = currentPolyline.constBegin(); - for(; line_it != currentPolyline.constEnd(); ++line_it) - { - if(addTopologicalPoints(*line_it) != 0) - { - returnVal = 2; - } - } - } - break; + for(int i = 0; i < theMultiLine.size(); ++i) + { + QgsPolyline::const_iterator line_it = currentPolyline.constBegin(); + for(; line_it != currentPolyline.constEnd(); ++line_it) + { + if(addTopologicalPoints(*line_it) != 0) + { + returnVal = 2; + } + } + } + break; } - + //polygon case QGis::WKBPolygon25D: case QGis::WKBPolygon: { - QgsPolygon thePolygon = geom->asPolygon(); - QgsPolyline currentRing; - - for(int i = 0; i < thePolygon.size(); ++i) - { - currentRing = thePolygon.at(i); - QgsPolyline::const_iterator line_it = currentRing.constBegin(); - for(; line_it != currentRing.constEnd(); ++line_it) - { - if(addTopologicalPoints(*line_it) != 0) - { - returnVal = 2; - } - } - } - break; + QgsPolygon thePolygon = geom->asPolygon(); + QgsPolyline currentRing; + + for(int i = 0; i < thePolygon.size(); ++i) + { + currentRing = thePolygon.at(i); + QgsPolyline::const_iterator line_it = currentRing.constBegin(); + for(; line_it != currentRing.constEnd(); ++line_it) + { + if(addTopologicalPoints(*line_it) != 0) + { + returnVal = 2; + } + } + } + break; } //multipolygon case QGis::WKBMultiPolygon25D: case QGis::WKBMultiPolygon: { - QgsMultiPolygon theMultiPolygon = geom->asMultiPolygon(); - QgsPolygon currentPolygon; - QgsPolyline currentRing; - - for(int i = 0; i < theMultiPolygon.size(); ++i) - { - currentPolygon = theMultiPolygon.at(i); - for(int j = 0; j < currentPolygon.size(); ++j) - { - currentRing = currentPolygon.at(j); - QgsPolyline::const_iterator line_it = currentRing.constBegin(); - for(; line_it != currentRing.constEnd(); ++line_it) - { - if(addTopologicalPoints(*line_it) != 0) - { - returnVal = 2; - } - } - } - } - break; + QgsMultiPolygon theMultiPolygon = geom->asMultiPolygon(); + QgsPolygon currentPolygon; + QgsPolyline currentRing; + + for(int i = 0; i < theMultiPolygon.size(); ++i) + { + currentPolygon = theMultiPolygon.at(i); + for(int j = 0; j < currentPolygon.size(); ++j) + { + currentRing = currentPolygon.at(j); + QgsPolyline::const_iterator line_it = currentRing.constBegin(); + for(; line_it != currentRing.constEnd(); ++line_it) + { + if(addTopologicalPoints(*line_it) != 0) + { + returnVal = 2; + } + } + } + } + break; } - } + } return returnVal; } @@ -1959,36 +1889,35 @@ int QgsVectorLayer::addTopologicalPoints(const QgsPoint& p) const double threshold = 0.00000001; if(snapWithContext(p, threshold, snapResults, QgsSnapper::SNAP_TO_SEGMENT) != 0) - { - return 2; - } + { + return 2; + } QMultiMap::const_iterator snap_it = snapResults.constBegin(); QMultiMap::const_iterator vertex_snap_it; - for(; snap_it != snapResults.constEnd(); ++snap_it) - { - //test if p is already a vertex of this geometry. If yes, don't insert it - bool vertexAlreadyExists = false; - if(snapWithContext(p, threshold, vertexSnapResults, QgsSnapper::SNAP_TO_VERTEX) != 0) - { - continue; - } - - vertex_snap_it = vertexSnapResults.constBegin(); - for(; vertex_snap_it != vertexSnapResults.constEnd(); ++vertex_snap_it) - { - if(snap_it.value().snappedAtGeometry == vertex_snap_it.value().snappedAtGeometry) - { - vertexAlreadyExists = true; - } - } - - if(!vertexAlreadyExists) - { - filteredSnapResults.push_back(*snap_it); - } + { + //test if p is already a vertex of this geometry. If yes, don't insert it + bool vertexAlreadyExists = false; + if(snapWithContext(p, threshold, vertexSnapResults, QgsSnapper::SNAP_TO_VERTEX) != 0) + { + continue; } + + vertex_snap_it = vertexSnapResults.constBegin(); + for(; vertex_snap_it != vertexSnapResults.constEnd(); ++vertex_snap_it) + { + if(snap_it.value().snappedAtGeometry == vertex_snap_it.value().snappedAtGeometry) + { + vertexAlreadyExists = true; + } + } + + if(!vertexAlreadyExists) + { + filteredSnapResults.push_back(*snap_it); + } + } insertSegmentVerticesForSnap(filteredSnapResults); return 0; } @@ -2016,17 +1945,32 @@ bool QgsVectorLayer::startEditing() { return false; } - - if(!(mDataProvider->capabilities()&QgsVectorDataProvider::AddFeatures)) + + if( !(mDataProvider->capabilities() & QgsVectorDataProvider::AddFeatures) ) { return false; } + if(mEditable) + { + // editing already underway + return false; + } + mEditable=true; + + mUpdatedFields = mDataProvider->fields(); + mMaxUpdatedIndex = -1; + + for(QgsFieldMap::const_iterator it=mUpdatedFields.begin(); it!=mUpdatedFields.end(); it++) + if( it.key() > mMaxUpdatedIndex ) + mMaxUpdatedIndex = it.key(); + + emit editingStarted(); + return true; } - bool QgsVectorLayer::readXml( QDomNode & layer_node ) { QgsDebugMsg(QString("Datasource in QgsVectorLayer::readXml: ") + mDataSource.toLocal8Bit().data()); @@ -2082,7 +2026,41 @@ bool QgsVectorLayer::readXml( QDomNode & layer_node ) setDisplayField(e.text()); } + QDomNode editTypesNode = layer_node.namedItem("edittypes"); + if( !editTypesNode.isNull() ) + { + QDomNodeList editTypeNodes = editTypesNode.childNodes(); + for(int i=0; i() ); + + QDomNodeList valueMapNodes = editTypeNode.childNodes(); + for(int j=0; j=0.7 project it should have been validated and have all // coord xform info // @@ -2177,7 +2155,7 @@ bool QgsVectorLayer::setDataProvider( QString const & provider ) //XXX - This was a dynamic cast but that kills the Windows // version big-time with an abnormal termination error - mDataProvider = + mDataProvider = (QgsVectorDataProvider*)(QgsProviderRegistry::instance()->getProvider(provider,mDataSource)); if (mDataProvider) @@ -2189,9 +2167,7 @@ bool QgsVectorLayer::setDataProvider( QString const & provider ) { // TODO: Check if the provider has the capability to send fullExtentCalculated - connect(mDataProvider, SIGNAL( fullExtentCalculated() ), - this, SLOT( updateExtents() ) - ); + connect(mDataProvider, SIGNAL( fullExtentCalculated() ), this, SLOT(updateExtents()) ); // get the extent QgsRect mbr = mDataProvider->extent(); @@ -2288,13 +2264,52 @@ bool QgsVectorLayer::setDataProvider( QString const & provider ) QgsAttributeList attributes=mRenderer->classificationAttributes(); const QgsFieldMap providerFields = mDataProvider->fields(); for(QgsAttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) + { + QDomElement classificationElement = document.createElement("classificationattribute"); + QDomText classificationText = document.createTextNode(providerFields[*it].name()); + classificationElement.appendChild(classificationText); + layer_node.appendChild(classificationElement); + } + + if( mEditTypes.size()>0 ) + { + QDomElement editTypesElement = document.createElement("edittypes"); + + for(QMap::const_iterator it = mEditTypes.begin(); it != mEditTypes.end(); ++it) { - QDomElement classificationElement = document.createElement("classificationattribute"); - QDomText classificationText = document.createTextNode(providerFields[*it].name()); - classificationElement.appendChild(classificationText); - layer_node.appendChild(classificationElement); + QDomElement editTypeElement = document.createElement("edittype"); + editTypeElement.setAttribute( "name", it.key() ); + editTypeElement.setAttribute( "type", it.value() ); + + if( it.value() == ValueMap ) + { + if( mValueMaps.contains( it.key() ) ) + { + const QMap &map = mValueMaps[ it.key() ]; + + for(QMap::const_iterator vmit=map.begin(); vmit!=map.end(); vmit++) + { + QDomElement value = document.createElement("valuepair"); + value.setAttribute( "key", vmit.key() ); + value.setAttribute( "value", vmit.value().toString() ); + editTypeElement.appendChild( value ); + } + } + } else if( it.value() == Range ) { + if( mRanges.contains( it.key() ) ) + { + editTypeElement.setAttribute( "min", mRanges[ it.key() ].mMin.toString() ); + editTypeElement.setAttribute( "max", mRanges[ it.key() ].mMax.toString() ); + editTypeElement.setAttribute( "step", mRanges[ it.key() ].mStep.toString() ); + } + } + + editTypesElement.appendChild(editTypeElement); } + layer_node.appendChild(editTypesElement); + } + // add the display field QDomElement dField = document.createElement( "displayfield" ); @@ -2351,7 +2366,7 @@ bool QgsVectorLayer::setDataProvider( QString const & provider ) QString fieldname = myLabel->labelField(QgsLabel::Text); if(fieldname!="") { dField = document.createElement( "labelfield" ); - dFieldText = document.createTextNode( fieldname ); + dFieldText = document.createTextNode( fieldname ); dField.appendChild( dFieldText ); layer_node.appendChild( dField ); } @@ -2408,196 +2423,379 @@ bool QgsVectorLayer::setDataProvider( QString const & provider ) return true; } // bool QgsVectorLayer::writeXml - -int QgsVectorLayer::findFreeId() +bool QgsVectorLayer::changeAttributeValue(int fid, int field, QVariant value, bool emitSignal) { - int freeid=-INT_MAX; - int fid; - if(mDataProvider) - { - mDataProvider->select(); - QgsFeature fet; + if( !isEditable() ) + return false; - //TODO: Is there an easier way of doing this other than iteration? - //TODO: Also, what about race conditions between this code and a competing mapping client? - //TODO: Maybe push this to the data provider? - while (mDataProvider->getNextFeature(fet)) + if(fid>=0) + { + // changed attribute of existing feature + if( !mChangedAttributeValues.contains(fid) ) { - fid = fet.featureId(); - if(fid>freeid) - { - freeid=fid; - } + mChangedAttributeValues.insert( fid, QgsAttributeMap() ); } - - QgsDebugMsg("freeid is: " + QString::number(freeid+1)); - - return freeid+1; + + mChangedAttributeValues[fid].insert(field, value); } else { - QgsDebugMsg("Error, mDataProvider is 0 in QgsVectorLayer::findFreeId"); - return -1; + // updated added feature + int i; + for(i=0; isupportedNativeTypes(); + QVariant::Type typeType = QVariant::String; + if( types.contains(type) ) + typeType = (QVariant::Type) types[type]; + + mMaxUpdatedIndex++; + mUpdatedFields.insert( mMaxUpdatedIndex, QgsField(name, typeType, type) ); + mAddedAttributeIds.insert( mMaxUpdatedIndex ); + + setModified(true, false); + + emit attributeAdded(mMaxUpdatedIndex); + + return true; +} + +bool QgsVectorLayer::deleteAttribute(int index) +{ + if ( !isEditable() ) + return false; + + if ( mDeletedAttributeIds.contains( index ) ) + return false; + + if ( !mDataProvider->fields().contains(index) ) + return false; + + mDeletedAttributeIds.insert(index); + + setModified(true, false); + + emit attributeDeleted(index); + + return true; +} + +bool QgsVectorLayer::deleteFeature(int fid) +{ + if ( !isEditable() ) + return false; + + if ( mDeletedFeatureIds.contains( fid ) ) + return true; + + mSelectedFeatureIds.remove(fid); // remove it from selection + mDeletedFeatureIds.insert(fid); + + setModified(true, false); + + emit featureDeleted(fid); + + return true; +} + +const QgsFieldMap &QgsVectorLayer::pendingFields() +{ + return isEditable() ? mUpdatedFields : mDataProvider->fields(); +} + +QgsAttributeList QgsVectorLayer::pendingAllAttributesList() +{ + return isEditable() ? mUpdatedFields.keys() : mDataProvider->allAttributesList(); +} + +int QgsVectorLayer::pendingFeatureCount() +{ + return mDataProvider->featureCount() + + mAddedFeatures.size() + - mDeletedFeatureIds.size(); } bool QgsVectorLayer::commitChanges() { - if (!mDataProvider) + bool success = true; + + mCommitErrors.clear(); + + if ( !mDataProvider ) { + mCommitErrors << tr("ERROR: no provider"); return false; } - - if (!isEditable()) + + if ( !isEditable() ) { + mCommitErrors << tr("ERROR: layer not editable"); return false; } - - /* - Unfortunately the commits occur in four distinct stages, - (add features, change attributes, change geometries, delete features) - so if a stage fails, it's difficult to roll back cleanly. - Therefore the error messages become a bit complicated to generate. - */ - // Attempt the commit of new features - bool addedFeaturesOk = FALSE; - if (mAddedFeatures.size() > 0) + int cap = mDataProvider->capabilities(); + + // + // add attributes + // + bool attributesChanged = false; + if ( mAddedAttributeIds.size()>0 ) { - if(!mDataProvider->addFeatures(mAddedFeatures)) - { - QStringList errorStrings; - errorStrings += tr("Could not commit the added features."); - errorStrings += tr("No other types of changes will be committed at this time."); + QgsNewAttributesMap addedAttributes; + for(QgsAttributeIds::const_iterator it = mAddedAttributeIds.begin(); it!=mAddedAttributeIds.end(); it++) + addedAttributes[ mUpdatedFields[ *it ].name() ] = mUpdatedFields[ *it ].typeName(); - // TODO: use an error string or something to store error to be retrieved later [MD] - //QMessageBox::warning(0, tr("Error"), errorStrings.join(" ")); - return FALSE; + if( (cap & QgsVectorDataProvider::AddAttributes ) && mDataProvider->addAttributes( addedAttributes ) ) + { + mCommitErrors << tr( "SUCCESS: %1 attributes added." ).arg( mAddedAttributeIds.size() ); + mAddedAttributeIds.clear(); + attributesChanged = true; } else { - //Remove all negative Id in selected features as they are not valid any more after commit - if(mSelectedFeatureIds.size() > 0) - { - for(QgsFeatureList::iterator it = mAddedFeatures.begin(); it != mAddedFeatures.end(); ++it) - { - mSelectedFeatureIds.remove(it->featureId()); - } - } - - // done, remove features from the list - mAddedFeatures.clear(); - addedFeaturesOk = TRUE; + mCommitErrors << tr( "ERROR: %1 new attributes not added" ).arg( mAddedAttributeIds.size() ); + success = false; } } - // Attempt the commit of changed attributes - bool changedAttributesOk = FALSE; - if (mChangedAttributes.size() > 0) + // + // delete attributes + // + if ( mDeletedAttributeIds.size()>0 ) { - if (!mDataProvider->changeAttributeValues(mChangedAttributes)) + if( (cap & QgsVectorDataProvider::DeleteAttributes) && mDataProvider->deleteAttributes ( mDeletedAttributeIds ) ) { - QStringList errorStrings; - errorStrings += tr("Could not commit the changed attributes."); - if (addedFeaturesOk) - { - errorStrings += tr("However, the added features were committed OK."); - } - errorStrings += tr("No other types of changes will be committed at this time."); - - // TODO: use an error string or something to store error to be retrieved later [MD] - //QMessageBox::warning(0, tr("Error"), errorStrings.join(" ")); - return FALSE; + mCommitErrors << tr( "SUCCESS: %1 attributes deleted." ).arg( mDeletedAttributeIds.size() ); + mDeletedAttributeIds.clear(); + attributesChanged = true; } else { - // Changed attributes committed OK, remove the in-memory changes - mChangedAttributes.clear(); - changedAttributesOk = TRUE; + mCommitErrors << tr( "ERROR: %1 attributes not deleted." ).arg( mDeletedAttributeIds.size() ); + success = false; } } - // Attempt the commit of changed geometries - bool changedGeometriesOk = FALSE; - if (mChangedGeometries.size() > 0) + // + // remap changed and attributes of added features + // + bool attributeChangesOk = true; + if( attributesChanged ) { - if (!mDataProvider->changeGeometryValues(mChangedGeometries)) + // map updates field indexes to names + QMap src; + for(QgsFieldMap::const_iterator it=mUpdatedFields.begin(); it!=mUpdatedFields.end(); it++) { - QStringList errorStrings; - errorStrings += tr("Could not commit the changed geometries."); - if (addedFeaturesOk) - { - errorStrings += tr("However, the added features were committed OK."); - } - if (changedAttributesOk) - { - errorStrings += tr("However, the changed attributes were committed OK."); - } - errorStrings += tr("No other types of changes will be committed at this time."); - - // TODO: use an error string or something to store error to be retrieved later [MD] - //QMessageBox::warning(0, tr("Error"), errorStrings.join(" ")); - return FALSE; + src[ it.key() ] = it.value().name(); } - else + + int maxAttrIdx = -1; + const QgsFieldMap &pFields = mDataProvider->fields(); + + // map provider table names to field indexes + QMap dst; + for(QgsFieldMap::const_iterator it=pFields.begin(); it!=pFields.end(); it++) { - // Changed geometries committed OK, remove the in-memory changes + dst[ it.value().name() ] = it.key(); + if( it.key() > maxAttrIdx ) + maxAttrIdx = it.key(); + } + + // if adding attributes failed add fields that are now missing + // (otherwise we'll loose updates when doing the remapping) + if( mAddedAttributeIds.size()>0 ) + { + for(QgsAttributeIds::const_iterator it=mAddedAttributeIds.begin(); it!=mAddedAttributeIds.end(); it++) + { + QString name = mUpdatedFields[ *it ].name(); + if( dst.contains( name ) ) + { + // it's there => so we don't need to add it anymore + mAddedAttributeIds.remove(*it); + mCommitErrors << tr( "SUCCESS: attribute %1 was added." ).arg( name ); + } + else + { + // field not there => put it behind the existing attributes + dst[ name ] = ++maxAttrIdx; + attributeChangesOk = false; // don't try attribute updates - they'll fail. + mCommitErrors << tr( "ERROR: attribute %1 not added" ).arg( name ); + } + } + } + + // map updated fields to provider fields + QMap remap; + for(QMap::const_iterator it=src.begin(); it!=src.end(); it++) + { + if( dst.contains(it.value()) ) + { + remap[ it.key() ] = dst[ it.value() ]; + } + } + + // remap changed attributes + for(QgsChangedAttributesMap::iterator fit=mChangedAttributeValues.begin(); fit!=mChangedAttributeValues.end(); fit++) + { + QgsAttributeMap &src = fit.value(); + QgsAttributeMap dst; + + for(QgsAttributeMap::const_iterator it=src.begin(); it!=src.end(); it++) + if( remap.contains( it.key() ) ) + dst[ remap[it.key()] ] = it.value(); + + src = dst; + } + + // remap features of added attributes + for(QgsFeatureList::iterator fit=mAddedFeatures.begin(); fit!=mAddedFeatures.end(); fit++) + { + const QgsAttributeMap &src = fit->attributeMap(); + QgsAttributeMap dst; + + for(QgsAttributeMap::const_iterator it=src.begin(); it!=src.end(); it++) + if( remap.contains( it.key() ) ) + dst[ remap[it.key()] ] = it.value(); + + fit->setAttributeMap(dst); + } + + QgsFieldMap attributes; + + // update private field map + for(QMap::iterator it=remap.begin(); it!=remap.end(); it++) + attributes[ it.value() ] = mUpdatedFields[ it.key() ]; + + mUpdatedFields = attributes; + } + + if(attributeChangesOk) { + // + // change attributes + // + if ( mChangedAttributeValues.size() >0 ) + { + if( (cap & QgsVectorDataProvider::ChangeAttributeValues ) && mDataProvider->changeAttributeValues ( mChangedAttributeValues ) ) + { + mCommitErrors << tr( "SUCCESS: %1 attribute values changed." ).arg( mChangedAttributeValues.size() ); + mChangedAttributeValues.clear(); + } + else + { + mCommitErrors << tr( "ERROR: %1 attribute value changes not applied." ).arg( mChangedAttributeValues.size() ); + success = false; + } + } + + // + // add features + // + if ( mAddedFeatures.size() >0 ) + { + if( ( cap & QgsVectorDataProvider::AddFeatures ) && mDataProvider->addFeatures ( mAddedFeatures ) ) + { + mCommitErrors << tr( "SUCCESS: %1 features added." ).arg( mAddedFeatures.size() ); + mAddedFeatures.clear(); + } + else + { + mCommitErrors << tr( "ERROR: %1 features not added." ).arg( mAddedFeatures.size() ); + success = false; + } + } + } + + // + // update geometries + // + if ( mChangedGeometries.size() > 0 ) + { + if( (cap & QgsVectorDataProvider::ChangeGeometries) && mDataProvider->changeGeometryValues( mChangedGeometries ) ) + { + mCommitErrors << tr ( "SUCCESS: %1 geometries were changed." ).arg( mChangedGeometries.size() ); mChangedGeometries.clear(); - changedGeometriesOk = TRUE; - } - } - - // Attempt the commit of deleted features - bool deletedFeaturesOk = FALSE; - if (mDeletedFeatureIds.size() > 0) - { - if (!mDataProvider->deleteFeatures(mDeletedFeatureIds)) - { - QStringList errorStrings; - errorStrings += tr("Could not commit the deleted features."); - if (addedFeaturesOk) - { - errorStrings += tr("However, the added features were committed OK."); - } - if (changedAttributesOk) - { - errorStrings += tr("However, the changed attributes were committed OK."); - } - if (changedGeometriesOk) - { - errorStrings += tr("However, the changed geometries were committed OK."); - } - errorStrings += tr("No other types of changes will be committed at this time."); - - // TODO: use an error string or something to store error to be retrieved later [MD] - //QMessageBox::warning(0, tr("Error"), errorStrings.join(" ")); - return FALSE; } else { - // just in case some of those features are still selected - for (QgsFeatureIds::iterator it = mDeletedFeatureIds.begin(); it != mDeletedFeatureIds.end(); ++it) + mCommitErrors << tr ( "ERROR: %1 geometries not changed." ).arg( mChangedGeometries.size() ); + success = false; + } + } + + // + // delete features + // + if ( mDeletedFeatureIds.size() > 0 ) + { + if ( (cap & QgsVectorDataProvider::DeleteFeatures) && mDataProvider->deleteFeatures ( mDeletedFeatureIds ) ) + { + mCommitErrors << tr ( "SUCCESS: %1 features deleted." ).arg( mDeletedFeatureIds.size() ); + for(QgsFeatureIds::const_iterator it=mDeletedFeatureIds.begin(); it!=mDeletedFeatureIds.end(); it++) { - mSelectedFeatureIds.remove(*it); + mChangedAttributeValues.remove(*it); + mChangedGeometries.remove(*it); } - - // Deleted features committed OK, remove the in-memory changes mDeletedFeatureIds.clear(); - deletedFeaturesOk = TRUE; + } + else + { + mCommitErrors << tr ( "ERROR: %1 features not deleted." ).arg( mDeletedFeatureIds.size() ); + success = false; } } deleteCachedGeometries(); - - mEditable = false; - setModified(FALSE); - + + if(success) + { + mEditable = false; + setModified ( FALSE ); + + mUpdatedFields.clear(); + mMaxUpdatedIndex = -1; + + emit editingStopped(); + } + mDataProvider->updateExtents(); mDataProvider->updateFeatureCount(); triggerRepaint(); - - return TRUE; + + QgsDebugMsg( "result:\n " + mCommitErrors.join("\n ") ); + + return success; +} + +const QStringList &QgsVectorLayer::commitErrors() +{ + return mCommitErrors; } bool QgsVectorLayer::rollBack() @@ -2606,28 +2804,49 @@ bool QgsVectorLayer::rollBack() { return false; } - + if (isModified()) { - // roll back changed attributes - mChangedAttributes.clear(); + while( mAddedAttributeIds.size()>0 ) + { + int idx = *mAddedAttributeIds.begin(); + mAddedAttributeIds.remove(idx); + mUpdatedFields.remove(idx); + emit attributeDeleted(idx); + } + + while( mDeletedAttributeIds.size()>0 ) + { + int idx = *mDeletedAttributeIds.begin(); + mDeletedAttributeIds.remove(idx); + emit attributeAdded(idx); + } + + // roll back changed attribute values + mChangedAttributeValues.clear(); // roll back changed geometries mChangedGeometries.clear(); - + // Roll back added features // Delete the features themselves before deleting the references to them. mAddedFeatures.clear(); - + // Roll back deleted features mDeletedFeatureIds.clear(); + + // clear private field map + mUpdatedFields.clear(); + mMaxUpdatedIndex = -1; } deleteCachedGeometries(); mEditable = false; + emit editingStopped(); + setModified(FALSE); - + triggerRepaint(); return true; @@ -2657,21 +2876,21 @@ QgsFeatureList QgsVectorLayer::selectedFeatures() { return QgsFeatureList(); } - + QgsFeatureList features; QgsAttributeList allAttrs = mDataProvider->allAttributesList(); - - for (QgsFeatureIds::iterator it = mSelectedFeatureIds.begin(); it != mSelectedFeatureIds.end(); ++it) + + for (QgsFeatureIds::iterator it = mSelectedFeatureIds.begin(); it!=mSelectedFeatureIds.end(); ++it) { QgsFeature feat; - + bool selectionIsAddedFeature = FALSE; // Check this selected item against the uncommitted added features - for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) + for(QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) { - if ( (*it) == (*iter).featureId() ) + if ( *it == iter->featureId() ) { feat = QgsFeature(*iter); selectionIsAddedFeature = TRUE; @@ -2685,9 +2904,10 @@ QgsFeatureList QgsVectorLayer::selectedFeatures() mDataProvider->getFeatureAtId(*it, feat, true, allAttrs); } - // Transform the feature to the "current" in-memory version - features.append(QgsFeature(feat, mChangedAttributes, mChangedGeometries)); + updateFeatureAttributes(feat); + updateFeatureGeometry(feat); + features << feat; } // for each selected return features; @@ -2696,15 +2916,15 @@ QgsFeatureList QgsVectorLayer::selectedFeatures() bool QgsVectorLayer::addFeatures(QgsFeatureList features, bool makeSelected) { if (!mDataProvider) - { + { return false; } - + if(!(mDataProvider->capabilities() & QgsVectorDataProvider::AddFeatures)) { return false; } - + if (!isEditable()) { return false; @@ -2721,12 +2941,12 @@ bool QgsVectorLayer::addFeatures(QgsFeatureList features, bool makeSelected) if (makeSelected) { - mSelectedFeatureIds.insert((*iter).featureId()); + mSelectedFeatureIds.insert( iter->featureId() ); } } updateExtents(); - + if (makeSelected) { emit selectionChanged(); @@ -2740,7 +2960,8 @@ bool QgsVectorLayer::copySymbologySettings(const QgsMapLayer& other) { const QgsVectorLayer* vl = dynamic_cast(&other); - if(this == vl)//exit if both vectorlayer are the same + // exit if both vectorlayer are the same + if(this == vl) { return false; } @@ -2765,17 +2986,15 @@ bool QgsVectorLayer::copySymbologySettings(const QgsMapLayer& other) bool QgsVectorLayer::isSymbologyCompatible(const QgsMapLayer& other) const { - //vector layers are symbology compatible if they have the same type, the same sequence of numerical/ non numerical fields and the same field names - + // vector layers are symbology compatible if they have the same type, the same sequence of numerical/ non numerical fields and the same field names const QgsVectorLayer* otherVectorLayer = dynamic_cast(&other); if(otherVectorLayer) { - if(otherVectorLayer->vectorType() != vectorType()) - { - return false; - } + { + return false; + } const QgsFieldMap& fieldsThis = mDataProvider->fields(); const QgsFieldMap& fieldsOther = otherVectorLayer ->mDataProvider->fields(); @@ -2791,13 +3010,13 @@ bool QgsVectorLayer::isSymbologyCompatible(const QgsMapLayer& other) const for(uint i = 0; i < fieldsThisSize; ++i) { - if(fieldsThis[i].name() != fieldsOther[i].name())//field names need to be the same + if(fieldsThis[i].name() != fieldsOther[i].name()) // field names need to be the same { return false; } // TODO: compare types of the fields } - return true; //layers are symbology compatible if the code reaches this point + return true; // layers are symbology compatible if the code reaches this point } return false; } @@ -2806,7 +3025,7 @@ bool QgsVectorLayer::snapPoint(QgsPoint& point, double tolerance) { QMultiMap snapResults; int result = snapWithContext(point, tolerance, snapResults, QgsSnapper::SNAP_TO_VERTEX); - + if(result != 0) { return false; @@ -2824,40 +3043,39 @@ bool QgsVectorLayer::snapPoint(QgsPoint& point, double tolerance) } -int QgsVectorLayer::snapWithContext(const QgsPoint& startPoint, double snappingTolerance, QMultiMap& snappingResults, - QgsSnapper::SNAP_TO snap_to) +int QgsVectorLayer::snapWithContext(const QgsPoint& startPoint, double snappingTolerance, + QMultiMap& snappingResults, + QgsSnapper::SNAP_TO snap_to) { if (snappingTolerance<=0 || !mDataProvider) - { - return 1; - } - + { + return 1; + } + QList featureList; QgsRect searchRect(startPoint.x()-snappingTolerance, startPoint.y()-snappingTolerance, - startPoint.x()+snappingTolerance, startPoint.y()+snappingTolerance); + startPoint.x()+snappingTolerance, startPoint.y()+snappingTolerance); double sqrSnappingTolerance = snappingTolerance * snappingTolerance; - if(featuresInRectangle(searchRect, featureList, true, false) != 0) - { - return 2; - } + select(QgsAttributeList(), searchRect, true); - QList::iterator feature_it = featureList.begin(); - for(; feature_it != featureList.end(); ++feature_it) - { - snapToGeometry(startPoint, feature_it->featureId(), feature_it->geometry(), sqrSnappingTolerance, snappingResults, snap_to); - } + int n=0; + QgsFeature f; + while( getNextFeature(f) ) + { + snapToGeometry(startPoint, f.featureId(), f.geometry(), sqrSnappingTolerance, snappingResults, snap_to); + } - return 0; + return n==0 ? 2 : 0; } void QgsVectorLayer::snapToGeometry(const QgsPoint& startPoint, int featureId, QgsGeometry* geom, double sqrSnappingTolerance, - QMultiMap& snappingResults, QgsSnapper::SNAP_TO snap_to) const + QMultiMap& snappingResults, QgsSnapper::SNAP_TO snap_to) const { if(!geom) - { - return; - } + { + return; + } int atVertex, beforeVertex, afterVertex; double sqrDistVertexSnap, sqrDistSegmentSnap; @@ -2866,78 +3084,77 @@ void QgsVectorLayer::snapToGeometry(const QgsPoint& startPoint, int featureId, Q QgsSnappingResult snappingResultSegment; if(snap_to == QgsSnapper::SNAP_TO_VERTEX || snap_to == QgsSnapper::SNAP_TO_VERTEX_AND_SEGMENT) + { + snappedPoint = geom->closestVertex(startPoint, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap); + if(sqrDistVertexSnap < sqrSnappingTolerance) { - snappedPoint = geom->closestVertex(startPoint, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap); - if(sqrDistVertexSnap < sqrSnappingTolerance) - { - snappingResultVertex.snappedVertex = snappedPoint; - snappingResultVertex.snappedVertexNr = atVertex; - snappingResultVertex.beforeVertexNr = beforeVertex; - if(beforeVertex != -1) //make sure the vertex is valid - { - snappingResultVertex.beforeVertex = geom->vertexAt(beforeVertex); - } - snappingResultVertex.afterVertexNr = afterVertex; - if(afterVertex != -1) //make sure the vertex is valid - { - snappingResultVertex.afterVertex = geom->vertexAt(afterVertex); - } - snappingResultVertex.snappedAtGeometry = featureId; - snappingResultVertex.layer = this; - snappingResults.insert(sqrt(sqrDistVertexSnap), snappingResultVertex); - return; - } + snappingResultVertex.snappedVertex = snappedPoint; + snappingResultVertex.snappedVertexNr = atVertex; + snappingResultVertex.beforeVertexNr = beforeVertex; + if(beforeVertex != -1) // make sure the vertex is valid + { + snappingResultVertex.beforeVertex = geom->vertexAt(beforeVertex); + } + snappingResultVertex.afterVertexNr = afterVertex; + if(afterVertex != -1) // make sure the vertex is valid + { + snappingResultVertex.afterVertex = geom->vertexAt(afterVertex); + } + snappingResultVertex.snappedAtGeometry = featureId; + snappingResultVertex.layer = this; + snappingResults.insert(sqrt(sqrDistVertexSnap), snappingResultVertex); + return; } - if(snap_to == QgsSnapper::SNAP_TO_SEGMENT || snap_to == QgsSnapper::SNAP_TO_VERTEX_AND_SEGMENT) //snap to segment + } + if(snap_to == QgsSnapper::SNAP_TO_SEGMENT || snap_to == QgsSnapper::SNAP_TO_VERTEX_AND_SEGMENT) // snap to segment + { + if(vectorType() != QGis::Point) // cannot snap to segment for points/multipoints { - if(vectorType() != QGis::Point) //cannot snap to segment for points/multipoints - { - sqrDistSegmentSnap = geom->closestSegmentWithContext(startPoint, snappedPoint, afterVertex); + sqrDistSegmentSnap = geom->closestSegmentWithContext(startPoint, snappedPoint, afterVertex); - if(sqrDistSegmentSnap < sqrSnappingTolerance) - { - snappingResultSegment.snappedVertex = snappedPoint; - snappingResultSegment.snappedVertexNr = -1; - snappingResultSegment.beforeVertexNr = afterVertex - 1; - snappingResultSegment.afterVertexNr = afterVertex; - snappingResultSegment.snappedAtGeometry = featureId; - snappingResultSegment.beforeVertex = geom->vertexAt(afterVertex - 1); - snappingResultSegment.afterVertex = geom->vertexAt(afterVertex); - snappingResultSegment.layer = this; - snappingResults.insert(sqrt(sqrDistSegmentSnap), snappingResultSegment); - } - } + if(sqrDistSegmentSnap < sqrSnappingTolerance) + { + snappingResultSegment.snappedVertex = snappedPoint; + snappingResultSegment.snappedVertexNr = -1; + snappingResultSegment.beforeVertexNr = afterVertex - 1; + snappingResultSegment.afterVertexNr = afterVertex; + snappingResultSegment.snappedAtGeometry = featureId; + snappingResultSegment.beforeVertex = geom->vertexAt(afterVertex - 1); + snappingResultSegment.afterVertex = geom->vertexAt(afterVertex); + snappingResultSegment.layer = this; + snappingResults.insert(sqrt(sqrDistSegmentSnap), snappingResultSegment); + } } - + } + } int QgsVectorLayer::insertSegmentVerticesForSnap(const QList& snapResults) { int returnval = 0; QgsPoint layerPoint; - - QList::const_iterator it = snapResults.constBegin(); + + QList::const_iterator it=snapResults.constBegin(); for(; it != snapResults.constEnd(); ++it) + { + if(it->snappedVertexNr == -1) // segment snap { - if(it->snappedVertexNr == -1) //segment snap - { - layerPoint = it->snappedVertex; - if(!insertVertexBefore(layerPoint.x(), layerPoint.y(), it->snappedAtGeometry, it->afterVertexNr)) - { - returnval = 3; - } - } + layerPoint = it->snappedVertex; + if(!insertVertexBefore(layerPoint.x(), layerPoint.y(), it->snappedAtGeometry, it->afterVertexNr)) + { + returnval = 3; + } } - return returnval; + } + return returnval; } int QgsVectorLayer::boundingBoxFromPointList(const QList& list, double& xmin, double& ymin, double& xmax, double& ymax) const - { if(list.size() < 1) - { - return 1; - } + { + return 1; + } xmin = std::numeric_limits::max(); xmax = -std::numeric_limits::max(); @@ -2945,24 +3162,24 @@ int QgsVectorLayer::boundingBoxFromPointList(const QList& list, double ymax = -std::numeric_limits::max(); for(QList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) + { + if(it->x() < xmin) { - if(it->x() < xmin) - { - xmin = it->x(); - } - if(it->x() > xmax) - { - xmax = it->x(); - } - if(it->y() < ymin) - { - ymin = it->y(); - } - if(it->y() > ymax) - { - ymax = it->y(); - } + xmin = it->x(); } + if(it->x() > xmax) + { + xmax = it->x(); + } + if(it->y() < ymin) + { + ymin = it->y(); + } + if(it->y() > ymax) + { + ymax = it->y(); + } + } return 0; } @@ -2972,13 +3189,13 @@ QgsVectorLayer::VertexMarkerType QgsVectorLayer::currentVertexMarkerType() QSettings settings; QString markerTypeString = settings.value("/qgis/digitizing/marker_style", "SemiTransparentCircle").toString(); if(markerTypeString == "Cross") - { - return QgsVectorLayer::Cross; - } + { + return QgsVectorLayer::Cross; + } else - { - return QgsVectorLayer::SemiTransparentCircle; - } + { + return QgsVectorLayer::SemiTransparentCircle; + } } void QgsVectorLayer::drawFeature(QPainter* p, @@ -2987,7 +3204,7 @@ void QgsVectorLayer::drawFeature(QPainter* p, const QgsCoordinateTransform* ct, QImage * marker, double widthScale, - double rasterScaleFactor, + double rasterScaleFactor, bool drawingToEditingCanvas) { // Only have variables, etc outside the switch() statement that are @@ -3004,10 +3221,6 @@ void QgsVectorLayer::drawFeature(QPainter* p, QGis::WKBTYPE wkbType = geom->wkbType(); -#ifdef QGISDEBUG - //std::cout <<"Entering drawFeature()" << std::endl; -#endif - switch (wkbType) { case QGis::WKBPoint: @@ -3016,19 +3229,13 @@ void QgsVectorLayer::drawFeature(QPainter* p, double x = *((double *) (feature + 5)); double y = *((double *) (feature + 5 + sizeof(double))); -#ifdef QGISDEBUG - // std::cout <<"...WKBPoint (" << x << ", " << y << ")" <width()/2), y - (marker->height()/2)); QPointF pt(x*rasterScaleFactor - (marker->width()/2), y*rasterScaleFactor - (marker->height()/2)); p->save(); //p->scale(markerScaleFactor,markerScaleFactor); - p->scale(1.0/rasterScaleFactor, 1.0/rasterScaleFactor); + p->scale(1.0/rasterScaleFactor, 1.0/rasterScaleFactor); p->drawImage(pt, *marker); p->restore(); @@ -3043,7 +3250,7 @@ void QgsVectorLayer::drawFeature(QPainter* p, p->save(); //p->scale(markerScaleFactor, markerScaleFactor); - p->scale(1.0/rasterScaleFactor, 1.0/rasterScaleFactor); + p->scale(1.0/rasterScaleFactor, 1.0/rasterScaleFactor); for (register unsigned int i = 0; i < nPoints; ++i) { @@ -3052,19 +3259,19 @@ void QgsVectorLayer::drawFeature(QPainter* p, ptr += sizeof(double); double y = *((double *) ptr); ptr += sizeof(double); - + if (wkbType == QGis::WKBMultiPoint25D) // ignore Z value ptr += sizeof(double); -#ifdef QGISDEBUG +#ifdef QGISDEBUG std::cout <<"...WKBMultiPoint (" << x << ", " << y << ")" <width()/2), y - (marker->height()/2)); //QPointF pt(x/markerScaleFactor - (marker->width()/2), y/markerScaleFactor - (marker->height()/2)); - QPointF pt(x, y); - + QPointF pt(x, y); + #if defined(Q_WS_X11) // Work around a +/- 32768 limitation on coordinates in X11 if (std::abs(x) > QgsClipper::maxX || @@ -3094,7 +3301,7 @@ void QgsVectorLayer::drawFeature(QPainter* p, unsigned char* ptr = feature + 5; unsigned int numLineStrings = *((int*)ptr); ptr = feature + 9; - + for (register unsigned int jdx = 0; jdx < numLineStrings; jdx++) { ptr = drawLineString(ptr, @@ -3124,7 +3331,7 @@ void QgsVectorLayer::drawFeature(QPainter* p, for (register unsigned int kdx = 0; kdx < numPolygons; kdx++) ptr = drawPolygon(ptr, p, - theMapToPixelTransform, + theMapToPixelTransform, ct, drawingToEditingCanvas); break; @@ -3140,37 +3347,14 @@ void QgsVectorLayer::drawFeature(QPainter* p, void QgsVectorLayer::setCoordinateSystem() { QgsDebugMsg("QgsVectorLayer::setCoordinateSystem ----- Computing Coordinate System"); - + // - // Get the layers project info and set up the QgsCoordinateTransform + // Get the layers project info and set up the QgsCoordinateTransform // for this layer // - + // get SRS directly from provider *mSRS = mDataProvider->getSRS(); - - /* - - // XXX old stuff - - int srid = getProjectionSrid(); - - if(srid == 0) - { - QString mySourceWKT(getProjectionWKT()); - if (mySourceWKT.isNull()) - { - mySourceWKT=QString(""); - } - QgsDebugMsg("QgsVectorLayer::setCoordinateSystem --- using wkt " + mySourceWKT); - mSRS->createFromWkt(mySourceWKT); - } - else - { - QgsDebugMsg("QgsVectorLayer::setCoordinateSystem --- using srid " + QString::number(srid)); - mSRS->createFromSrid(srid); - } - */ //QgsSpatialRefSys provides a mechanism for FORCE a srs to be valid //which is inolves falling back to system, project or user selected @@ -3180,65 +3364,11 @@ void QgsVectorLayer::setCoordinateSystem() { mSRS->validate(); } - -} - -bool QgsVectorLayer::commitAttributeChanges(const QgsAttributeIds& deleted, - const QgsNewAttributesMap& added, - const QgsChangedAttributesMap& changed) -{ - bool returnvalue=true; - if(mDataProvider->capabilities()&QgsVectorDataProvider::DeleteAttributes) - { - //delete attributes in all not commited features - for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - for (QgsAttributeIds::const_iterator it = deleted.begin(); it != deleted.end(); ++it) - { - (*iter).deleteAttribute(*it); - } - } - - //and then in the provider - if(!mDataProvider->deleteAttributes(deleted)) - { - returnvalue=false; - } - } - - if(mDataProvider->capabilities()&QgsVectorDataProvider::AddAttributes) - { - //add attributes in all not commited features - // TODO: is it necessary? [MD] - /*for (QgsFeatureList::iterator iter = mAddedFeatures.begin(); iter != mAddedFeatures.end(); ++iter) - { - for (QgsNewAttributesMap::const_iterator it = added.begin(); it != added.end(); ++it) - { - (*iter).addAttribute(, QgsFeatureAttribute(it.key(), "")); - } - }*/ - - //and then in the provider - if(!mDataProvider->addAttributes(added)) - { - returnvalue=false; - } - } - - if(mDataProvider->capabilities()&QgsVectorDataProvider::ChangeAttributeValues) - { - //and then those of the commited ones - if(!mDataProvider->changeAttributeValues(changed)) - { - returnvalue=false; - } - } - return returnvalue; } // Convenience function to transform the given point -inline void QgsVectorLayer::transformPoint(double& x, - double& y, +inline void QgsVectorLayer::transformPoint( + double& x, double& y, const QgsMapToPixel* mtp, const QgsCoordinateTransform* ct) { @@ -3249,7 +3379,7 @@ inline void QgsVectorLayer::transformPoint(double& x, ct->transformInPlace(x, y, z); } - // transform from projected coordinate system to pixel + // transform from projected coordinate system to pixel // position on map canvas mtp->transformInPlace(x, y); } @@ -3262,7 +3392,7 @@ inline void QgsVectorLayer::transformPoints( if (ct) ct->transformInPlace(x, y, z); - // transform from projected coordinate system to pixel + // transform from projected coordinate system to pixel // position on map canvas mtp->transformInPlace(x, y); } @@ -3283,23 +3413,52 @@ bool QgsVectorLayer::isModified() const return mModified; } -QgsFeatureList& QgsVectorLayer::addedFeatures() -{ - return mAddedFeatures; -} - -QgsFeatureIds& QgsVectorLayer::deletedFeatureIds() -{ - return mDeletedFeatureIds; -} - -QgsChangedAttributesMap& QgsVectorLayer::changedAttributes() -{ - return mChangedAttributes; -} - void QgsVectorLayer::setModified(bool modified, bool onlyGeometry) { mModified = modified; emit wasModified(onlyGeometry); } + +QgsVectorLayer::EditType QgsVectorLayer::editType(int idx) +{ + const QgsFieldMap &fields = pendingFields(); + if( fields.contains(idx) && mEditTypes.contains( fields[idx].name() ) ) + return mEditTypes[ fields[idx].name() ]; + else + return LineEdit; +} + +void QgsVectorLayer::setEditType(int idx, EditType type) +{ + const QgsFieldMap &fields = pendingFields(); + if( fields.contains(idx) ) + mEditTypes[ fields[idx].name() ] = type; +} + +QMap< QString, QVariant > &QgsVectorLayer::valueMap(int idx) +{ + const QgsFieldMap &fields = pendingFields(); + + // FIXME: throw an exception!? + if( fields.contains(idx) ) + QgsDebugMsg( QString("field %1 not found").arg(idx) ); + + if( !mValueMaps.contains( fields[idx].name() ) ) + mValueMaps[ fields[idx].name() ] = QMap(); + + return mValueMaps[ fields[idx].name() ]; +} + +QgsVectorLayer::RangeData &QgsVectorLayer::range(int idx) +{ + const QgsFieldMap &fields = pendingFields(); + + // FIXME: throw an exception!? + if( fields.contains(idx) ) + QgsDebugMsg( QString("field %1 not found").arg(idx) ); + + if( !mRanges.contains( fields[idx].name() ) ) + mRanges[ fields[idx].name() ] = RangeData(); + + return mRanges[ fields[idx].name() ]; +} diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index e63d924ac15..68e24383df0 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -22,20 +22,20 @@ #include #include #include -#include +#include #include "qgis.h" #include "qgsmaplayer.h" #include "qgsfeature.h" #include "qgssnapper.h" +#include "qgsfeature.h" +#include "qgsfield.h" class QPainter; class QImage; class QgsAttributeAction; class QgsCoordinateTransform; -class QgsField; -class QgsFeature; class QgsGeometry; class QgsGeometryVertexIndex; class QgsMapToPixel; @@ -44,34 +44,39 @@ class QgsRect; class QgsRenderer; class QgsVectorDataProvider; - class QgsGeometry; class QgsRect; -class QgsFeature; - -//typedef QList QgsFieldList; -typedef QList QgsFeatureList; -typedef QList QgsAttributeList; +typedef QList QgsAttributeList; typedef QSet QgsFeatureIds; typedef QSet QgsAttributeIds; -// key = attribute name, value = attribute type -typedef QMap QgsNewAttributesMap; - -typedef QMap QgsFieldMap; - - - /*! \class QgsVectorLayer * \brief Vector layer backed by a data source provider */ - class CORE_EXPORT QgsVectorLayer : public QgsMapLayer { Q_OBJECT public: + enum EditType { + LineEdit, + UniqueValues, + UniqueValuesEditable, + ValueMap, + Classification, + Range, + }; + + struct RangeData { + RangeData() {} + RangeData(QVariant theMin, QVariant theMax, QVariant theStep) + : mMin(theMin), mMax(theMax), mStep(theStep) {} + + QVariant mMin; + QVariant mMax; + QVariant mStep; + }; /** Constructor */ QgsVectorLayer(QString path = 0, QString baseName = 0, @@ -113,35 +118,31 @@ public: /** The number of features that are selected in this layer */ int selectedFeatureCount(); - + /** Select features found within the search rectangle (in layer's coordinates) */ void select(QgsRect & rect, bool lock); - + /** Select not selected features and deselect selected ones */ void invertSelection(); - /** Get a copy of the user-selected features */ + /** Get a copy of the user-selected features */ QgsFeatureList selectedFeatures(); - + /** Return reference to identifiers of selected features */ const QgsFeatureIds& selectedFeaturesIds() const; - + /** Change selection to the new set of features */ void setSelectedFeatures(const QgsFeatureIds& ids); /** Returns the bounding box of the selected features. If there is no selection, QgsRect(0,0,0,0) is returned */ QgsRect boundingBoxOfSelected(); - - /** Insert a copy of the given features into the layer */ - bool addFeatures(QgsFeatureList features, bool makeSelected = TRUE); - /** Copies the symbology settings from another layer. Returns true in case of success */ bool copySymbologySettings(const QgsMapLayer& other); /** Returns true if this layer can be in the same symbology group with another layer */ bool isSymbologyCompatible(const QgsMapLayer& other) const; - + /** Returns a pointer to the renderer */ const QgsRenderer* renderer() const; @@ -196,13 +197,15 @@ public: */ virtual QString subsetString(); - /**Returns the features contained in the rectangle. Considers the changed, added, deleted and permanent features - @return 0 in case of success*/ - int featuresInRectangle(const QgsRect& searchRect, QList& features, bool fetchGeometries = true, bool fetchAttributes = true); + void select(QgsAttributeList fetchAttributes, + QgsRect rect = QgsRect(), + bool fetchGeometry = true); + + bool getNextFeature(QgsFeature& feature); /**Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features @return 0 in case of success*/ - int getFeatureAtId(int featureId, QgsFeature& f, bool fetchGeometries = true, bool fetchAttributes = true); + int getFeatureAtId(int featureId, QgsFeature &f, bool fetchGeometries = true, bool fetchAttributes = true); /** Adds a feature @param lastFeatureInBatch If True, will also go to the effort of e.g. updating the extents. @@ -233,14 +236,24 @@ public: bool deleteSelectedFeatures(); /**Adds a ring to polygon/multipolygon features - @return 0 in case of success, 1 problem with feature type, 2 ring not closed, 3 ring not valid, 4 ring crosses \ -existing rings, 5 no feature found where ring can be inserted*/ + @return + 0 in case of success, + 1 problem with feature type, + 2 ring not closed, + 3 ring not valid, + 4 ring crosses existing rings, + 5 no feature found where ring can be inserted*/ int addRing(const QList& ring); /**Adds a new island polygon to a multipolygon feature - @return 0 in case of success, 1 if selected feature is not multipolygon, 2 if ring is not a valid geometry, \ -3if new polygon ring not disjoint with existing rings, 4 if no feature was selected, 5 if several features are selected, \ -6 if selected geometry not found*/ + @return + 0 in case of success, + 1 if selected feature is not multipolygon, + 2 if ring is not a valid geometry, + 3 if new polygon ring not disjoint with existing rings, + 4 if no feature was selected, + 5 if several features are selected, + 6 if selected geometry not found*/ int addIsland(const QList& ring); /**Translates feature by dx, dy @@ -256,7 +269,7 @@ existing rings, 5 no feature found where ring can be inserted*/ @return 0 in case of success*/ int splitFeatures(const QList& splitLine, bool topologicalEditing = false); - /**Changes the specified geometry such that it has no intersections with other \ + /**Changes the specified geometry such that it has no intersections with other polygon (or multipolygon) geometries in this vector layer @param geom geometry to modify @return 0 in case of success*/ @@ -309,30 +322,8 @@ existing rings, 5 no feature found where ring can be inserted*/ @param snap_to to segment / to vertex @return 0 in case of success */ - int snapWithContext(const QgsPoint& startPoint, double snappingTolerance, QMultiMap& snappingResults, \ - QgsSnapper::SNAP_TO snap_to); - - /** - Commits edited attributes. Depending on the feature id, - the changes are written to not commited features or redirected to - the data provider - - The commits (in this version) occur in three distinct stages, - (delete attributes, add attributes, change attribute values) - so if a stage fails, it's difficult to roll back cleanly. - - \todo Need to indicate at which stage the failed commit occurred, - for better cleanup and recovery from the error. - - \param deleted Set of attribute indices (i.e. columns) to delete - \param added Map (name, type) of attribute names (i.e. columns) to add - \param changed Map (feature ID, Map (attribute name, new value) ) - of attribute values to change - - */ - bool commitAttributeChanges(const QgsAttributeIds& deleted, - const QgsNewAttributesMap& added, - const QgsChangedAttributesMap& changed); + int snapWithContext(const QgsPoint& startPoint, double snappingTolerance, QMultiMap& snappingResults, + QgsSnapper::SNAP_TO snap_to); /** Draws the layer * @return FALSE if an error occurred during drawing @@ -347,21 +338,37 @@ existing rings, 5 no feature found where ring can be inserted*/ */ void drawLabels(QPainter * p, const QgsRect& viewExtent, const QgsMapToPixel* cXf, const QgsCoordinateTransform* ct, double scale); - /** returns array of added features */ - QgsFeatureList& addedFeatures(); + /** returns fields list which are not commited */ + const QgsFieldMap &pendingFields(); - /** returns array of deleted feature IDs */ - QgsFeatureIds& deletedFeatureIds(); - - /** returns array of features with changed attributes */ - QgsChangedAttributesMap& changedAttributes(); + /** returns list of attributes */ + QgsAttributeList pendingAllAttributesList(); + + /** returns feature count after commit */ + int pendingFeatureCount(); /** Sets whether some features are modified or not */ void setModified(bool modified = TRUE, bool onlyGeometryWasModified = FALSE); - + /** Make layer editable */ bool startEditing(); - + + /** changed an attribute value (but does not commit it */ + bool changeAttributeValue(int fid, int field, QVariant value, bool emitSignal = true); + + /** add an attribute field (but does not commit it) + returns the field index or -1 in case of failure */ + bool addAttribute(QString name, QString type); + + /** delete an attribute field (but does not commit it) */ + bool deleteAttribute(int attr); + + /** Insert a copy of the given features into the layer (but does not commit it) */ + bool addFeatures(QgsFeatureList features, bool makeSelected = TRUE); + + /** delete a feature from the layer (but does not commit it) */ + bool deleteFeature(int fid); + /** Attempts to commit any changes to disk. Returns the result of the attempt. If a commit fails, the in-memory changes are left alone. @@ -370,20 +377,31 @@ existing rings, 5 no feature found where ring can be inserted*/ disallowed value in a Postgres database - the user can re-edit and try again. - The commits (in this version) occur in four distinct stages, - (add features, change attributes, change geometries, delete features) + The commits occur in distinct stages, + (add attributes, add features, change attribute values, change geometries, delete features, delete attributes) so if a stage fails, it's difficult to roll back cleanly. Therefore any error message also includes which stage failed so that the user has some chance of repairing the damage cleanly. - */ bool commitChanges(); + const QStringList &commitErrors(); /** Stop editing and discard the edits */ bool rollBack(); -public slots: + /**get edit type*/ + EditType editType(int idx); + /**set edit type*/ + void setEditType(int idx, EditType edit); + + /**access value map*/ + QMap &valueMap(int idx); + + /**access range */ + RangeData &range(int idx); + +public slots: /** Select feature by its ID, optionally emit signal selectionChanged() */ void select(int featureId, bool emitSignal = TRUE); @@ -405,6 +423,15 @@ signals: /** This signal is emitted when modifications has been done on layer */ void wasModified(bool onlyGeometry); + void editingStarted(); + void editingStopped(); + void attributeAdded(int idx); + void attributeDeleted(int idx); + void featureDeleted(int fid); + void layerDeleted(); + + void attributeValueChanged(int fid, int idx, const QVariant &); + private: // Private methods enum VertexMarkerType @@ -424,7 +451,7 @@ private: // Private methods @todo XXX should this return bool? Throw exceptions? */ bool setDataProvider( QString const & provider ); - + /** Draws features. May cause projections exceptions to be generated * (i.e., code that calls this function needs to catch them */ @@ -433,7 +460,7 @@ private: // Private methods const QgsMapToPixel* cXf, const QgsCoordinateTransform* ct, QImage* marker, - double widthScale, + double widthScale, double markerScaleFactor, bool drawingToEditingCanvas); @@ -447,19 +474,19 @@ private: // Private methods /** Draw the linestring as given in the WKB format. Returns a pointer * to the byte after the end of the line string binary data stream (WKB). */ - unsigned char* drawLineString(unsigned char* WKBlinestring, - QPainter* p, - const QgsMapToPixel* mtp, - const QgsCoordinateTransform* ct, + unsigned char *drawLineString(unsigned char *WKBlinestring, + QPainter *p, + const QgsMapToPixel *mtp, + const QgsCoordinateTransform *ct, bool drawingToEditingCanvas); /** Draw the polygon as given in the WKB format. Returns a pointer to * the byte after the end of the polygon binary data stream (WKB). */ - unsigned char* drawPolygon(unsigned char* WKBpolygon, - QPainter* p, - const QgsMapToPixel* mtp, - const QgsCoordinateTransform* ct, + unsigned char *drawPolygon(unsigned char *WKBpolygon, + QPainter *p, + const QgsMapToPixel *mtp, + const QgsCoordinateTransform *ct, bool drawingToEditingCanvas); /** Goes through all features and finds a free id (e.g. to give it temporarily to a not-commited feature) */ @@ -478,7 +505,7 @@ private: // Private methods @param snappingResult list to which the result is appended @param snap_to snap to vertex or to segment */ - void snapToGeometry(const QgsPoint& startPoint, int featureId, QgsGeometry* geom, double sqrSnappingTolerance, \ + void snapToGeometry(const QgsPoint& startPoint, int featureId, QgsGeometry* geom, double sqrSnappingTolerance, QMultiMap& snappingResults, QgsSnapper::SNAP_TO snap_to) const; /**Little helper function that gives bounding box from a list of points. @@ -488,6 +515,11 @@ private: // Private methods /**Reads vertex marker type from settings*/ QgsVectorLayer::VertexMarkerType currentVertexMarkerType(); + /**Update feature with uncommited attribute updates*/ + void updateFeatureAttributes(QgsFeature &f); + + /**Update feature with uncommited geometry updates*/ + void updateFeatureGeometry(QgsFeature &f); private: // Private attributes @@ -513,36 +545,48 @@ private: // Private attributes /** Flag indicating whether the layer has been modified since the last commit */ bool mModified; - + /** cache of the committed geometries retrieved *for the current display* */ QgsGeometryMap mCachedGeometries; - + /** Set holding the feature IDs that are activated. Note that if a feature subsequently gets deleted (i.e. by its addition to mDeletedFeatureIds), it always needs to be removed from mSelectedFeatureIds as well. - */ + */ QgsFeatureIds mSelectedFeatureIds; - + /** Deleted feature IDs which are not commited. Note a feature can be added and then deleted again before the change is committed - in that case the added feature would be removed from mAddedFeatures only and *not* entered here. - */ + */ QgsFeatureIds mDeletedFeatureIds; - + /** New features which are not commited. Note a feature can be added and then changed, - therefore the details here can be overridden by mChangedAttributes and mChangedGeometries. - */ + therefore the details here can be overridden by mChangedAttributeValues and mChangedGeometries. + */ QgsFeatureList mAddedFeatures; - - /** Changed attributes which are not commited */ - QgsChangedAttributesMap mChangedAttributes; - + + /** Changed attributes values which are not commited */ + QgsChangedAttributesMap mChangedAttributeValues; + + /** deleted attributes fields which are not commited */ + QgsAttributeIds mDeletedAttributeIds; + + /** added attributes fields which are not commited */ + QgsAttributeIds mAddedAttributeIds; + /** Changed geometries which are not commited. */ QgsGeometryMap mChangedGeometries; - + + /** field map to commit */ + QgsFieldMap mUpdatedFields; + + /** max field index */ + int mMaxUpdatedIndex; + /** Geometry type as defined in enum WKBTYPE (qgis.h) */ int mGeometryType; - + /** Renderer object which holds the information about how to display the features */ QgsRenderer *mRenderer; @@ -552,6 +596,20 @@ private: // Private attributes /** Display labels */ bool mLabelOn; + QStringList mCommitErrors; + + QMap< QString, EditType > mEditTypes; + QMap< QString, QMap > mValueMaps; + QMap< QString, RangeData > mRanges; + + bool mFetching; + QgsRect mFetchRect; + QgsAttributeList mFetchAttributes; + bool mFetchGeometry; + + QSet mFetchConsidered; + QgsGeometryMap::iterator mFetchChangedGeomIt; + QgsFeatureList::iterator mFetchAddedFeaturesIt; }; #endif diff --git a/src/core/renderer/qgsgraduatedsymbolrenderer.cpp b/src/core/renderer/qgsgraduatedsymbolrenderer.cpp index c65798a602e..d8924abdb66 100644 --- a/src/core/renderer/qgsgraduatedsymbolrenderer.cpp +++ b/src/core/renderer/qgsgraduatedsymbolrenderer.cpp @@ -156,7 +156,7 @@ void QgsGraduatedSymbolRenderer::renderFeature(QPainter * p, QgsFeature & f, QIm } } -QgsSymbol* QgsGraduatedSymbolRenderer::symbolForFeature(const QgsFeature* f) +QgsSymbol *QgsGraduatedSymbolRenderer::symbolForFeature(const QgsFeature* f) { //first find out the value for the classification attribute const QgsAttributeMap& attrs = f->attributeMap(); diff --git a/src/core/renderer/qgsgraduatedsymbolrenderer.h b/src/core/renderer/qgsgraduatedsymbolrenderer.h index 04cfb553fe0..b4928db109b 100644 --- a/src/core/renderer/qgsgraduatedsymbolrenderer.h +++ b/src/core/renderer/qgsgraduatedsymbolrenderer.h @@ -40,7 +40,7 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer void removeSymbols(); /** Determines if a feature will be rendered or not @param f a pointer to the feature to determine if rendering will happen*/ - bool willRenderFeature(QgsFeature *f); + virtual bool willRenderFeature(QgsFeature *f); /**Renders an OGRFeature \param p a painter (usually the one from the current map canvas) \param f a pointer to a feature to render @@ -72,7 +72,7 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer int mClassificationField; /**List holding the symbols for the individual classes*/ QList mSymbols; - QgsSymbol* symbolForFeature(const QgsFeature* f); + QgsSymbol *symbolForFeature(const QgsFeature* f); /**Cached copy of all underlying symbols required attribute fields*/ QgsAttributeList mSymbolAttributes; diff --git a/src/core/renderer/qgssinglesymbolrenderer.cpp b/src/core/renderer/qgssinglesymbolrenderer.cpp index 6017eb0b232..6f3d4aa17b2 100644 --- a/src/core/renderer/qgssinglesymbolrenderer.cpp +++ b/src/core/renderer/qgssinglesymbolrenderer.cpp @@ -103,13 +103,13 @@ void QgsSingleSymbolRenderer::renderFeature(QPainter * p, QgsFeature & f, QImage //first find out the value for the scale classification attribute const QgsAttributeMap& attrs = f.attributeMap(); fieldScale = sqrt(fabs(attrs[mSymbol->scaleClassificationField()].toDouble())); - QgsDebugMsg(QString("Feature has field scale factor %1").arg(fieldScale)); + QgsDebugMsgLevel(QString("Feature has field scale factor %1").arg(fieldScale), 3); } if ( mSymbol->rotationClassificationField() >= 0 ) { const QgsAttributeMap& attrs = f.attributeMap(); rotation = attrs[mSymbol->rotationClassificationField()].toDouble(); - QgsDebugMsg(QString("Feature has rotation factor %1").arg(rotation)); + QgsDebugMsgLevel(QString("Feature has rotation factor %1").arg(rotation), 3); } *img = mSymbol->getPointSymbolAsImage( widthScale, selected, mSelectionColor, fieldScale, rotation, rasterScaleFactor); diff --git a/src/core/renderer/qgssinglesymbolrenderer.h b/src/core/renderer/qgssinglesymbolrenderer.h index f5d50213ff4..0aaf4a426be 100644 --- a/src/core/renderer/qgssinglesymbolrenderer.h +++ b/src/core/renderer/qgssinglesymbolrenderer.h @@ -34,7 +34,7 @@ class CORE_EXPORT QgsSingleSymbolRenderer: public QgsRenderer void addSymbol(QgsSymbol* sy); /*Returns a pointer to mSymbol*/ const QgsSymbol* symbol() const; - /**Renders an OGRFeature*/ + /**Renders a feature*/ void renderFeature(QPainter* p, QgsFeature& f, QImage* img, bool selected, double widthScale = 1.0, double rasterScaleFactor = 1.0); /**Reads the renderer configuration from an XML file @param rnode the DOM node to read diff --git a/src/core/renderer/qgsuniquevaluerenderer.cpp b/src/core/renderer/qgsuniquevaluerenderer.cpp index a6bfc192c50..11ec7d570c3 100644 --- a/src/core/renderer/qgsuniquevaluerenderer.cpp +++ b/src/core/renderer/qgsuniquevaluerenderer.cpp @@ -165,7 +165,7 @@ void QgsUniqueValueRenderer::renderFeature(QPainter* p, QgsFeature& f,QImage* im } } -QgsSymbol* QgsUniqueValueRenderer::symbolForFeature(const QgsFeature* f) +QgsSymbol *QgsUniqueValueRenderer::symbolForFeature(const QgsFeature *f) { //first find out the value const QgsAttributeMap& attrs = f->attributeMap(); diff --git a/src/core/renderer/qgsuniquevaluerenderer.h b/src/core/renderer/qgsuniquevaluerenderer.h index cd9e625d934..8aa0adf6b0f 100644 --- a/src/core/renderer/qgsuniquevaluerenderer.h +++ b/src/core/renderer/qgsuniquevaluerenderer.h @@ -30,7 +30,7 @@ class CORE_EXPORT QgsUniqueValueRenderer: public QgsRenderer virtual ~QgsUniqueValueRenderer(); /** Determines if a feature will be rendered or not @param f a pointer to the feature to determine if rendering will happen*/ - bool willRenderFeature(QgsFeature *f); + virtual bool willRenderFeature(QgsFeature *f); void renderFeature(QPainter* p, QgsFeature& f,QImage* img, bool selected, double widthScale = 1.0, double rasterScaleFactor = 1.0); /**Reads the renderer configuration from an XML file @param rnode the DOM node to read @@ -64,7 +64,7 @@ class CORE_EXPORT QgsUniqueValueRenderer: public QgsRenderer /**Symbols for the unique values*/ QMap mSymbols; /**Returns the symbol for a feature or 0 if there isn't any*/ - QgsSymbol* symbolForFeature(const QgsFeature* f); + QgsSymbol *symbolForFeature(const QgsFeature* f); /**Cached copy of all underlying symbols required attribute fields*/ QgsAttributeList mSymbolAttributes; bool mSymbolAttributesDirty; // insertValue was called diff --git a/src/core/symbology/qgssymbol.cpp b/src/core/symbology/qgssymbol.cpp index f0cc3197a1a..0a67a767c30 100644 --- a/src/core/symbology/qgssymbol.cpp +++ b/src/core/symbology/qgssymbol.cpp @@ -324,7 +324,7 @@ QImage QgsSymbol::getCachedPointSymbolAsImage( double widthScale, } } -QImage QgsSymbol::getPointSymbolAsImage( double widthScale, bool selected, QColor selectionColor, double scale, \ +QImage QgsSymbol::getPointSymbolAsImage( double widthScale, bool selected, QColor selectionColor, double scale, double rotation, double rasterScaleFactor) { //QgsDebugMsg(QString("Symbol scale = %1, and rotation = %2").arg(scale).arg(rotation)); diff --git a/src/plugins/grass/qgsgrassmodule.h b/src/plugins/grass/qgsgrassmodule.h index 115ef75df3a..64a419d5ea5 100644 --- a/src/plugins/grass/qgsgrassmodule.h +++ b/src/plugins/grass/qgsgrassmodule.h @@ -44,6 +44,8 @@ class QValidator; // Must be here, so that it is included to moc file #include "qgsvectorlayer.h" +#include "qgsfield.h" + class QgisInterface; class QgsMapCanvas; @@ -490,9 +492,9 @@ public: * \param qdesc option element in QGIS module description XML file * \param gdesc GRASS module XML description file */ - QgsGrassModuleInput ( QgsGrassModule *module, - QgsGrassModuleStandardOptions *options, QString key, - QDomElement &qdesc, QDomElement &gdesc, QDomNode &gnode, + QgsGrassModuleInput ( QgsGrassModule *module, + QgsGrassModuleStandardOptions *options, QString key, + QDomElement &qdesc, QDomElement &gdesc, QDomNode &gnode, QWidget * parent = 0 ); //! Destructor diff --git a/src/providers/gpx/qgsgpxprovider.h b/src/providers/gpx/qgsgpxprovider.h index c771e9719ac..5e8af4db7d4 100644 --- a/src/providers/gpx/qgsgpxprovider.h +++ b/src/providers/gpx/qgsgpxprovider.h @@ -37,7 +37,7 @@ class GPSData; */ class QgsGPXProvider : public QgsVectorDataProvider { - Q_OBJECT; + Q_OBJECT public: diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 1b900be7d32..3ff0e5cc538 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -36,16 +36,6 @@ email : sherman at mrcc.com #include #include -//TODO Following ifndef can be removed once WIN32 GEOS support -// is fixed -#ifndef NOWIN32GEOSXXX -//XXX GEOS support on windows is broken until we can get VC++ to -// tolerate geos.h without throwing a bunch of type errors. It -// appears that the windows version of GEOS may be compiled with -// MINGW rather than VC++. -#endif - - #include "qgsapplication.h" #include "qgsdataprovider.h" #include "qgsfeature.h" @@ -134,13 +124,9 @@ QgsOgrProvider::QgsOgrProvider(QString const & uri) valid = false; } - // create the geos objects - geometryFactory = new GEOS_GEOM::GeometryFactory(); - assert(geometryFactory!=0); - - mSupportedNativeTypes.insert("Integer"); - mSupportedNativeTypes.insert("Real"); - mSupportedNativeTypes.insert("String"); + mSupportedNativeTypes.insert("Integer", QVariant::Int); + mSupportedNativeTypes.insert("Real", QVariant::Double); + mSupportedNativeTypes.insert("String", QVariant::String); } QgsOgrProvider::~QgsOgrProvider() @@ -149,7 +135,6 @@ QgsOgrProvider::~QgsOgrProvider() ogrDataSource = 0; free(extent_); extent_ = 0; - delete geometryFactory; if( mSelectionRectangle ) { OGR_G_DestroyGeometry( mSelectionRectangle ); diff --git a/src/providers/ogr/qgsogrprovider.h b/src/providers/ogr/qgsogrprovider.h index d7859241902..93193505389 100644 --- a/src/providers/ogr/qgsogrprovider.h +++ b/src/providers/ogr/qgsogrprovider.h @@ -19,23 +19,10 @@ email : sherman at mrcc.com #include "qgsrect.h" #include "qgsvectordataprovider.h" -#include -#if GEOS_VERSION_MAJOR < 3 -#include -#define GEOS_GEOM geos -#else -#include -#define GEOS_GEOM geos::geom -#endif - class QgsFeature; class QgsField; -typedef void *OGRDataSourceH; -typedef void *OGRSFDriverH; -typedef void *OGRLayerH; -typedef void *OGRFeatureH; -typedef void *OGRGeometryH; +#include /** \class QgsOgrProvider @@ -264,6 +251,4 @@ class QgsOgrProvider : public QgsVectorDataProvider bool addFeature(QgsFeature& f); /**Deletes one feature*/ bool deleteFeature(int id); - //! The geometry factory - GEOS_GEOM::GeometryFactory *geometryFactory; }; diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index e40e1dc0086..8172e9d5c98 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -49,7 +49,8 @@ const QString POSTGRES_KEY = "postgres"; const QString POSTGRES_DESCRIPTION = "PostgreSQL/PostGIS data provider"; -QMap QgsPostgresProvider::connections; +QMap QgsPostgresProvider::connectionsRO; +QMap QgsPostgresProvider::connectionsRW; int QgsPostgresProvider::providerIds=0; QgsPostgresProvider::QgsPostgresProvider(QString const & uri) @@ -86,12 +87,9 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) QgsDebugMsg("Schema is: " + mSchemaName); QgsDebugMsg("Table name is: " + mTableName); - //QString logFile = "./pg_provider_" + mTableName + ".log"; - //pLog.open((const char *)logFile); - //QgsDebugMsg("Opened log file for " + mTableName); - - connection = connectDb( mUri.connInfo() ); - if( connection==NULL ) { + connectionRW = NULL; + connectionRO = connectDb( mUri.connInfo() ); + if( connectionRO==NULL ) { valid = false; return; } @@ -100,7 +98,7 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) the problems they will see :) */ QgsDebugMsg("Checking for GEOS support"); - if(!hasGEOS(connection)) + if(!hasGEOS(connectionRO)) { showMessageBox(tr("No GEOS Support!"), tr("Your PostGIS installation has no GEOS support.\n" @@ -114,7 +112,7 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) // Check that we can read from the table (i.e., we have // select permission). QString sql = QString("select * from %1 limit 1").arg(mSchemaTableName); - PGresult* testAccess = PQexec(connection, sql.toUtf8()); + PGresult* testAccess = PQexec(connectionRO, sql.toUtf8()); if (PQresultStatus(testAccess) != PGRES_TUPLES_OK) { showMessageBox(tr("Unable to access relation"), @@ -136,7 +134,7 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) "current_schema()") .arg( quotedValue(mSchemaTableName) ); - testAccess = PQexec( connection, sql.toUtf8() ); + testAccess = PQexec( connectionRO, sql.toUtf8() ); if( PQresultStatus(testAccess) != PGRES_TUPLES_OK ) { showMessageBox(tr("Unable to access relation"), tr("Unable to determine table access privileges for the ") + mSchemaTableName + @@ -181,7 +179,7 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) "relname=%1 AND nspname=%2") .arg( quotedValue(mTableName) ) .arg( quotedValue(mSchemaName) ); - testAccess = PQexec(connection, sql.toUtf8()); + testAccess = PQexec(connectionRO, sql.toUtf8()); if (PQresultStatus(testAccess) == PGRES_TUPLES_OK && PQntuples(testAccess)==1) { enabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes; @@ -212,7 +210,7 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) // Set the postgresql message level so that we don't get the // 'there is no transaction in progress' warning. #ifndef QGISDEBUG - PQexecNR(connection, QString("set client_min_messages to error").toUtf8()); + PQexecNR(connectionRO, QString("set client_min_messages to error").toUtf8()); #endif // Kick off the long running threads @@ -240,11 +238,11 @@ QgsPostgresProvider::QgsPostgresProvider(QString const & uri) #endif //fill type names into sets - mSupportedNativeTypes.insert("double precision"); - mSupportedNativeTypes.insert("int4"); - mSupportedNativeTypes.insert("int8"); - mSupportedNativeTypes.insert("text"); - mSupportedNativeTypes.insert("varchar(30)"); + mSupportedNativeTypes.insert("double precision", QVariant::Double); + mSupportedNativeTypes.insert("int4", QVariant::Int); + mSupportedNativeTypes.insert("int8", QVariant::LongLong); + mSupportedNativeTypes.insert("text", QVariant::String); + mSupportedNativeTypes.insert("varchar(30)", QVariant::String); if (primaryKey.isEmpty()) { @@ -284,8 +282,10 @@ QgsPostgresProvider::~QgsPostgresProvider() //pLog.flush(); } -PGconn *QgsPostgresProvider::connectDb(const QString & conninfo) +PGconn *QgsPostgresProvider::connectDb(const QString & conninfo, bool readonly) { + QMap &connections = readonly ? connectionsRO : connectionsRW; + if( connections.contains(conninfo) ) { QgsDebugMsg( QString("Using cached connection for %1").arg(conninfo) ); @@ -333,24 +333,42 @@ void QgsPostgresProvider::disconnectDb() { if(mFetching) { - PQexecNR(connection, QString("CLOSE qgisf%1").arg(providerId).toUtf8() ); + PQexecNR(connectionRO, QString("CLOSE qgisf%1").arg(providerId).toUtf8() ); mFetching=false; } - QMap::iterator i; - for(i=connections.begin(); i!=connections.end() && i.value()->conn!=connection; i++) - ; + if(!connectionRO) { + QMap::iterator i; + for(i=connectionsRO.begin(); i!=connectionsRO.end() && i.value()->conn!=connectionRO; i++) + ; - assert( i.value()->conn==connection ); - assert( i.value()->ref>0 ); + assert( i.value()->conn==connectionRO ); + assert( i.value()->ref>0 ); - if( --i.value()->ref==0 ) { - PQfinish( connection ); - delete i.value(); - connections.remove( i.key() ); + if( --i.value()->ref==0 ) { + PQfinish( connectionRO ); + delete i.value(); + connectionsRO.remove( i.key() ); + } + + connectionRO = 0; + + if(connectionRW) { + for(i=connectionsRW.begin(); i!=connectionsRW.end() && i.value()->conn!=connectionRW; i++) + ; + + assert( i.value()->conn==connectionRW ); + assert( i.value()->ref>0 ); + + if( --i.value()->ref==0 ) { + PQfinish( connectionRW ); + delete i.value(); + connectionsRW.remove( i.key() ); + } + + connectionRW = 0; + } } - - connection = 0; } QString QgsPostgresProvider::storageType() const @@ -409,7 +427,7 @@ bool QgsPostgresProvider::declareCursor(const QString &cursorName, QgsDebugMsg("Binary cursor: " + declare); - return PQexecNR(connection, declare.toUtf8()); + return PQexecNR(connectionRO, declare.toUtf8()); } catch(PGFieldNotFound) { @@ -515,7 +533,7 @@ void QgsPostgresProvider::select(QgsAttributeList fetchAttributes, QgsRect rect, if(mFetching) { - PQexecNR(connection, QString("CLOSE %1").arg(cursorName).toUtf8() ); + PQexecNR(connectionRO, QString("CLOSE %1").arg(cursorName).toUtf8() ); mFetching=false; while(!mFeatureQueue.empty()) @@ -575,13 +593,13 @@ bool QgsPostgresProvider::getNextFeature(QgsFeature& feature) if( mFeatureQueue.empty() ) { QString fetch = QString("fetch forward %1 from %2").arg(mFeatureQueueSize).arg(cursorName); - if(PQsendQuery(connection, fetch.toUtf8()) == 0) //fetch features in asynchronously + if(PQsendQuery(connectionRO, fetch.toUtf8()) == 0) //fetch features in asynchronously { qWarning("PQsendQuery failed (1)"); } PGresult *queryResult; - while( (queryResult = PQgetResult(connection)) ) + while( (queryResult = PQgetResult(connectionRO)) ) { int rows = PQntuples(queryResult); if (rows == 0) @@ -627,7 +645,7 @@ bool QgsPostgresProvider::getFeatureAtId(int featureId, QgsFeature& feature, boo if( !declareCursor( cursorName, fetchAttributes, fetchGeometry, QString("%2=%3").arg(quotedIdentifier(primaryKey)).arg(featureId) ) ) return false; - PGresult *queryResult = PQexec(connection, QString("fetch forward 1 from %1").arg(cursorName).toUtf8()); + PGresult *queryResult = PQexec(connectionRO, QString("fetch forward 1 from %1").arg(cursorName).toUtf8()); if(queryResult==0) return false; @@ -636,7 +654,7 @@ bool QgsPostgresProvider::getFeatureAtId(int featureId, QgsFeature& feature, boo { QgsDebugMsg("feature " + QString::number(featureId) + " not found"); PQclear(queryResult); - PQexecNR(connection, QString("CLOSE %1").arg(cursorName).toUtf8()); + PQexecNR(connectionRO, QString("CLOSE %1").arg(cursorName).toUtf8()); return false; } else if(rows != 1) @@ -648,7 +666,7 @@ bool QgsPostgresProvider::getFeatureAtId(int featureId, QgsFeature& feature, boo PQclear(queryResult); - PQexecNR(connection, QString("CLOSE %1").arg(cursorName).toUtf8()); + PQexecNR(connectionRO, QString("CLOSE %1").arg(cursorName).toUtf8()); return gotit; } @@ -724,7 +742,7 @@ void QgsPostgresProvider::reset() if(mFetching) { //move cursor to first record - PQexecNR(connection, QString("move 0 in qgisf%1").arg(providerId).toUtf8()); + PQexecNR(connectionRO, QString("move 0 in qgisf%1").arg(providerId).toUtf8()); } mFeatureQueue.empty(); loadFields(); @@ -752,13 +770,13 @@ void QgsPostgresProvider::loadFields() // Get the relation oid for use in later queries QString sql = QString("SELECT regclass(%1)::oid").arg( quotedValue(mSchemaTableName) ); - PGresult *tresult= PQexec(connection, sql.toUtf8()); + PGresult *tresult= PQexec(connectionRO, sql.toUtf8()); QString tableoid = QString::fromUtf8(PQgetvalue(tresult, 0, 0)); PQclear(tresult); // Get the table description sql = QString("SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=0").arg( tableoid ); - tresult = PQexec(connection, sql.toUtf8()); + tresult = PQexec(connectionRO, sql.toUtf8()); if (PQntuples(tresult) > 0) mDataComment = QString::fromUtf8(PQgetvalue(tresult, 0, 0)); PQclear(tresult); @@ -767,7 +785,7 @@ void QgsPostgresProvider::loadFields() // field name, type, length, and precision (if numeric) sql = QString("select * from %1 limit 0").arg ( mSchemaTableName ); - PGresult *result = PQexec(connection, sql.toUtf8()); + PGresult *result = PQexec(connectionRO, sql.toUtf8()); //--std::cout << "Field: Name, Type, Size, Modifier:" << std::endl; // The queries inside this loop could possibly be combined into one @@ -787,7 +805,7 @@ void QgsPostgresProvider::loadFields() // "oid = (SELECT Distinct typelem FROM pg_type WHERE " //needs DISTINCT to guard against 2 or more rows on int2 // "typelem = " + typOid + " AND typlen = -1)"; - PGresult* oidResult = PQexec(connection, sql.toUtf8()); + PGresult* oidResult = PQexec(connectionRO, sql.toUtf8()); QString fieldTypeName = QString::fromUtf8(PQgetvalue(oidResult, 0, 0)); QString fieldSize = QString::fromUtf8(PQgetvalue(oidResult, 0, 1)); PQclear(oidResult); @@ -795,14 +813,14 @@ void QgsPostgresProvider::loadFields() sql = QString("SELECT attnum FROM pg_attribute WHERE attrelid=%1 AND attname=%2") .arg( tableoid ).arg( quotedValue(fieldName) ); - PGresult *tresult = PQexec(connection, sql.toUtf8()); + PGresult *tresult = PQexec(connectionRO, sql.toUtf8()); QString attnum = QString::fromUtf8(PQgetvalue(tresult, 0, 0)); PQclear(tresult); sql = QString("SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=%2") .arg( tableoid ).arg( attnum ); - tresult = PQexec(connection, sql.toUtf8()); + tresult = PQexec(connectionRO, sql.toUtf8()); if (PQntuples(tresult) > 0) fieldComment = QString::fromUtf8(PQgetvalue(tresult, 0, 0)); PQclear(tresult); @@ -859,7 +877,7 @@ QString QgsPostgresProvider::getPrimaryKey() QgsDebugMsg("Getting unique index using '" + sql + "'"); - PGresult *pk = executeDbCommand(connection, sql); + PGresult *pk = executeDbCommand(connectionRO, sql); QgsDebugMsg("Got " + QString::number(PQntuples(pk)) + " rows."); @@ -877,7 +895,7 @@ QString QgsPostgresProvider::getPrimaryKey() sql = QString("SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid") .arg( quotedValue(mSchemaTableName) ); - PGresult* tableType = executeDbCommand(connection, sql); + PGresult* tableType = executeDbCommand(connectionRO, sql); QString type = QString::fromUtf8(PQgetvalue(tableType, 0, 0)); PQclear(tableType); @@ -892,7 +910,7 @@ QString QgsPostgresProvider::getPrimaryKey() sql = QString("SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)") .arg( quotedValue(mSchemaTableName) ); - PGresult* oidCheck = executeDbCommand(connection, sql); + PGresult* oidCheck = executeDbCommand(connectionRO, sql); if (PQntuples(oidCheck) != 0) { @@ -940,7 +958,7 @@ QString QgsPostgresProvider::getPrimaryKey() // Get the column name and data type sql = QString("select attname,pg_type.typname from pg_attribute,pg_type where atttypid=pg_type.oid and attnum=%1 and attrelid=regclass(%2)") .arg( col ).arg( quotedValue(mSchemaTableName) ); - PGresult* types = executeDbCommand(connection, sql); + PGresult* types = executeDbCommand(connectionRO, sql); if( PQntuples(types) > 0 ) { @@ -968,7 +986,7 @@ QString QgsPostgresProvider::getPrimaryKey() .arg( col.replace(" ", ",") ) .arg( quotedValue(mSchemaTableName) ); - PGresult* types = executeDbCommand(connection, sql); + PGresult* types = executeDbCommand(connectionRO, sql); QString colNames; int numCols = PQntuples(types); for (int j = 0; j < numCols; ++j) @@ -1002,7 +1020,7 @@ QString QgsPostgresProvider::getPrimaryKey() // If there is an oid on the table, use that instead, // otherwise give up sql = QString("select attname from pg_attribute where attname='oid' and attrelid=regclass(%1)::oid").arg( quotedValue(mSchemaTableName) ); - PGresult* oidCheck = executeDbCommand(connection, sql); + PGresult* oidCheck = executeDbCommand(connectionRO, sql); if (PQntuples(oidCheck) != 0) { @@ -1069,7 +1087,7 @@ QString QgsPostgresProvider::chooseViewColumn(const tableCols& cols) // Get the oid from pg_class for the given schema.relation for use // in subsequent queries. sql = QString("select regclass(%1)::oid").arg( quotedValue( quotedIdentifier(schemaName) + "." + quotedIdentifier(tableName) ) ); - PGresult* result = PQexec(connection, sql.toUtf8()); + PGresult* result = PQexec(connectionRO, sql.toUtf8()); QString rel_oid; if (PQntuples(result) == 1) { @@ -1100,7 +1118,7 @@ QString QgsPostgresProvider::chooseViewColumn(const tableCols& cols) "and conrelid=%2 and (contype='p' or contype='u') " "and array_dims(conkey)='[1:1]'").arg( quotedValue(tableCol) ).arg( rel_oid ); - result = PQexec(connection, sql.toUtf8()); + result = PQexec(connectionRO, sql.toUtf8()); if (PQntuples(result) == 1 && colType == "int4") suitable[viewCol] = iter->second; @@ -1159,7 +1177,7 @@ QString QgsPostgresProvider::chooseViewColumn(const tableCols& cols) sql = QString( "select * from pg_index where indrelid=%1 and indkey[0]=(select attnum from pg_attribute where attrelid=%1 and attname=%2)") .arg( rel_oid ) .arg( quotedValue( i->second.column ) ); - PGresult* result = PQexec(connection, sql.toUtf8()); + PGresult* result = PQexec(connectionRO, sql.toUtf8()); if (PQntuples(result) > 0 && uniqueData(mSchemaName, mTableName, i->first)) { // Got one. Use it. @@ -1245,7 +1263,7 @@ bool QgsPostgresProvider::uniqueData(QString schemaName, .arg( quotedIdentifier(schemaName) ) .arg( quotedIdentifier(tableName) ); - PGresult* unique = PQexec(connection, sql.toUtf8()); + PGresult* unique = PQexec(connectionRO, sql.toUtf8()); if (PQntuples(unique)==1 && QString::fromUtf8(PQgetvalue(unique, 0, 0)).startsWith("t")) isUnique = true; @@ -1259,7 +1277,7 @@ int QgsPostgresProvider::SRCFromViewColumn(const QString& ns, const QString& rel { QString newViewDefSql = QString("SELECT definition FROM pg_views WHERE schemaname=%1 AND viewname=%2") .arg( quotedValue(ns) ).arg( quotedValue(relname) ); - PGresult* newViewDefResult = PQexec(connection, newViewDefSql.toUtf8()); + PGresult* newViewDefResult = PQexec(connectionRO, newViewDefSql.toUtf8()); int numEntries = PQntuples(newViewDefResult); if(numEntries > 0) //relation is a view @@ -1335,7 +1353,7 @@ int QgsPostgresProvider::SRCFromViewColumn(const QString& ns, const QString& rel .arg( quotedValue(relname) ) .arg( quotedValue(newAttNameTable) ); - PGresult* viewColumnResult = PQexec(connection, viewColumnSql.toUtf8()); + PGresult* viewColumnResult = PQexec(connectionRO, viewColumnSql.toUtf8()); if(PQntuples(viewColumnResult) > 0) { QString newTableSchema = QString::fromUtf8(PQgetvalue(viewColumnResult, 0, 0)); @@ -1373,7 +1391,7 @@ int QgsPostgresProvider::SRCFromViewColumn(const QString& ns, const QString& rel .arg( quotedValue(ns) ) .arg( quotedValue(attname_table) ); - PGresult* typeSqlResult = PQexec(connection, typeSql.toUtf8()); + PGresult* typeSqlResult = PQexec(connectionRO, typeSql.toUtf8()); if(PQntuples(typeSqlResult) < 1) { return 1; @@ -1446,13 +1464,13 @@ void QgsPostgresProvider::findColumns(tableCols& cols) "view_schema=%1 AND view_name=%2") .arg( quotedValue(mSchemaName) ) .arg( quotedValue(mTableName) ); - PGresult* viewColumnResult = PQexec(connection, viewColumnSql.toUtf8()); + PGresult* viewColumnResult = PQexec(connectionRO, viewColumnSql.toUtf8()); //find out view definition QString viewDefSql = QString("SELECT definition FROM pg_views WHERE schemaname=%1 AND viewname=%2") .arg( quotedValue( mSchemaName ) ) .arg( quotedValue( mTableName ) ); - PGresult* viewDefResult = PQexec(connection, viewDefSql.toUtf8()); + PGresult* viewDefResult = PQexec(connectionRO, viewDefSql.toUtf8()); if(PQntuples(viewDefResult) < 1) { PQclear(viewDefResult); @@ -1523,7 +1541,7 @@ QVariant QgsPostgresProvider::minValue(int index) .arg(mSchemaTableName) .arg(sqlWhereClause); } - PGresult *rmin = PQexec(connection, sql.toUtf8()); + PGresult *rmin = PQexec(connectionRO, sql.toUtf8()); QString minValue = QString::fromUtf8(PQgetvalue(rmin,0,0)); PQclear(rmin); return minValue.toDouble(); @@ -1558,7 +1576,7 @@ void QgsPostgresProvider::getUniqueValues(int index, QStringList &uniqueValues) .arg(sqlWhereClause); } - PGresult *res= PQexec(connection, sql.toUtf8()); + PGresult *res= PQexec(connectionRO, sql.toUtf8()); if (PQresultStatus(res) == PGRES_TUPLES_OK) { for(int i=0; i0) { std::string box3d = PQgetvalue(result, 0, 0); @@ -2390,7 +2425,7 @@ bool QgsPostgresProvider::deduceEndian() // return data in the endian of the server QString firstOid = QString("select regclass(%1)::oid").arg( quotedValue(mSchemaTableName) ); - PGresult * oidResult = PQexec(connection, firstOid.toUtf8()); + PGresult * oidResult = PQexec(connectionRO, firstOid.toUtf8()); // get the int value from a "normal" select QString oidValue = QString::fromUtf8(PQgetvalue(oidResult,0,0)); PQclear(oidResult); @@ -2401,12 +2436,12 @@ bool QgsPostgresProvider::deduceEndian() QString oidDeclare = QString("declare oidcursor binary cursor with hold for select regclass(%1)::oid").arg( quotedValue(mSchemaTableName) ); // set up the cursor - PQexecNR(connection, oidDeclare.toUtf8()); + PQexecNR(connectionRO, oidDeclare.toUtf8()); QString fetch = "fetch forward 1 from oidcursor"; QgsDebugMsg("Fetching a record and attempting to get check endian-ness"); - PGresult *fResult = PQexec(connection, fetch.toUtf8()); + PGresult *fResult = PQexec(connectionRO, fetch.toUtf8()); swapEndian = true; if(PQntuples(fResult) > 0){ // get the oid value from the binary cursor @@ -2420,7 +2455,7 @@ bool QgsPostgresProvider::deduceEndian() PQclear(fResult); } - PQexecNR(connection, QString("close oidcursor").toUtf8()); + PQexecNR(connectionRO, QString("close oidcursor").toUtf8()); return swapEndian; } @@ -2439,7 +2474,7 @@ bool QgsPostgresProvider::getGeometryDetails() QgsDebugMsg("Getting geometry column: " + sql); - PGresult *result = executeDbCommand(connection, sql); + PGresult *result = executeDbCommand(connectionRO, sql); QgsDebugMsg("geometry column query returned " + QString::number(PQntuples(result))); @@ -2467,7 +2502,7 @@ bool QgsPostgresProvider::getGeometryDetails() sql += " limit 1"; - result = executeDbCommand(connection, sql); + result = executeDbCommand(connectionRO, sql); if (PQntuples(result) > 0) { @@ -2493,7 +2528,7 @@ bool QgsPostgresProvider::getGeometryDetails() if(mUri.sql()!="") sql += " where " + mUri.sql(); - result = executeDbCommand(connection, sql); + result = executeDbCommand(connectionRO, sql); if (PQntuples(result)==1) { @@ -2665,7 +2700,7 @@ QString QgsPostgresProvider::subsetString() PGconn * QgsPostgresProvider::pgConnection() { - return connection; + return connectionRW; } QString QgsPostgresProvider::getTableName() diff --git a/src/providers/postgres/qgspostgresprovider.h b/src/providers/postgres/qgspostgresprovider.h index 13eba876ca5..cd3ba61fcf1 100644 --- a/src/providers/postgres/qgspostgresprovider.h +++ b/src/providers/postgres/qgspostgresprovider.h @@ -47,10 +47,9 @@ class QgsGeometry; interface defined in the QgsDataProvider class to provide access to spatial data residing in a PostgreSQL/PostGIS enabled database. */ -class QgsPostgresProvider:public QgsVectorDataProvider +class QgsPostgresProvider : public QgsVectorDataProvider { - - Q_OBJECT + Q_OBJECT; public: /** @@ -405,7 +404,18 @@ class QgsPostgresProvider:public QgsVectorDataProvider /** * Connection pointer */ - PGconn *connection; + PGconn *connectionRO; + PGconn *connectionRW; + + bool connectRW() { + if(connectionRW) + return connectionRW; + + connectionRW = connectDb(mUri.connInfo(), false); + + return connectionRW; + } + /** * Spatial reference id of the layer */ @@ -584,7 +594,7 @@ class QgsPostgresProvider:public QgsVectorDataProvider */ void customEvent ( QEvent *e ); - PGconn *connectDb(const QString &conninfo); + PGconn *connectDb(const QString &conninfo, bool readonly=true); void disconnectDb(); bool useWkbHex; @@ -605,7 +615,8 @@ class QgsPostgresProvider:public QgsVectorDataProvider int ref; PGconn *conn; }; - static QMap connections; + static QMap connectionsRW; + static QMap connectionsRO; static int providerIds; }; diff --git a/src/ui/qgsattributetablebase.ui b/src/ui/qgsattributetablebase.ui index 21822232df1..250837a0f01 100644 --- a/src/ui/qgsattributetablebase.ui +++ b/src/ui/qgsattributetablebase.ui @@ -13,27 +13,20 @@ Attribute Table - + + + - - 0 - - - 0 - - - 0 - - - 0 - 6 2 + + 0 + @@ -43,7 +36,8 @@ - ../xpm/remove_selection.xpm + + ../xpm/remove_selection.xpm../xpm/remove_selection.xpm @@ -59,7 +53,8 @@ - ../xpm/selected_to_top.png + + ../xpm/selected_to_top.png../xpm/selected_to_top.png Ctrl+T @@ -75,7 +70,8 @@ - ../xpm/switch_selection.png + + ../xpm/switch_selection.png../xpm/switch_selection.png Ctrl+S @@ -94,7 +90,8 @@ - ../xpm/copy_selection.png + + ../xpm/copy_selection.png../xpm/copy_selection.png Ctrl+C @@ -117,66 +114,12 @@ - - - - - 0 - 0 - - - - New column - - - - - - ../xpm/new_attribute.png - - - Ctrl+N - - - - - - Delete column - - - - - - ../xpm/delete_attribute.png - - - Ctrl+X - - - - - - - Toggle editing mode - - - Click to toggle table editing - - - - - - true - - - - Qt::Horizontal - + 421 20 @@ -184,17 +127,7 @@ - - - - - 0 - 0 - - - - - + @@ -251,10 +184,36 @@ - + - QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::NoButton + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + + 0 + 0 + + + + + + + + Toggle editing mode + + + Click to toggle table editing + + + + + + true @@ -274,9 +233,6 @@ mInvertSelectionButton mCopySelectedRowsButton mZoomMapToSelectedRowsButton - mAddAttributeButton - mDeleteAttributeButton - btnEdit mSearchText mSearchColumns mSearchButton diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 8b369f55c5a..d9aa932d460 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -213,6 +213,13 @@ + + + + Open attribute table in a dock window + + + @@ -223,8 +230,8 @@ - 547 - 51 + 577 + 21 diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 23ed544953a..b3f74ffa60d 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -5,8 +5,8 @@ 0 0 - 552 - 600 + 839 + 675 @@ -19,21 +19,287 @@ Layer Properties - + + + true + + + + 3 + + + 1 + + + + + Restore Default Style + + + + + + + Save As Default + + + + + + + Load Style ... + + + + + + + Save Style ... + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + true + + + + 0 + 0 + + + + false + QTabWidget::Rounded - 0 + 5 + + + 0 + 0 + 817 + 557 + + + + General + + + + + + Options + + + + + + Display name + + + txtDisplayName + + + + + + + + + + Display field for the Identify Results dialog box + + + This sets the display field for the Identify Results dialog box + + + Display field + + + displayFieldComboBox + + + + + + + Use this control to set which field is placed at the top level of the Identify Results dialog box. + + + + + + + true + + + + + + + Create Spatial Index + + + + + + + Change CRS + + + + + + + + + + Use scale dependent rendering + + + true + + + + 11 + + + + + Maximum + + + spinMaximumScale + + + + + + + Minimum + + + spinMinimumScale + + + + + + + Minimum scale at which this layer will be displayed. + + + 1 + + + 100000000 + + + + + + + Maximum scale at which this layer will be displayed. + + + 1 + + + 100000000 + + + + + + + + + + Subset + + + + 11 + + + + + false + + + false + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 480 + 21 + + + + + + + + Query Builder + + + + + + + + + + + + 0 + 0 + 817 + 552 + + + + + 0 + 0 + + Symbology @@ -134,227 +400,44 @@ 1 - - - - - - - - - General - - - - - - Options - - - - - - Display name - - - txtDisplayName - - - - - - - - - - Display field for the Identify Results dialog box - - - This sets the display field for the Identify Results dialog box - - - Display field - - - displayFieldComboBox - - - - - - - Use this control to set which field is placed at the top level of the Identify Results dialog box. - - - - - - - true - - - - - - - Create Spatial Index - - - - - - - Change CRS - - - - - - - - - - Use scale dependent rendering - - - true - - - - 11 + + + + 0 + 0 + 100 + 30 + - - 11 + + + + + 0 + 0 + 799 + 502 + - - 11 - - - 11 - - - - - Maximum - - - spinMaximumScale - - - - - - - Minimum - - - spinMinimumScale - - - - - - - Minimum scale at which this layer will be displayed. - - - 1 - - - 100000000 - - - - - - - Maximum scale at which this layer will be displayed. - - - 1 - - - 100000000 - - - - - - - - - - Subset - - - - 11 - - - 11 - - - 11 - - - 11 - - - - - false - - - false - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 480 - 21 - - - - - - - - Query Builder - - - - + + + + 0 + 0 + 817 + 552 + + Metadata - - 11 - - - 11 - - - 11 - - + 11 @@ -370,6 +453,14 @@ + + + 0 + 0 + 817 + 552 + + Labels @@ -406,6 +497,14 @@ + + + 0 + 0 + 817 + 552 + + Actions @@ -428,63 +527,118 @@ - - - - - - 3 - - - 1 - - - 1 - - - 1 - - - 1 - - - - - Restore Default Style - - - - - - - Save As Default - - - - - - - Load Style ... - - - - - - - Save Style ... - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::NoButton|QDialogButtonBox::Ok - + + + + 0 + 0 + 817 + 557 + + + + Attributes + + + + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + + + New column + + + + + + + ../xpm/new_attribute.png../xpm/new_attribute.png + + + Ctrl+N + + + + + + + Delete column + + + + + + + ../xpm/delete_attribute.png../xpm/delete_attribute.png + + + Ctrl+X + + + + + + + Toggle editing mode + + + Click to toggle table editing + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 256 + 0 + + + + false + + + + + tblAttributes + +