154 changes: 115 additions & 39 deletions python/plugins/fTools/tools/doValidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def setGeom(self, p):

def reset(self):
if not self.__marker is None:
self.__canvas.scene().removeItem(self.__marker)
self.__canvas.scene().removeItem(self.__marker)
del self.__marker
self.__marker = None

Expand All @@ -83,9 +83,10 @@ def __init__(self, iface):
self.tblUnique.setSelectionBehavior(QAbstractItemView.SelectRows)
# populate list of available layers
myList = ftools_utils.getLayerNames( [ QGis.Point, QGis.Line, QGis.Polygon ] )
self.connect(self.tblUnique, SIGNAL("currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)" ),
self.connect(self.tblUnique, SIGNAL("currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)" ),
self.zoomToError)
self.inShape.addItems( myList )
self.buttonBox_2.setOrientation(Qt.Horizontal)
self.cancel_close = self.buttonBox_2.button(QDialogButtonBox.Close)
self.buttonOk = self.buttonBox_2.button(QDialogButtonBox.Ok)
self.progressBar.setValue(0)
Expand All @@ -96,12 +97,16 @@ def __init__(self, iface):
settings = QSettings()
self.restoreGeometry( settings.value("/fTools/ValidateDialog/geometry").toByteArray() )

QObject.connect( self.browseShpError, SIGNAL( "clicked()" ), self.outFile )
QObject.connect( self.ckBoxShpError, SIGNAL( "stateChanged( int )" ), self.updateGui )
self.updateGui()

def closeEvent(self, e):
settings = QSettings()
settings.setValue( "/fTools/ValidateDialog/geometry", QVariant(self.saveGeometry()) )
QDialog.closeEvent(self, e)
del self.marker

def keyPressEvent( self, e ):
if ( e.modifiers() == Qt.ControlModifier or \
e.modifiers() == Qt.MetaModifier ) and \
Expand All @@ -122,10 +127,37 @@ def accept( self ):
QMessageBox.information( self, self.tr("Error!"), self.tr( "Please specify input vector layer" ) )
elif self.cmbField.isVisible() and self.cmbField.currentText() == "":
QMessageBox.information( self, self.tr("Error!"), self.tr( "Please specify input field" ) )
elif self.ckBoxShpError.isChecked() and self.lineEditShpError.text() == "":
QMessageBox.information( self, self.tr( "Error!" ), self.tr( "Please specify output shapefile" ) )
else:
self.vlayer = ftools_utils.getVectorLayerByName( self.inShape.currentText() )
self.validate( self.useSelected.checkState() )


def updateGui( self ):
if self.ckBoxShpError.isChecked():
self.lineEditShpError.setEnabled( True )
self.browseShpError.setEnabled( True )
self.tblUnique.setEnabled( False )
self.lstCount.setEnabled( False )
self.label_2.setEnabled( False )
self.label_4.setEnabled( False )
self.label_5.setEnabled( False )
else:
self.lineEditShpError.setEnabled( False )
self.browseShpError.setEnabled( False )
self.tblUnique.setEnabled( True )
self.lstCount.setEnabled( True )
self.label_2.setEnabled( True )
self.label_4.setEnabled( True )
self.label_5.setEnabled( True )

def outFile( self ):
self.lineEditShpError.clear()
(self.shapefileName, self.encoding) = ftools_utils.saveDialog( self )
if self.shapefileName is None or self.encoding is None:
return
self.lineEditShpError.setText( QString( self.shapefileName ) )

def zoomToError(self, curr, prev):
if curr is None:
return
Expand Down Expand Up @@ -162,11 +194,16 @@ def zoomToError(self, curr, prev):
mc.refresh()

def validate( self, mySelection ):
self.tblUnique.clearContents()
self.tblUnique.setRowCount( 0 )
self.lstCount.clear()
if not self.ckBoxShpError.isChecked():
self.tblUnique.clearContents()
self.tblUnique.setRowCount( 0 )
self.lstCount.clear()
self.shapefileName = None
self.encoding = None

self.buttonOk.setEnabled( False )
self.testThread = validateThread( self.iface.mainWindow(), self, self.vlayer, mySelection )

self.testThread = validateThread( self.iface.mainWindow(), self, self.vlayer, mySelection, self.shapefileName, self.encoding, self.ckBoxShpError.isChecked() )
QObject.connect( self.testThread, SIGNAL( "runFinished(PyQt_PyObject)" ), self.runFinishedFromThread )
QObject.connect( self.testThread, SIGNAL( "runStatus(PyQt_PyObject)" ), self.runStatusFromThread )
QObject.connect( self.testThread, SIGNAL( "runRange(PyQt_PyObject)" ), self.runRangeFromThread )
Expand All @@ -180,60 +217,73 @@ def reject(self):
# Remove Marker
self.marker.reset()
QDialog.reject(self)

def cancelThread( self ):
self.testThread.stop()
QApplication.restoreOverrideCursor()
self.buttonOk.setEnabled( True )
def runFinishedFromThread( self, output ):

def runFinishedFromThread( self, success ):
self.testThread.stop()
QApplication.restoreOverrideCursor()
self.buttonOk.setEnabled( True )
self.tblUnique.setColumnCount( 2 )
count = 0
for rec in output:
if len(rec[1]) < 1:
continue
where = None
for err in rec[1]: # for each error we find
self.tblUnique.insertRow(count)
fidItem = QTableWidgetItem( str(rec[0]) )
self.tblUnique.setItem( count, 0, fidItem )
message = err.what()
errItem = QTableWidgetItem( message )
if err.hasWhere(): # if there is a location associated with the error
errItem.setData(Qt.UserRole, QVariant(err.where()))
self.tblUnique.setItem( count, 1, errItem )
count += 1
self.tblUnique.setHorizontalHeaderLabels( [ self.tr("Feature"), self.tr("Error(s)") ] )
self.tblUnique.horizontalHeader().setResizeMode( 0, QHeaderView.ResizeToContents )
self.tblUnique.horizontalHeader().show()
self.tblUnique.horizontalHeader().setResizeMode( 1, QHeaderView.Stretch )
self.tblUnique.resizeRowsToContents()
self.lstCount.insert(str(count))
if success == "writeShape":
extra = ""
addToTOC = QMessageBox.question( self, self.tr("Geometry"),
self.tr( "Created output shapefile:\n%1\n%2\n\nWould you like to add the new layer to the TOC?" ).arg( unicode( self.shapefileName ) ).arg( extra ),
QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton )
if addToTOC == QMessageBox.Yes:
if not ftools_utils.addShapeToCanvas( unicode( self.shapefileName ) ):
QMessageBox.warning( self, self.tr( "Geometry"),
self.tr( "Error loading output shapefile:\n%1" ).arg( unicode( self.shapefileName ) ) )
else:
self.tblUnique.setColumnCount( 2 )
count = 0
for rec in success:
if len(rec[1]) < 1:
continue
where = None
for err in rec[1]: # for each error we find
self.tblUnique.insertRow(count)
fidItem = QTableWidgetItem( str(rec[0]) )
self.tblUnique.setItem( count, 0, fidItem )
message = err.what()
errItem = QTableWidgetItem( message )
if err.hasWhere(): # if there is a location associated with the error
errItem.setData(Qt.UserRole, QVariant(err.where()))
self.tblUnique.setItem( count, 1, errItem )
count += 1
self.tblUnique.setHorizontalHeaderLabels( [ self.tr("Feature"), self.tr("Error(s)") ] )
self.tblUnique.horizontalHeader().setResizeMode( 0, QHeaderView.ResizeToContents )
self.tblUnique.horizontalHeader().show()
self.tblUnique.horizontalHeader().setResizeMode( 1, QHeaderView.Stretch )
self.tblUnique.resizeRowsToContents()
self.lstCount.insert(str(count))
self.cancel_close.setText( "Close" )
QObject.disconnect( self.cancel_close, SIGNAL( "clicked()" ), self.cancelThread )
return True

def runStatusFromThread( self, status ):
self.progressBar.setValue( status )

def runRangeFromThread( self, range_vals ):
self.progressBar.setRange( range_vals[ 0 ], range_vals[ 1 ] )

class validateThread( QThread ):
def __init__( self, parentThread, parentObject, vlayer, mySelection ):
def __init__( self, parentThread, parentObject, vlayer, mySelection, myName, myEncoding, myNewShape ):
QThread.__init__( self, parentThread )
self.parent = parentObject
self.running = False
self.vlayer = vlayer
self.mySelection = mySelection
self.myName = myName
self.myEncoding = myEncoding
self.writeShape = myNewShape

def run( self ):
self.running = True
output = self.check_geometry( self.vlayer )
self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), output )
success = self.check_geometry( self.vlayer )
self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), success )

def stop(self):
self.running = False
Expand Down Expand Up @@ -265,4 +315,30 @@ def check_geometry( self, vlayer ):
if not geom.isGeosEmpty():
lstErrors.append((feat.id(), list(geom.validateGeometry())))
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nFeat )
return lstErrors

if self.writeShape:
fields = { 0 : QgsField( "FEAT_ID", QVariant.Int ),
1 : QgsField( "ERROR", QVariant.String ) }
writer = QgsVectorFileWriter( self.myName, self.myEncoding, fields,
QGis.WKBPoint, vlayer.crs() )
for rec in lstErrors:
if len(rec[1]) < 1:
continue
for err in rec[1]:
fidItem = str(rec[0])
message = err.what()
if err.hasWhere():
locErr = err.where()
xP = locErr.x()
yP = locErr.y()
myPoint = QgsPoint( xP, yP )
geometry = QgsGeometry().fromPoint( myPoint )
ft = QgsFeature()
ft.setGeometry( geometry )
ft.setAttributeMap( { 0 : QVariant( fidItem ),
1 : QVariant( message ) } )
writer.addFeature( ft )
del writer
return "writeShape"
else:
return lstErrors
13 changes: 11 additions & 2 deletions python/plugins/fTools/tools/doVisual.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ def __init__( self, iface, function ):
self.iface = iface
self.setupUi( self )
self.myFunction = function

## Set object visibility to False if tool is not Check geometry
self.ckBoxShpError.hide()
self.browseShpError.hide()
self.lineEditShpError.hide()
self.label_6.hide()
self.line.hide()
self.buttonBox_2.setOrientation(Qt.Horizontal)

if self.myFunction == 2 or self.myFunction == 3:
QObject.connect( self.inShape, SIGNAL( "currentIndexChanged(QString)" ), self.update )
self.manageGui()
Expand Down Expand Up @@ -289,7 +298,7 @@ def basic_statistics( self, vlayer, myField ):
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nElement )
else: # there is no selection, process the whole layer
nFeat = vprovider.featureCount()
if nFeat > 0:
if nFeat > 0:
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) )
vprovider.select( allAttrs )
Expand Down Expand Up @@ -355,7 +364,7 @@ def basic_statistics( self, vlayer, myField ):
else: # there is no selection, process the whole layer
nFeat = vprovider.featureCount()
uniqueVal = ftools_utils.getUniqueValuesCount( vlayer, index, False )
if nFeat > 0:
if nFeat > 0:
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) )
vprovider.select( allAttrs )
Expand Down
123 changes: 85 additions & 38 deletions python/plugins/fTools/tools/frmVisual.ui
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>481</height>
<width>370</width>
<height>484</height>
</rect>
</property>
<property name="windowTitle">
Expand All @@ -20,7 +20,7 @@
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<item row="0" column="0">
<layout class="QVBoxLayout">
<item>
<widget class="QLabel" name="label_3">
Expand All @@ -34,18 +34,7 @@
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="useSelected">
<property name="text">
<string>Use only selected features</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<item row="2" column="0">
<layout class="QVBoxLayout">
<item>
<widget class="QLabel" name="label">
Expand All @@ -59,7 +48,7 @@
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<item row="3" column="0">
<layout class="QVBoxLayout">
<item>
<widget class="QLabel" name="label_2">
Expand Down Expand Up @@ -107,7 +96,7 @@
</item>
</layout>
</item>
<item row="6" column="0" colspan="2">
<item row="4" column="0">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_4">
Expand All @@ -125,37 +114,95 @@
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<item row="7" column="0">
<widget class="QCheckBox" name="ckBoxShpError">
<property name="text">
<string>Save errors location</string>
</property>
</widget>
</item>
<item row="8" column="1" rowspan="2">
<widget class="QDialogButtonBox" name="buttonBox_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Press Ctrl+C to copy results to the clipboard</string>
</property>
</widget>
</item>
<item row="9" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEditShpError">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browseShpError">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0">
<widget class="QProgressBar" name="partProgressBar">
<property name="value">
<number>24</number>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Output point shapefile</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Press Ctrl+C to copy results to the clipboard</string>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="useSelected">
<property name="text">
<string>Use only selected features</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QProgressBar" name="partProgressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
Expand Down
12 changes: 8 additions & 4 deletions src/app/qgisapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,21 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
QAction *actionAddOgrLayer() { return mActionAddOgrLayer; }
QAction *actionAddRasterLayer() { return mActionAddRasterLayer; }
QAction *actionAddPgLayer() { return mActionAddPgLayer; }
QAction *actionAddSpatiaLiteLayer() { return mActionAddSpatiaLiteLayer; };
QAction *actionAddSpatiaLiteLayer() { return mActionAddSpatiaLiteLayer; }
QAction *actionAddWmsLayer() { return mActionAddWmsLayer; }
QAction *actionAddWcsLayer() { return mActionAddWcsLayer; }
QAction *actionAddWfsLayer() { return mActionAddWfsLayer; }
/** @note added in 1.9 */
QAction *actionCopyLayerStyle() { return mActionCopyStyle; }
/** @note added in 1.9 */
QAction *actionPasteLayerStyle() { return mActionPasteStyle; }
QAction *actionOpenTable() { return mActionOpenTable; }
QAction *actionToggleEditing() { return mActionToggleEditing; }
QAction *actionSaveEdits() { return mActionSaveEdits; }
QAction *actionLayerSaveAs() { return mActionLayerSaveAs; }
QAction *actionLayerSelectionSaveAs() { return mActionLayerSelectionSaveAs; }
QAction *actionRemoveLayer() { return mActionRemoveLayer; }
/** @note added in 2.0 */
/** @note added in 1.9 */
QAction *actionDuplicateLayer() { return mActionDuplicateLayer; }
QAction *actionSetLayerCRS() { return mActionSetLayerCRS; }
QAction *actionSetProjectCRSFromLayer() { return mActionSetProjectCRSFromLayer; }
Expand Down Expand Up @@ -437,13 +441,13 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
(defaults to the active layer on the legend)
*/
void editPaste( QgsMapLayer * destinationLayer = 0 );

//! copies style of the active layer to the clipboard
/**
\param sourceLayer The layer where the style will be taken from
(defaults to the active layer on the legend)
*/
void copyStyle( QgsMapLayer * sourceLayer = 0 );
//! copies style on the clipboard to the active layer
//! pastes style on the clipboard to the active layer
/**
\param destinatioLayer The layer that the clipboard will be pasted to
(defaults to the active layer on the legend)
Expand Down
2 changes: 2 additions & 0 deletions src/app/qgisappinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,8 @@ QAction *QgisAppInterface::actionAddRasterLayer() { return qgis->actionAddRaster
QAction *QgisAppInterface::actionAddPgLayer() { return qgis->actionAddPgLayer(); }
QAction *QgisAppInterface::actionAddWmsLayer() { return qgis->actionAddWmsLayer(); }
QAction *QgisAppInterface::actionLayerSeparator1() { return 0; }
QAction *QgisAppInterface::actionCopyLayerStyle() { return qgis->actionCopyLayerStyle(); }
QAction *QgisAppInterface::actionPasteLayerStyle() { return qgis->actionPasteLayerStyle(); }
QAction *QgisAppInterface::actionOpenTable() { return qgis->actionOpenTable(); }
QAction *QgisAppInterface::actionToggleEditing() { return qgis->actionToggleEditing(); }
QAction *QgisAppInterface::actionLayerSaveAs() { return qgis->actionLayerSaveAs(); }
Expand Down
6 changes: 5 additions & 1 deletion src/app/qgisappinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,16 @@ class QgisAppInterface : public QgisInterface
virtual QAction *actionAddPgLayer();
virtual QAction *actionAddWmsLayer();
virtual QAction *actionLayerSeparator1();
/** @note added in 1.9 */
virtual QAction *actionCopyLayerStyle();
/** @note added in 1.9 */
virtual QAction *actionPasteLayerStyle();
virtual QAction *actionOpenTable();
virtual QAction *actionToggleEditing();
virtual QAction *actionLayerSaveAs();
virtual QAction *actionLayerSelectionSaveAs();
virtual QAction *actionRemoveLayer();
/** @note added in 2.0 */
/** @note added in 1.9 */
virtual QAction *actionDuplicateLayer();
virtual QAction *actionLayerProperties();
virtual QAction *actionLayerSeparator2();
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsattributeactiondialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ void QgsAttributeActionDialog::addDefaultActions()
insertRow( pos++, QgsAction::GenericPython, tr( "Selected field's value (Identify features tool)" ), "QtGui.QMessageBox.information(None, \"Current field's value\", \"[% $currentfield %]\")", false );
insertRow( pos++, QgsAction::GenericPython, tr( "Clicked coordinates (Run feature actions tool)" ), "QtGui.QMessageBox.information(None, \"Clicked coords\", \"layer: [% $layerid %]\\ncoords: ([% $clickx %],[% $clickx %])\")", false );
insertRow( pos++, QgsAction::OpenUrl, tr( "Open file" ), "[% \"PATH\" %]", false );
insertRow( pos++, QgsAction::OpenUrl, tr( "Search on web based on attribute's value" ), "http://www.google.it/?q=[% \"ATTRIBUTE\" %]", false );
insertRow( pos++, QgsAction::OpenUrl, tr( "Search on web based on attribute's value" ), "http://www.google.com/search?q=[% \"ATTRIBUTE\" %]", false );
}

void QgsAttributeActionDialog::itemSelectionChanged()
Expand Down
9 changes: 9 additions & 0 deletions src/app/qgslabelinggui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
mUpsidedownRadioOff->setChecked( true );
break;
}
mMaxCharAngleInDSpinBox->setValue( lyr.maxCurvedCharAngleIn );
// lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
mMaxCharAngleOutDSpinBox->setValue( qAbs( lyr.maxCurvedCharAngleOut ) );

wrapCharacterEdit->setText( lyr.wrapChar );
mFontLineHeightSpinBox->setValue( lyr.multilineHeight );
Expand Down Expand Up @@ -501,6 +504,10 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
{
lyr.upsidedownLabels = QgsPalLayerSettings::ShowAll;
}
lyr.maxCurvedCharAngleIn = mMaxCharAngleInDSpinBox->value();
// lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI
lyr.maxCurvedCharAngleOut = -mMaxCharAngleOutDSpinBox->value();

lyr.minFeatureSize = mMinSizeSpinBox->value();
lyr.limitNumLabels = mLimitLabelChkBox->isChecked();
lyr.maxNumLabels = mLimitLabelSpinBox->value();
Expand Down Expand Up @@ -958,6 +965,8 @@ void QgsLabelingGui::updateOptions()
|| ( stackedPlacement->currentWidget() == pageLine && radLineCurved->isChecked() ) )
{
stackedOptions->setCurrentWidget( pageOptionsLine );
mMaxCharAngleFrame->setVisible(( stackedPlacement->currentWidget() == pageLine
&& radLineCurved->isChecked() ) );
}
else
{
Expand Down
25 changes: 15 additions & 10 deletions src/app/qgsmaptoolidentify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int
return false;
}

QMap< QString, QString > attributes, derivedAttributes;
QMap< QString, QString > derivedAttributes;

QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );

Expand Down Expand Up @@ -259,6 +259,7 @@ bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int
calc.setEllipsoid( ellipsoid );
calc.setSourceCrs( layer->crs().srsid() );
}

QgsFeatureList::iterator f_it = featureList.begin();

bool filter = false;
Expand All @@ -280,8 +281,6 @@ bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int

featureCount++;

QMap<QString, QString> derivedAttributes;

// Calculate derived attributes and insert:
// measure distance or area depending on geometry type
if ( layer->geometryType() == QGis::Line )
Expand All @@ -295,33 +294,39 @@ bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int
f_it->geometry()->wkbType() == QGis::WKBLineString25D )
{
// Add the start and end points in as derived attributes
str = QLocale::system().toString( f_it->geometry()->asPolyline().first().x(), 'g', 10 );
QgsPoint pnt = mCanvas->mapRenderer()->layerToMapCoordinates( layer, f_it->geometry()->asPolyline().first() );
str = QLocale::system().toString( pnt.x(), 'g', 10 );
derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str );
str = QLocale::system().toString( f_it->geometry()->asPolyline().first().y(), 'g', 10 );
str = QLocale::system().toString( pnt.y(), 'g', 10 );
derivedAttributes.insert( tr( "firstY" ), str );
str = QLocale::system().toString( f_it->geometry()->asPolyline().last().x(), 'g', 10 );
pnt = mCanvas->mapRenderer()->layerToMapCoordinates( layer, f_it->geometry()->asPolyline().last() );
str = QLocale::system().toString( pnt.x(), 'g', 10 );
derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str );
str = QLocale::system().toString( f_it->geometry()->asPolyline().last().y(), 'g', 10 );
str = QLocale::system().toString( pnt.y(), 'g', 10 );
derivedAttributes.insert( tr( "lastY" ), str );
}
}
else if ( layer->geometryType() == QGis::Polygon )
{
double area = calc.measure( f_it->geometry() );
double perimeter = calc.measurePerimeter( f_it->geometry() );
QGis::UnitType myDisplayUnits;
convertMeasurement( calc, area, myDisplayUnits, true ); // area and myDisplayUnits are out params
QString str = calc.textUnit( area, 3, myDisplayUnits, true );
derivedAttributes.insert( tr( "Area" ), str );
convertMeasurement( calc, perimeter, myDisplayUnits, false ); // area and myDisplayUnits are out params
str = calc.textUnit( perimeter, 3, myDisplayUnits, false );
derivedAttributes.insert( tr( "Perimeter" ), str );
}
else if ( layer->geometryType() == QGis::Point &&
( f_it->geometry()->wkbType() == QGis::WKBPoint ||
f_it->geometry()->wkbType() == QGis::WKBPoint25D ) )
{
// Include the x and y coordinates of the point as a derived attribute
QString str;
str = QLocale::system().toString( f_it->geometry()->asPoint().x(), 'g', 10 );
QgsPoint pnt = mCanvas->mapRenderer()->layerToMapCoordinates( layer, f_it->geometry()->asPoint() );
QString str = QLocale::system().toString( pnt.x(), 'g', 10 );
derivedAttributes.insert( "X", str );
str = QLocale::system().toString( f_it->geometry()->asPoint().y(), 'g', 10 );
str = QLocale::system().toString( pnt.y(), 'g', 10 );
derivedAttributes.insert( "Y", str );
}

Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ SET(QGIS_CORE_MOC_HDRS
gps/qgsgpsdconnection.h

symbology-ng/qgscptcityarchive.h
symbology-ng/qgssvgcache.h
)

IF (WITH_INTERNAL_QEXTSERIALPORT)
Expand Down
7 changes: 0 additions & 7 deletions src/core/composer/qgscomposerlegend.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,6 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
/**Draws a layer item and all subitems*/
QSizeF drawLayerItemTitle( QgsComposerLayerItem* layerItem, QPainter* painter = 0, QPointF point = QPointF() );

/**Draws child items of a layer item
@param p painter
@param layerItem parent model item (layer)
@param currentPosition in/out: current y position of legend item
@param layerOpacity opacity of the corresponding map layer
*/

Nucleon drawSymbolItem( QgsComposerLegendItem* symbolItem, QPainter* painter = 0, QPointF point = QPointF(), double labelXOffset = 0. );

/**Draws a symbol at the current y position and returns the new x position. Returns real symbol height, because for points,
Expand Down
5 changes: 4 additions & 1 deletion src/core/pal/feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,10 @@ namespace pal
// normalise between -180 and 180
while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
if ( f->labelInfo->max_char_angle_delta > 0 && fabs( angle_delta ) > f->labelInfo->max_char_angle_delta*( M_PI / 180 ) )
if (( f->labelInfo->max_char_angle_inside > 0 && angle_delta > 0
&& angle_delta > f->labelInfo->max_char_angle_inside*( M_PI / 180 ) )
|| ( f->labelInfo->max_char_angle_outside < 0 && angle_delta < 0
&& angle_delta < f->labelInfo->max_char_angle_outside*( M_PI / 180 ) ) )
{
delete slp;
return NULL;
Expand Down
9 changes: 6 additions & 3 deletions src/core/pal/feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,19 @@ namespace pal
double width;
} CharacterInfo;

LabelInfo( int num, double height )
LabelInfo( int num, double height, double maxinangle = 20.0, double maxoutangle = -20.0 )
{
max_char_angle_delta = 20;
max_char_angle_inside = maxinangle;
// outside angle should be negative
max_char_angle_outside = maxoutangle > 0 ? -maxoutangle : maxoutangle;
label_height = height;
char_num = num;
char_info = new CharacterInfo[num];
}
~LabelInfo() { delete [] char_info; }

double max_char_angle_delta;
double max_char_angle_inside;
double max_char_angle_outside;
double label_height;
int char_num;
CharacterInfo* char_info;
Expand Down
34 changes: 31 additions & 3 deletions src/core/qgspallabeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,23 @@ class QgsPalGeometry : public PalGeometry
const char* strId() { return mStrId.data(); }
QString text() { return mText; }

pal::LabelInfo* info( QFontMetricsF* fm, const QgsMapToPixel* xform, double fontScale )
pal::LabelInfo* info( QFontMetricsF* fm, const QgsMapToPixel* xform, double fontScale, double maxinangle, double maxoutangle )
{
if ( mInfo )
return mInfo;

mFontMetrics = new QFontMetricsF( *fm ); // duplicate metrics for when drawing label

// max angle between curved label characters (20.0/-20.0 was default in QGIS <= 1.8)
if ( maxinangle < 20.0 )
maxinangle = 20.0;
if ( 60.0 < maxinangle )
maxinangle = 60.0;
if ( maxoutangle > -20.0 )
maxoutangle = -20.0;
if ( -95.0 > maxoutangle )
maxoutangle = -95.0;

// create label info!
QgsPoint ptZero = xform->toMapCoordinates( 0, 0 );
QgsPoint ptSize = xform->toMapCoordinatesF( 0.0, -fm->height() / fontScale );
Expand All @@ -111,7 +121,7 @@ class QgsPalGeometry : public PalGeometry
// (non-curved spacings handled by Qt in QgsPalLayerSettings/QgsPalLabeling)
qreal charWidth;
qreal wordSpaceFix;
mInfo = new pal::LabelInfo( mText.count(), ptSize.y() - ptZero.y() );
mInfo = new pal::LabelInfo( mText.count(), ptSize.y() - ptZero.y(), maxinangle, maxoutangle );
for ( int i = 0; i < mText.count(); i++ )
{
mInfo->char_info[i].chr = mText[i].unicode();
Expand Down Expand Up @@ -221,6 +231,8 @@ QgsPalLayerSettings::QgsPalLayerSettings()
reverseDirectionSymbol = false;
placeDirectionSymbol = SymbolLeftRight;
upsidedownLabels = Upright;
maxCurvedCharAngleIn = 20.0;
maxCurvedCharAngleOut = -20.0;
fontSizeInMapUnits = false;
fontLimitPixelSize = false;
fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
Expand Down Expand Up @@ -280,6 +292,8 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
reverseDirectionSymbol = s.reverseDirectionSymbol;
placeDirectionSymbol = s.placeDirectionSymbol;
upsidedownLabels = s.upsidedownLabels;
maxCurvedCharAngleIn = s.maxCurvedCharAngleIn;
maxCurvedCharAngleOut = s.maxCurvedCharAngleOut;
fontSizeInMapUnits = s.fontSizeInMapUnits;
fontLimitPixelSize = s.fontLimitPixelSize;
fontMinPixelSize = s.fontMinPixelSize;
Expand Down Expand Up @@ -470,6 +484,8 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
placeDirectionSymbol = ( DirectionSymbols ) layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt();
upsidedownLabels = ( UpsideDownLabels ) layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt();
maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
Expand Down Expand Up @@ -540,6 +556,8 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
layer->setCustomProperty( "labeling/placeDirectionSymbol", ( unsigned int )placeDirectionSymbol );
layer->setCustomProperty( "labeling/upsidedownLabels", ( unsigned int )upsidedownLabels );
layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
Expand Down Expand Up @@ -776,6 +794,16 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
double labelX, labelY; // will receive label size
calculateLabelSize( labelFontMetrics, labelText, labelX, labelY );

// maximum angle between curved label characters
double maxcharanglein = 20.0;
double maxcharangleout = -20.0;
if ( placement == QgsPalLayerSettings::Curved )
{
maxcharanglein = maxCurvedCharAngleIn;
maxcharangleout = maxCurvedCharAngleOut > 0 ? -maxCurvedCharAngleOut : maxCurvedCharAngleOut;
}
// TODO: add data defined override for maximum angle between curved label characters

QgsGeometry* geom = f.geometry();
if ( !geom )
{
Expand Down Expand Up @@ -1037,7 +1065,7 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
// TODO: only for placement which needs character info
pal::Feature* feat = palLayer->getFeature( lbl->strId() );
// account for any data defined font metrics adjustments
feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor ) );
feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor, maxcharanglein, maxcharangleout ) );
delete labelFontMetrics;

// TODO: allow layer-wide feature dist in PAL...?
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgspallabeling.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ class CORE_EXPORT QgsPalLayerSettings
bool reverseDirectionSymbol;
DirectionSymbols placeDirectionSymbol; // whether to place left/right, above or below label
unsigned int upsidedownLabels; // whether, or how, to show upsidedown labels
double maxCurvedCharAngleIn; // maximum angle between inside curved label characters (defaults to 20.0, range 20.0 to 60.0)
double maxCurvedCharAngleOut; // maximum angle between outside curved label characters (defaults to -20.0, range -20.0 to -95.0)
bool fontSizeInMapUnits; //true if font size is in map units (otherwise in points)
bool fontLimitPixelSize; // true is label should be limited by fontMinPixelSize/fontMaxPixelSize
int fontMinPixelSize; // minimum pixel size for showing rendered map unit labels (1 - 1000)
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgsproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,8 @@ bool QgsProject::write()

dirty( false ); // reset to pristine state

emit projectSaved();

return true;
} // QgsProject::write

Expand Down
3 changes: 3 additions & 0 deletions src/core/qgsproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ class CORE_EXPORT QgsProject : public QObject
//! emitted when project is being written
void writeProject( QDomDocument & );

//! emitted when the project file has been written and closed
void projectSaved();

//! emitted when an old project file is read.
void oldProjectVersionWarning( QString );

Expand Down
12 changes: 11 additions & 1 deletion src/core/qgsvectorfilewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,17 @@ class CORE_EXPORT QgsVectorFileWriter

/** Write contents of vector layer to an (OGR supported) vector formt
@note: this method was added in version 1.5
@param newFileName QString pointer which will contain the new file name created
@param layer layer to write
@param fileName file name to write to
@param fileEncoding encoding to use
@param destCRS pointer to CRS to reproject exported geometries to
@param driverName OGR driver to use
@param onlySelected write only selected features of layer
@param errorMessage pointer to buffer fo error message
@param datasourceOptions list of OGR data source creation options
@param layerOptions list of OGR layer creation options
@param skipAttributeCreation only write geometries
@param newFilename QString pointer which will contain the new file name created
(in case it is different to fileName).
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
Expand Down
8 changes: 2 additions & 6 deletions src/core/symbology-ng/qgsfillsymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,9 @@ QgsSVGFillSymbolLayer::~QgsSVGFillSymbolLayer()

void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
{
QFile svgFile( svgPath );
if ( svgFile.open( QFile::ReadOnly ) )
{
mSvgData = svgFile.readAll();
mSvgData = QgsSvgCache::instance()->getImageData( svgPath );
storeViewBox();

storeViewBox();
}
mSvgFilePath = svgPath;
setDefaultSvgParams();
}
Expand Down
22 changes: 21 additions & 1 deletion src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2Re
}
else
{
p->setOpacity( context.alpha( ) );
p->setOpacity( context.alpha() );
const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( mPath, size, mFillColor, mOutlineColor, mOutlineWidth,
context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() );
p->drawPicture( 0, 0, pct );
Expand Down Expand Up @@ -842,6 +842,26 @@ QString QgsSvgMarkerSymbolLayerV2::symbolNameToPath( QString name )
if ( QFile( name ).exists() )
return QFileInfo( name ).canonicalFilePath();

// or it might be an url...
QUrl url( name );
if ( url.isValid() )
{
if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
{
// it's a url to a local file
name = url.toLocalFile();
if ( QFile( name ).exists() )
{
return QFileInfo( name ).canonicalFilePath();
}
}
else
{
// it's a url pointing to a online resource
return name;
}
}

// SVG symbol not found - probably a relative path was used

QStringList svgPaths = QgsApplication::svgPaths();
Expand Down
146 changes: 131 additions & 15 deletions src/core/symbology-ng/qgssvgcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@

#include "qgssvgcache.h"
#include "qgslogger.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsmessagelog.h"
#include <QApplication>
#include <QCoreApplication>
#include <QCursor>
#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QImage>
#include <QPainter>
#include <QPicture>
#include <QSvgRenderer>
#include <QFileInfo>
#include <QNetworkReply>
#include <QNetworkRequest>

QgsSvgCacheEntry::QgsSvgCacheEntry(): file( QString() ), size( 0 ), outlineWidth( 0 ), widthScaleFactor( 1.0 ), rasterScaleFactor( 1.0 ), fill( Qt::black ),
outline( Qt::black ), image( 0 ), picture( 0 )
Expand Down Expand Up @@ -81,7 +89,11 @@ QgsSvgCache* QgsSvgCache::instance()
return mInstance;
}

QgsSvgCache::QgsSvgCache(): mTotalSize( 0 ), mLeastRecentEntry( 0 ), mMostRecentEntry( 0 )
QgsSvgCache::QgsSvgCache( QObject *parent )
: QObject( parent )
, mTotalSize( 0 )
, mLeastRecentEntry( 0 )
, mMostRecentEntry( 0 )
{
}

Expand Down Expand Up @@ -163,14 +175,8 @@ void QgsSvgCache::containsParams( const QString& path, bool& hasFillParam, QColo
defaultOutlineColor = QColor( Qt::black );
defaultOutlineWidth = 1.0;

QFile svgFile( path );
if ( !svgFile.open( QIODevice::ReadOnly ) )
{
return;
}

QDomDocument svgDoc;
if ( !svgDoc.setContent( &svgFile ) )
if ( !svgDoc.setContent( getImageData( path ) ) )
{
return;
}
Expand All @@ -186,14 +192,8 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry )
return;
}

QFile svgFile( entry->file );
if ( !svgFile.open( QIODevice::ReadOnly ) )
{
return;
}

QDomDocument svgDoc;
if ( !svgDoc.setContent( &svgFile ) )
if ( !svgDoc.setContent( getImageData( entry->file ) ) )
{
return;
}
Expand All @@ -206,6 +206,116 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry )
mTotalSize += entry->svgContent.size();
}

QByteArray QgsSvgCache::getImageData( const QString &path ) const
{
// is it a path to local file?
QFile svgFile( path );
if ( svgFile.exists() )
{
if ( svgFile.open( QIODevice::ReadOnly ) )
{
return svgFile.readAll();
}
else
{
return QByteArray();
}
}

// maybe it's a url...
QUrl svgUrl( path );
if ( !svgUrl.isValid() )
{
return QByteArray();
}

// check whether it's a url pointing to a local file
if ( svgUrl.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
{
svgFile.setFileName( svgUrl.toLocalFile() );
if ( svgFile.exists() )
{
if ( svgFile.open( QIODevice::ReadOnly ) )
{
return svgFile.readAll();
}
}

// not found...
return QByteArray();
}

// the url points to a remote resource, download it!
QNetworkReply *reply = 0;

// The following code blocks until the file is downloaded...
// TODO: use signals to get reply finished notification, in this moment
// it's executed while rendering.
while ( 1 )
{
QgsDebugMsg( QString( "get svg: %1" ).arg( svgUrl.toString() ) );
QNetworkRequest request( svgUrl );
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );

reply = QgsNetworkAccessManager::instance()->get( request );
connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( downloadProgress( qint64, qint64 ) ) );

//emit statusChanged( tr( "Downloading svg." ) );

// wait until the image download finished
// TODO: connect to the reply->finished() signal
while ( !reply->isFinished() )
{
QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 );
}

if ( reply->error() != QNetworkReply::NoError )
{
QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ), tr( "SVG" ) );

reply->deleteLater();
return QByteArray();
}

QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
if ( redirect.isNull() )
{
// neither network error nor redirection
// TODO: cache the image
break;
}

// do a new request to the redirect url
svgUrl = redirect.toUrl();
reply->deleteLater();
}

QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
if ( !status.isNull() && status.toInt() >= 400 )
{
QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ), tr( "SVG" ) );

reply->deleteLater();
return QByteArray();
}

QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
QgsDebugMsg( "contentType: " + contentType );
if ( !contentType.startsWith( "image/svg+xml", Qt::CaseInsensitive ) )
{
reply->deleteLater();
return QByteArray();
}

// read the image data
QByteArray ba = reply->readAll();
reply->deleteLater();

return ba;
}

void QgsSvgCache::cacheImage( QgsSvgCacheEntry* entry )
{
if ( !entry )
Expand Down Expand Up @@ -553,3 +663,9 @@ void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry* entry )
}
}

void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
{
QString msg = tr( "%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString( "unknown number of" ) : QString::number( bytesTotal ) );
QgsDebugMsg( msg );
emit statusChanged( msg );
}
18 changes: 16 additions & 2 deletions src/core/symbology-ng/qgssvgcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <QMap>
#include <QMultiHash>
#include <QString>
#include <QUrl>

class QDomElement;
class QImage;
Expand Down Expand Up @@ -59,8 +60,10 @@ class CORE_EXPORT QgsSvgCacheEntry
/**A cache for images / pictures derived from svg files. This class supports parameter replacement in svg files
according to the svg params specification (http://www.w3.org/TR/2009/WD-SVGParamPrimer-20090616/). Supported are
the parameters 'fill-color', 'pen-color', 'outline-width', 'stroke-width'. E.g. <circle fill="param(fill-color red)" stroke="param(pen-color black)" stroke-width="param(outline-width 1)"*/
class CORE_EXPORT QgsSvgCache
class CORE_EXPORT QgsSvgCache : public QObject
{
Q_OBJECT

public:

static QgsSvgCache* instance();
Expand All @@ -76,8 +79,16 @@ class CORE_EXPORT QgsSvgCache
void containsParams( const QString& path, bool& hasFillParam, QColor& defaultFillColor, bool& hasOutlineParam, QColor& defaultOutlineColor, bool& hasOutlineWidthParam,
double& defaultOutlineWidth ) const;

/**Get image data*/
QByteArray getImageData( const QString &path ) const;

signals:
/** Emit a signal to be caught by qgisapp and display a msg on status bar */
void statusChanged( QString const & theStatusQString );

protected:
QgsSvgCache();
//! protected constructor
QgsSvgCache( QObject * parent = 0 );

/**Creates new cache entry and returns pointer to it*/
QgsSvgCacheEntry* insertSVG( const QString& file, int size, const QColor& fill, const QColor& outline, double outlineWidth,
Expand All @@ -96,6 +107,9 @@ class CORE_EXPORT QgsSvgCache
//Removes entry from the ordered list (but does not delete the entry itself)
void takeEntryFromList( QgsSvgCacheEntry* entry );

private slots:
void downloadProgress( qint64, qint64 );

private:
static QgsSvgCache* mInstance;

Expand Down
6 changes: 5 additions & 1 deletion src/gui/qgisinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,16 @@ class GUI_EXPORT QgisInterface : public QObject
Q_DECL_DEPRECATED
#endif
virtual QAction *actionLayerSeparator1() = 0;
/** @note added in 1.9 */
virtual QAction *actionCopyLayerStyle() = 0;
/** @note added in 1.9 */
virtual QAction *actionPasteLayerStyle() = 0;
virtual QAction *actionOpenTable() = 0;
virtual QAction *actionToggleEditing() = 0;
virtual QAction *actionLayerSaveAs() = 0;
virtual QAction *actionLayerSelectionSaveAs() = 0;
virtual QAction *actionRemoveLayer() = 0;
/** @note added in 2.0 */
/** @note added in 1.9 */
virtual QAction *actionDuplicateLayer() = 0;
virtual QAction *actionLayerProperties() = 0;
#ifndef Q_MOC_RUN
Expand Down
64 changes: 57 additions & 7 deletions src/gui/symbology-ng/qgssymbollayerv2widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include <QAbstractButton>
#include <QColorDialog>
#include <QCursor>
#include <QDir>
#include <QFileDialog>
#include <QPainter>
Expand Down Expand Up @@ -683,12 +684,16 @@ void QgsSvgMarkerSymbolLayerV2Widget::setGuiForSvg( const QgsSvgMarkerSymbolLaye
mBorderWidthSpinBox->blockSignals( true );
mBorderWidthSpinBox->setValue( layer->outlineWidth() );
mBorderWidthSpinBox->blockSignals( false );

}


void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
{
if ( !layer )
{
return;
}

if ( layer->layerType() != "SvgMarker" )
return;

Expand All @@ -711,8 +716,6 @@ void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
}
}



spinSize->setValue( mLayer->size() );
spinAngle->setValue( mLayer->angle() );

Expand All @@ -725,7 +728,6 @@ void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
spinOffsetY->blockSignals( false );

setGuiForSvg( mLayer );

}

QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2Widget::symbolLayer()
Expand Down Expand Up @@ -790,6 +792,25 @@ void QgsSvgMarkerSymbolLayerV2Widget::on_mFileLineEdit_textEdited( const QString
emit changed();
}

void QgsSvgMarkerSymbolLayerV2Widget::on_mFileLineEdit_editingFinished()
{
if ( !QFileInfo( mFileLineEdit->text() ).exists() )
{
QUrl url( mFileLineEdit->text() );
if ( !url.isValid() )
{
return;
}
}

QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
mLayer->setPath( mFileLineEdit->text() );
QApplication::restoreOverrideCursor();

setGuiForSvg( mLayer );
emit changed();
}

void QgsSvgMarkerSymbolLayerV2Widget::on_mChangeColorButton_clicked()
{
if ( !mLayer )
Expand Down Expand Up @@ -945,7 +966,7 @@ void QgsSVGFillSymbolLayerWidget::on_mTextureWidthSpinBox_valueChanged( double d
}
}

void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_textChanged( const QString & text )
void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_textEdited( const QString & text )
{
if ( !mLayer )
{
Expand All @@ -958,14 +979,43 @@ void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_textChanged( const QString & t
return;
}
mLayer->setSvgFilePath( text );
updateParamGui();
emit changed();
}

void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_editingFinished()
{
if ( !mLayer )
{
return;
}

QFileInfo fi( mSVGLineEdit->text() );
if ( !fi.exists() )
{
QUrl url( mSVGLineEdit->text() );
if ( !url.isValid() )
{
return;
}
}

QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
mLayer->setSvgFilePath( mSVGLineEdit->text() );
QApplication::restoreOverrideCursor();

updateParamGui();
emit changed();
}

void QgsSVGFillSymbolLayerWidget::setFile( const QModelIndex& item )
{
mSVGLineEdit->setText( item.data( Qt::UserRole ).toString() );
QString file = item.data( Qt::UserRole ).toString();
mLayer->setSvgFilePath( file );
mSVGLineEdit->setText( file );

updateParamGui();
emit changed();
}

void QgsSVGFillSymbolLayerWidget::insertIcons()
Expand Down Expand Up @@ -1000,8 +1050,8 @@ void QgsSVGFillSymbolLayerWidget::on_mRotationSpinBox_valueChanged( double d )
if ( mLayer )
{
mLayer->setAngle( d );
emit changed();
}
emit changed();
}

void QgsSVGFillSymbolLayerWidget::updateParamGui()
Expand Down
4 changes: 3 additions & 1 deletion src/gui/symbology-ng/qgssymbollayerv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerV2Widget : public QgsSymbolLayerV2Widget
void setOffset();
void on_mFileToolButton_clicked();
void on_mFileLineEdit_textEdited( const QString& text );
void on_mFileLineEdit_editingFinished();
void on_mChangeColorButton_clicked();
void on_mChangeBorderColorButton_clicked();
void on_mBorderWidthSpinBox_valueChanged( double d );
Expand Down Expand Up @@ -266,7 +267,8 @@ class GUI_EXPORT QgsSVGFillSymbolLayerWidget : public QgsSymbolLayerV2Widget, pr
private slots:
void on_mBrowseToolButton_clicked();
void on_mTextureWidthSpinBox_valueChanged( double d );
void on_mSVGLineEdit_textChanged( const QString & text );
void on_mSVGLineEdit_textEdited( const QString & text );
void on_mSVGLineEdit_editingFinished();
void setFile( const QModelIndex& item );
void populateIcons( const QModelIndex& item );
void on_mRotationSpinBox_valueChanged( double d );
Expand Down
4 changes: 3 additions & 1 deletion src/mapserver/qgsprojectparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2703,7 +2703,9 @@ void QgsProjectParser::drawAnnotationRectangle( QPainter* p, const QDomElement&
QColor backgroundColor( elem.attribute( "frameBackgroundColor", "#000000" ) );
backgroundColor.setAlpha( elem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
p->setBrush( QBrush( backgroundColor ) );
QPen framePen( QColor( elem.attribute( "frameColor", "#000000" ) ) );
QColor frameColor( elem.attribute( "frameColor", "#000000" ) );
frameColor.setAlpha( elem.attribute( "frameColorAlpha", "255" ).toInt() );
QPen framePen( frameColor );
framePen.setWidth( elem.attribute( "frameBorderWidth", "1" ).toInt() );
p->setPen( framePen );

Expand Down
4 changes: 2 additions & 2 deletions src/mapserver/qgswfsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ QDomDocument QgsWFSServer::getCapabilities()
QDomElement wfsCapabilitiesElement = doc.createElement( "WFS_Capabilities"/*wms:WFS_Capabilities*/ );
wfsCapabilitiesElement.setAttribute( "xmlns", WFS_NAMESPACE );
wfsCapabilitiesElement.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
wfsCapabilitiesElement.setAttribute( "xsi:schemaLocation", WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
wfsCapabilitiesElement.setAttribute( "xsi:schemaLocation", WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.0.0/WFS-capabilities.xsd" );
wfsCapabilitiesElement.setAttribute( "xmlns:ogc", OGC_NAMESPACE );
wfsCapabilitiesElement.setAttribute( "xmlns:gml", GML_NAMESPACE );
wfsCapabilitiesElement.setAttribute( "xmlns:ows", "http://www.opengis.net/ows" );
Expand Down Expand Up @@ -180,7 +180,7 @@ QDomDocument QgsWFSServer::getCapabilities()
QDomElement getElement = doc.createElement( "Get"/*wfs:Get*/ );
httpElement.appendChild( getElement );
requestUrl.truncate( requestUrl.indexOf( "?" ) + 1 );
getElement.setAttribute( "OnlineResource", hrefString );
getElement.setAttribute( "onlineResource", hrefString );

//wfs:DescribeFeatureType
QDomElement describeFeatureTypeElement = doc.createElement( "DescribeFeatureType"/*wfs:DescribeFeatureType*/ );
Expand Down
140 changes: 138 additions & 2 deletions src/ui/qgslabelingguibase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1921,7 +1921,7 @@
<x>0</x>
<y>0</y>
<width>686</width>
<height>574</height>
<height>618</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_13">
Expand Down Expand Up @@ -2366,6 +2366,142 @@
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="mMaxCharAngleFrame">
<layout class="QGridLayout" name="gridLayout_22">
<property name="verticalSpacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item row="1" column="4">
<widget class="QDoubleSpinBox" name="mMaxCharAngleOutDSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>20.000000000000000</double>
</property>
<property name="maximum">
<double>95.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<widget class="QLabel" name="mMaxCharAngleOutLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>outside</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="mMaxCharAngleInLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>inside</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="mMaxCharAngleInDSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>20.000000000000000</double>
</property>
<property name="maximum">
<double>60.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="5">
<spacer name="horizontalSpacer_12">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="6">
<widget class="QLabel" name="mMaxCharAngleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Maximum angle between curved characters</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageOptionsEmpty"/>
Expand Down Expand Up @@ -2858,7 +2994,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<width>686</width>
<height>981</height>
</rect>
</property>
Expand Down