532 changes: 466 additions & 66 deletions src/app/composer/qgscomposer.cpp

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions src/app/composer/qgscomposerlabelwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "qgscomposerlabelwidget.h"
#include "qgscomposerlabel.h"
#include "qgscomposeritemwidget.h"
#include "qgsexpressionbuilderdialog.h"

#include <QColorDialog>
#include <QFontDialog>
#include <QWidget>
Expand Down Expand Up @@ -98,6 +100,33 @@ void QgsComposerLabelWidget::on_mFontColorButton_clicked()
mComposerLabel->endCommand();
}

void QgsComposerLabelWidget::on_mInsertExpressionButton_clicked()
{
if ( !mComposerLabel)
{
return;
}

QString selText = mTextEdit->textCursor().selectedText();

// edit the selected expression if there's one
if ( selText.startsWith( "[%" ) && selText.endsWith( "%]" ) )
selText = selText.mid( 2, selText.size() - 4 );

QgsExpressionBuilderDialog exprDlg( /* layer = */ 0, selText, this );
exprDlg.setWindowTitle( tr( "Insert expression" ) );
if ( exprDlg.exec() == QDialog::Accepted )
{
QString expression = exprDlg.expressionText();
if ( !expression.isEmpty() )
{
mComposerLabel->beginCommand( tr( "Insert expression" ) );
mTextEdit->insertPlainText( "[%" + expression + "%]" );
mComposerLabel->endCommand();
}
}
}

void QgsComposerLabelWidget::on_mCenterRadioButton_clicked()
{
if ( mComposerLabel )
Expand Down
1 change: 1 addition & 0 deletions src/app/composer/qgscomposerlabelwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class QgsComposerLabelWidget: public QWidget, private Ui::QgsComposerLabelWidget
public slots:
void on_mTextEdit_textChanged();
void on_mFontButton_clicked();
void on_mInsertExpressionButton_clicked();
void on_mMarginDoubleSpinBox_valueChanged( double d );
void on_mFontColorButton_clicked();
void on_mCenterRadioButton_clicked();
Expand Down
169 changes: 169 additions & 0 deletions src/app/composer/qgscomposermapwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@
//#include "qgssymbolv2propertiesdialog.h"
#include "qgssymbolv2selectordialog.h"
#include "qgssymbollayerv2utils.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsmaplayerregistry.h"
#include "qgscomposershape.h"
#include "qgspaperitem.h"
#include "qgsexpressionbuilderdialog.h"
#include <QColorDialog>
#include <QFontDialog>
#include <QMessageBox>

QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ): QWidget(), mComposerMap( composerMap )
{
Expand Down Expand Up @@ -409,6 +416,29 @@ void QgsComposerMapWidget::updateGuiElements()
mLineWidthSpinBox->setValue( gridPen.widthF() );
mLineColorButton->setColor( gridPen.color() );

// special processing for atlas
QgsComposition* composition = mComposerMap->composition();
if ( composition->atlasMap() && composition->atlasMap() == mComposerMap )
{
mIsAtlasCheckBox->setCheckState( Qt::Checked );

int idx = mAtlasCoverageLayerComboBox->findData( qVariantFromValue( (void*)mComposerMap->atlasCoverageLayer() ));
if ( idx != -1 )
{
mAtlasCoverageLayerComboBox->setCurrentIndex( idx );
}

mAtlasMarginSpinBox->setValue( static_cast<int>(mComposerMap->atlasMargin() * 100) );
mAtlasFilenamePatternEdit->setText( mComposerMap->atlasFilenamePattern() );
mAtlasFixedScaleCheckBox->setCheckState( mComposerMap->atlasFixedScale() ? Qt::Checked : Qt::Unchecked );
mAtlasHideCoverageCheckBox->setCheckState( mComposerMap->atlasHideCoverage() ? Qt::Checked : Qt::Unchecked );
mAtlasSingleFileCheckBox->setCheckState( mComposerMap->atlasSingleFile() ? Qt::Checked : Qt::Unchecked );
}
else
{
mIsAtlasCheckBox->setCheckState( Qt::Unchecked );
}

blockAllSignals( false );
}
}
Expand Down Expand Up @@ -897,12 +927,151 @@ void QgsComposerMapWidget::on_mFrameWidthSpinBox_valueChanged( double d )
}
}

void QgsComposerMapWidget::on_mIsAtlasCheckBox_stateChanged( int state )
{
if ( !mComposerMap )
{
return;
}

QgsComposition* composition = mComposerMap->composition();
if ( state == Qt::Checked )
{
if ( composition->atlasMap() != 0 && composition->atlasMap() != mComposerMap )
{
QMessageBox msgBox;
msgBox.setText(tr("An atlas map already exists."));
msgBox.setInformativeText("Are you sure to define this map as the new atlas map ?");
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No );
msgBox.setDefaultButton(QMessageBox::No);
if ( msgBox.exec() != QMessageBox::Yes )
{
mIsAtlasCheckBox->setCheckState( Qt::Unchecked );
return;
}
}
composition->setAtlasMap( mComposerMap );

// repopulate the layer list
mAtlasCoverageLayerComboBox->clear();
QMap< QString, QgsMapLayer * >& layers = QgsMapLayerRegistry::instance()->mapLayers();
int idx = 0;
for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
{
// Only consider vector layers
if ( dynamic_cast<QgsVectorLayer*>(it.value()) )
{
mAtlasCoverageLayerComboBox->insertItem( idx++, it.key(), /* userdata */ qVariantFromValue( (void*)it.value() ) );
}
}

mAtlasFrame->setEnabled( true );
updateGuiElements();
}
else
{
mAtlasFrame->setEnabled( false );

// If the current atlas map was this one and a uncheck is requested, set the atlas map to null
if ( composition->atlasMap() == mComposerMap )
{
composition->setAtlasMap( 0 );
}
}
}

void QgsComposerMapWidget::on_mAtlasCoverageLayerComboBox_currentIndexChanged( int index )
{
if ( !mComposerMap )
{
return;
}

QgsVectorLayer* layer = reinterpret_cast<QgsVectorLayer*>(mAtlasCoverageLayerComboBox->itemData( index ).value<void*>());
mComposerMap->setAtlasCoverageLayer( layer );
}

void QgsComposerMapWidget::on_mAtlasFilenamePatternEdit_textChanged( const QString& text )
{
if ( !mComposerMap )
{
return;
}

mComposerMap->setAtlasFilenamePattern( text );
}

void QgsComposerMapWidget::on_mAtlasFilenameExpressionButton_clicked()
{
if ( !mComposerMap )
{
return;
}
if ( !mComposerMap->atlasCoverageLayer() )
{
return;
}
QgsExpressionBuilderDialog exprDlg( mComposerMap->atlasCoverageLayer(), mAtlasFilenamePatternEdit->text(), this );
exprDlg.setWindowTitle( tr( "Expression based filename" ) );
if ( exprDlg.exec() == QDialog::Accepted )
{
QString expression = exprDlg.expressionText();
if ( !expression.isEmpty() )
{
// will emit a textChanged signal
mAtlasFilenamePatternEdit->setText( expression );
}
}
}

void QgsComposerMapWidget::on_mAtlasHideCoverageCheckBox_stateChanged( int state )
{
if (!mComposerMap)
{
return;
}
mComposerMap->setAtlasHideCoverage( state == Qt::Checked );
}

void QgsComposerMapWidget::on_mAtlasFixedScaleCheckBox_stateChanged( int state )
{
if (!mComposerMap)
{
return;
}
mComposerMap->setAtlasFixedScale( state == Qt::Checked );

// in fixed scale mode, the margin is meaningless
if ( state == Qt::Checked )
{
mAtlasMarginSpinBox->setEnabled( false );
}
else
{
mAtlasMarginSpinBox->setEnabled( true );
}
}

void QgsComposerMapWidget::on_mAtlasSingleFileCheckBox_stateChanged( int state )
{
if (!mComposerMap)
{
return;
}
mComposerMap->setAtlasSingleFile( state == Qt::Checked );
}

void QgsComposerMapWidget::showEvent( QShowEvent * event )
{
refreshMapComboBox();
QWidget::showEvent( event );
}

void QgsComposerMapWidget::addPageToToolbox( QWidget* widget, const QString& name )
{
toolBox->addItem( widget, name );
}

void QgsComposerMapWidget::insertAnnotationPositionEntries( QComboBox* c )
{
c->insertItem( 0, tr( "Inside frame" ) );
Expand Down
18 changes: 14 additions & 4 deletions src/app/composer/qgscomposermapwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class QgsComposerMapWidget: public QWidget, private Ui::QgsComposerMapWidgetBase
public:

QgsComposerMapWidget( QgsComposerMap* composerMap );
~QgsComposerMapWidget();
virtual ~QgsComposerMapWidget();

public slots:
void on_mWidthLineEdit_editingFinished();
Expand Down Expand Up @@ -83,9 +83,22 @@ class QgsComposerMapWidget: public QWidget, private Ui::QgsComposerMapWidgetBase
void on_mFrameStyleComboBox_currentIndexChanged( const QString& text );
void on_mFrameWidthSpinBox_valueChanged( double d );

void on_mIsAtlasCheckBox_stateChanged( int state );
void on_mAtlasCoverageLayerComboBox_currentIndexChanged( int index );
void on_mAtlasFilenamePatternEdit_textChanged( const QString& );
void on_mAtlasFilenameExpressionButton_clicked();
void on_mAtlasHideCoverageCheckBox_stateChanged( int state );
void on_mAtlasFixedScaleCheckBox_stateChanged( int state );
void on_mAtlasSingleFileCheckBox_stateChanged( int state );

protected:
void showEvent( QShowEvent * event );

void addPageToToolbox( QWidget * widget, const QString& name );

/**Sets the current composer map values to the GUI elements*/
virtual void updateGuiElements();

private slots:

/**Sets the GUI elements to the values of mPicture*/
Expand All @@ -94,9 +107,6 @@ class QgsComposerMapWidget: public QWidget, private Ui::QgsComposerMapWidgetBase
private:
QgsComposerMap* mComposerMap;

/**Sets the current composer map values to the GUI elements*/
void updateGuiElements();

/**Sets extent of composer map from line edits*/
void updateComposerExtentFromGui();

Expand Down
1 change: 1 addition & 0 deletions src/core/composer/qgscomposeritem.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
virtual void removeItems() {}

const QgsComposition* composition() const {return mComposition;}
QgsComposition* composition() {return mComposition;}

virtual void beginItemCommand( const QString& text ) { beginCommand( text ); }

Expand Down
26 changes: 21 additions & 5 deletions src/core/composer/qgscomposerlabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
***************************************************************************/

#include "qgscomposerlabel.h"
#include "qgsexpression.h"
#include <QDate>
#include <QDomElement>
#include <QPainter>

QgsComposerLabel::QgsComposerLabel( QgsComposition *composition ): QgsComposerItem( composition ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ),
mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop )
QgsComposerLabel::QgsComposerLabel( QgsComposition *composition ):
QgsComposerItem( composition ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ),
mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop ),
mExpressionFeature( 0 ), mExpressionLayer( 0 )
{
//default font size is 10 point
mFont.setPointSizeF( 10 );
Expand Down Expand Up @@ -75,23 +78,36 @@ void QgsComposerLabel::setText( const QString& text )
emit itemChanged();
}

void QgsComposerLabel::setExpressionContext( QgsFeature* feature, QgsVectorLayer* layer, QMap<QString, QVariant> substitutions )
{
mExpressionFeature = feature;
mExpressionLayer = layer;
mSubstitutions = substitutions;
}

QString QgsComposerLabel::displayText() const
{
QString displayText = mText;
replaceDateText( displayText );
return displayText;
QMap<QString, QVariant> subs = mSubstitutions;
subs[ "$page" ] = QVariant((int)mComposition->itemPageNumber( this ) + 1);
return QgsExpression::replaceExpressionText( displayText, mExpressionFeature, mExpressionLayer, &subs );
}

void QgsComposerLabel::replaceDateText( QString& text ) const
{
int currentDatePos = text.indexOf( "$CURRENT_DATE" );
QString constant = "$CURRENT_DATE";
int currentDatePos = text.indexOf( constant );
if ( currentDatePos != -1 )
{
//check if there is a bracket just after $CURRENT_DATE
QString formatText;
int openingBracketPos = text.indexOf( "(", currentDatePos );
int closingBracketPos = text.indexOf( ")", openingBracketPos + 1 );
if ( openingBracketPos != -1 && closingBracketPos != -1 && ( closingBracketPos - openingBracketPos ) > 1 )
if ( openingBracketPos != -1 &&
closingBracketPos != -1 &&
( closingBracketPos - openingBracketPos ) > 1 &&
openingBracketPos == currentDatePos + constant.size() )
{
formatText = text.mid( openingBracketPos + 1, closingBracketPos - openingBracketPos - 1 );
text.replace( currentDatePos, closingBracketPos - currentDatePos + 1, QDate::currentDate().toString( formatText ) );
Expand Down
10 changes: 10 additions & 0 deletions src/core/composer/qgscomposerlabel.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

#include "qgscomposeritem.h"

class QgsVectorLayer;
class QgsFeature;

/** \ingroup MapComposer
* A label that can be placed onto a map composition.
*/
Expand All @@ -45,6 +48,9 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
@note this function was added in version 1.2*/
QString displayText() const;

/** Sets the current feature, the current layer and a list of local variable substitutions for evaluating expressions */
void setExpressionContext( QgsFeature* feature, QgsVectorLayer* layer, QMap<QString, QVariant> substitutions = QMap<QString, QVariant>() );

QFont font() const;
void setFont( const QFont& f );
/** Accessor for the vertical alignment of the label
Expand Down Expand Up @@ -120,6 +126,10 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
double mTextBoxWidth;
/**Height of the text box. This is different to rectangle().height() in case there is rotation*/
double mTextBoxHeight;

QgsFeature* mExpressionFeature;
QgsVectorLayer* mExpressionLayer;
QMap<QString, QVariant> mSubstitutions;
};

#endif
Expand Down
72 changes: 69 additions & 3 deletions src/core/composer/qgscomposermap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@
#include <cmath>

QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
: QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ), mGridEnabled( false ), mGridStyle( Solid ),
: QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ),
mOverviewFrameMapId( -1 ), mGridEnabled( false ), mGridStyle( Solid ),
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ),
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ), mTopGridAnnotationPosition( OutsideMapFrame ),
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
mTopGridAnnotationDirection( Horizontal ), mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ),
mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true )
mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true ),
mAtlasHideCoverage( false ), mAtlasFixedScale( false ), mAtlasMargin( 0.10 ), mAtlasFilenamePattern("'output_'||$feature"), mAtlasCoverageLayer(0), mAtlasSingleFile( false )
{
mComposition = composition;
mOverviewFrameMapSymbol = 0;
Expand Down Expand Up @@ -84,7 +86,8 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ), mTopGridAnnotationPosition( OutsideMapFrame ),
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
mTopGridAnnotationDirection( Horizontal ), mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ), mCrossLength( 3 ),
mMapCanvas( 0 ), mDrawCanvasItems( true )
mMapCanvas( 0 ), mDrawCanvasItems( true ),
mAtlasHideCoverage( false ), mAtlasFixedScale( false ), mAtlasMargin( 0.10 ), mAtlasFilenamePattern("'output_'||$feature"), mAtlasCoverageLayer(0), mAtlasSingleFile( false )
{
mOverviewFrameMapSymbol = 0;
createDefaultOverviewFrameSymbol();
Expand Down Expand Up @@ -617,10 +620,26 @@ void QgsComposerMap::connectUpdateSlot()
if ( layerRegistry )
{
connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( syncAtlasCoverageLayer( QString ) ) );
connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
}
}

void QgsComposerMap::syncAtlasCoverageLayer( QString lname )
{
if ( mAtlasCoverageLayer && mAtlasCoverageLayer->id() == lname )
{
mAtlasCoverageLayer = 0;
}
}

void QgsComposerMap::setAtlasCoverageLayer( QgsVectorLayer* map )
{
mAtlasCoverageLayer = map;

emit atlasCoverageLayerChanged( map );
}

bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
{
if ( elem.isNull() )
Expand Down Expand Up @@ -728,6 +747,27 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
gridElem.appendChild( annotationElem );
composerMapElem.appendChild( gridElem );

// atlas
if ( mComposition->atlasMap() == this )
{
QDomElement atlasElem = doc.createElement( "Atlas" );
if ( mAtlasCoverageLayer )
{
atlasElem.setAttribute( "coverageLayer", mAtlasCoverageLayer->id() );
}
else
{
atlasElem.setAttribute( "coverageLayer", "" );
}
atlasElem.setAttribute( "hideCoverage", mAtlasHideCoverage ? "true" : "false" );
atlasElem.setAttribute( "fixedScale", mAtlasFixedScale ? "true" : "false" );
atlasElem.setAttribute( "singleFile", mAtlasSingleFile ? "true" : "false" );
atlasElem.setAttribute( "margin", QString::number(mAtlasMargin) );
atlasElem.setAttribute( "filenamePattern", mAtlasFilenamePattern );

composerMapElem.appendChild( atlasElem );
}

elem.appendChild( composerMapElem );
return _writeXML( composerMapElem, doc );
}
Expand Down Expand Up @@ -865,6 +905,32 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
}
}

// atlas
QDomNodeList atlasNodeList = itemElem.elementsByTagName( "Atlas" );
if ( atlasNodeList.size() > 0 )
{
mComposition->setAtlasMap( this );

QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();

// look for stored layer name
mAtlasCoverageLayer = 0;
QMap<QString, QgsMapLayer*> layers = QgsMapLayerRegistry::instance()->mapLayers();
for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
{
if ( it.key() == atlasElem.attribute("coverageLayer") )
{
mAtlasCoverageLayer = dynamic_cast<QgsVectorLayer*>(it.value());
break;
}
}
mAtlasMargin = atlasElem.attribute( "margin", "0.0" ).toDouble();
mAtlasHideCoverage = atlasElem.attribute( "hideCoverage", "false" ) == "true" ? true : false;
mAtlasFixedScale = atlasElem.attribute( "fixedScale", "false" ) == "true" ? true : false;
mAtlasSingleFile = atlasElem.attribute( "singleFile", "false" ) == "true" ? true : false;
mAtlasFilenamePattern = atlasElem.attribute( "filenamePattern", "" );
}

//restore general composer item properties
QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
if ( composerItemList.size() > 0 )
Expand Down
33 changes: 32 additions & 1 deletion src/core/composer/qgscomposermap.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class QDomDocument;
class QGraphicsView;
class QPainter;
class QgsFillSymbolV2;
class QgsVectorLayer;

/** \ingroup MapComposer
* \class QgsComposerMap
Expand All @@ -45,7 +46,7 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height );
/** Constructor. Settings are read from project. */
QgsComposerMap( QgsComposition *composition );
~QgsComposerMap();
virtual ~QgsComposerMap();

/** return correct graphics item type. Added in v1.7 */
virtual int type() const { return ComposerMap; }
Expand Down Expand Up @@ -316,16 +317,39 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
Usually, this function is called before adding the composer map to the composition*/
void assignFreeId();

bool atlasHideCoverage() const { return mAtlasHideCoverage; }
void setAtlasHideCoverage( bool hide ) { mAtlasHideCoverage = hide; }

bool atlasFixedScale() const { return mAtlasFixedScale; }
void setAtlasFixedScale( bool fixed ) { mAtlasFixedScale = fixed; }

float atlasMargin() const { return mAtlasMargin; }
void setAtlasMargin( float margin ) { mAtlasMargin = margin; }

QString atlasFilenamePattern() const { return mAtlasFilenamePattern; }
void setAtlasFilenamePattern( const QString& pattern ) { mAtlasFilenamePattern = pattern; }

QgsVectorLayer* atlasCoverageLayer() const { return mAtlasCoverageLayer; }
void setAtlasCoverageLayer( QgsVectorLayer* lmap );

bool atlasSingleFile() const { return mAtlasSingleFile; }
void setAtlasSingleFile( bool single ) { mAtlasSingleFile = single; }

signals:
void extentChanged();

void atlasCoverageLayerChanged( QgsVectorLayer* );

public slots:

/**Called if map canvas has changed*/
void updateCachedImage( );
/**Call updateCachedImage if item is in render mode*/
void renderModeUpdateCachedImage();

private slots:
void syncAtlasCoverageLayer( QString );

private:

enum AnnotationCoordinate
Expand Down Expand Up @@ -438,6 +462,13 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**True if annotation items, rubber band, etc. from the main canvas should be displayed*/
bool mDrawCanvasItems;

bool mAtlasHideCoverage;
bool mAtlasFixedScale;
double mAtlasMargin;
QString mAtlasFilenamePattern;
QgsVectorLayer* mAtlasCoverageLayer;
bool mAtlasSingleFile;

/**Draws the map grid*/
void drawGrid( QPainter* p );
void drawGridFrame( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines );
Expand Down
342 changes: 324 additions & 18 deletions src/core/composer/qgscomposition.cpp

Large diffs are not rendered by default.

60 changes: 59 additions & 1 deletion src/core/composer/qgscomposition.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
#ifndef QGSCOMPOSITION_H
#define QGSCOMPOSITION_H

#include <memory>

#include <QDomDocument>
#include <QGraphicsScene>
#include <QLinkedList>
#include <QSet>
#include <QUndoStack>
#include <QPrinter>
#include <QPainter>

#include "qgsaddremoveitemcommand.h"
#include "qgscomposeritemcommand.h"
Expand All @@ -44,6 +48,37 @@ class QgsComposerShape;
class QgsComposerAttributeTable;
class QgsComposerMultiFrame;
class QgsComposerMultiFrameCommand;
class QgsVectorLayer;

/** \ingroup MapComposer
* Class used to render an Atlas, iterating over geometry features.
* prepareForFeature() modifies the atlas map's extent to zoom on the given feature.
* This class is used for printing, exporting to PDF and images.
* */
class QgsAtlasRendering
{
public:
QgsAtlasRendering( QgsComposition* composition );

/** Begins the rendering. Sets an optional output filename pattern */
void begin( const QString& filenamePattern = "" );
/** Ends the rendering. Restores original extent*/
void end();

/** Returns the number of features in the coverage layer */
size_t numFeatures() const;

/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( size_t i );

/** Returns the current filename. Must be called after prepareForFeature( i ) */
const QString& currentFilename() const;

private:
// Use the PImpl idiom for private members.
struct QgsAtlasRenderingImpl;
std::auto_ptr<QgsAtlasRenderingImpl> impl;
};

/** \ingroup MapComposer
* Graphics scene for map printing. The class manages the paper item which always
Expand Down Expand Up @@ -115,6 +150,12 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
/**Returns the topmost composer item. Ignores mPaperItem*/
QgsComposerItem* composerItemAt( const QPointF & position );

/** Returns the page number (0-bsaed) given a coordinate */
int pageNumberAt( const QPointF& position ) const;

/** Returns on which page number (0-based) is displayed an item */
int itemPageNumber( const QgsComposerItem* ) const;

QList<QgsComposerItem*> selectedComposerItems();

/**Returns pointers to all composer maps in the scene
Expand Down Expand Up @@ -159,6 +200,9 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
/**Returns pointer to map renderer of qgis map canvas*/
QgsMapRenderer* mapRenderer() {return mMapRenderer;}

QgsComposerMap* atlasMap() { return mAtlasMap; }
void setAtlasMap( QgsComposerMap* map );

QgsComposition::PlotStyle plotStyle() const {return mPlotStyle;}
void setPlotStyle( QgsComposition::PlotStyle style ) {mPlotStyle = style;}

Expand Down Expand Up @@ -271,10 +315,19 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene

//printing

void exportAsPDF( const QString& file );
/** Prepare the printer for printing */
void beginPrint( QPrinter& printer );
/** Prepare the printer for printing in a PDF */
void beginPrintAsPDF( QPrinter& printer, const QString& file );
/** Print on a preconfigured printer */
void doPrint( QPrinter& printer, QPainter& painter );

/** Convenience function that prepares the printer and prints */
void print( QPrinter &printer );

/** Convenience function that prepares the printer for printing in PDF and prints */
void exportAsPDF( const QString& file );

//! print composer page to image
//! If the image does not fit into memory, a null image is returned
QImage printPageAsRaster( int page );
Expand All @@ -287,6 +340,9 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
/**Casts object to the proper subclass type and calls corresponding itemAdded signal*/
void sendItemAddedSignal( QgsComposerItem* item );

private slots:
void onAtlasCoverageChanged( QgsVectorLayer* );

private:
/**Pointer to map renderer of QGIS main map*/
QgsMapRenderer* mMapRenderer;
Expand Down Expand Up @@ -324,6 +380,8 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
QgsComposerItemCommand* mActiveItemCommand;
QgsComposerMultiFrameCommand* mActiveMultiFrameCommand;

QgsComposerMap* mAtlasMap;

QgsComposition(); //default constructor is forbidden

/**Reset z-values of items based on position in z list*/
Expand Down
116 changes: 107 additions & 9 deletions src/core/qgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,19 @@ static QVariant fcnFormatNumber( const QVariantList& values, QgsFeature*, QgsExp
return QString( "%L1" ).arg( value, 0, 'f', places );
}

static QVariant fcnFormatDate( const QVariantList& values, QgsFeature*, QgsExpression* parent )
{
QDateTime dt = getDateTimeValue( values.at( 0 ), parent );
QString format = getStringValue( values.at( 1 ), parent );
return dt.toString( format );
}

static QVariant fcnSpecialColumn( const QVariantList& values, QgsFeature* /*f*/, QgsExpression* parent )
{
QString varName = getStringValue( values.at( 0 ), parent );
return QgsExpression::specialColumn( varName );
}

QList<QgsExpression::FunctionDef> QgsExpression::gmBuiltinFunctions;

const QList<QgsExpression::FunctionDef> &QgsExpression::BuiltinFunctions()
Expand Down Expand Up @@ -855,6 +868,7 @@ const QList<QgsExpression::FunctionDef> &QgsExpression::BuiltinFunctions()
<< FunctionDef( "rpad", 3, fcnRPad, QObject::tr( "String" ) )
<< FunctionDef( "lpad", 3, fcnLPad, QObject::tr( "String" ) )
<< FunctionDef( "format_number", 2, fcnFormatNumber, QObject::tr( "String" ) )
<< FunctionDef( "format_date", 2, fcnFormatDate, QObject::tr( "String" ) )

// geometry accessors
<< FunctionDef( "xat", 1, fcnXat, QObject::tr( "Geometry" ), "", true )
Expand All @@ -868,12 +882,61 @@ const QList<QgsExpression::FunctionDef> &QgsExpression::BuiltinFunctions()
<< FunctionDef( "$rownum", 0, fcnRowNumber, QObject::tr( "Record" ) )
<< FunctionDef( "$id", 0, fcnFeatureId, QObject::tr( "Record" ) )
<< FunctionDef( "$scale", 0, fcnScale, QObject::tr( "Record" ) )
// private functions
<< FunctionDef( "_specialcol_", 1, fcnSpecialColumn, QObject::tr( "Special" ) )
;
}

return gmBuiltinFunctions;
}

QMap<QString, QVariant> QgsExpression::gmSpecialColumns;

void QgsExpression::setSpecialColumn( const QString& name, QVariant variant )
{
int fnIdx = functionIndex( name );
if ( fnIdx != -1 )
{
// function of the same name already exists
return;
}
gmSpecialColumns[ name ] = variant;
}

void QgsExpression::unsetSpecialColumn( const QString& name )
{
QMap<QString, QVariant>::iterator fit = gmSpecialColumns.find( name );
if ( fit != gmSpecialColumns.end() )
{
gmSpecialColumns.erase( fit );
}
}

QVariant QgsExpression::specialColumn( const QString& name )
{
int fnIdx = functionIndex( name );
if ( fnIdx != -1 )
{
// function of the same name already exists
return QVariant();
}
QMap<QString, QVariant>::iterator it = gmSpecialColumns.find( name );
if ( it == gmSpecialColumns.end() )
{
return QVariant();
}
return it.value();
}

QList<QgsExpression::FunctionDef> QgsExpression::specialColumns()
{
QList<FunctionDef> defs;
for ( QMap<QString, QVariant>::const_iterator it = gmSpecialColumns.begin(); it != gmSpecialColumns.end(); ++it )
{
defs << FunctionDef( it.key(), 0, 0, QObject::tr( "Record" ));
}
return defs;
}

bool QgsExpression::isFunctionName( QString name )
{
Expand Down Expand Up @@ -1049,12 +1112,27 @@ void QgsExpression::acceptVisitor( QgsExpression::Visitor& v )
mRootNode->accept( v );
}

QString QgsExpression::replaceExpressionText( QString action, QgsFeature &feat,
QString QgsExpression::replaceExpressionText( QString action, QgsFeature* feat,
QgsVectorLayer* layer,
const QMap<QString, QVariant> *substitutionMap )
{
QString expr_action;

QMap<QString, QVariant> savedValues;
if ( substitutionMap )
{
// variables with a local scope (must be restored after evaluation)
for ( QMap<QString, QVariant>::const_iterator sit = substitutionMap->begin(); sit != substitutionMap->end(); ++sit )
{
QVariant oldValue = QgsExpression::specialColumn( sit.key() );
if ( !oldValue.isNull() )
savedValues.insert( sit.key(), oldValue );

// set the new value
QgsExpression::setSpecialColumn( sit.key(), sit.value() );
}
}

int index = 0;
while ( index < action.size() )
{
Expand All @@ -1070,12 +1148,6 @@ QString QgsExpression::replaceExpressionText( QString action, QgsFeature &feat,
QString to_replace = rx.cap( 1 ).trimmed();
QgsDebugMsg( "Found expression: " + to_replace );

if ( substitutionMap && substitutionMap->contains( to_replace ) )
{
expr_action += action.mid( start, pos - start ) + substitutionMap->value( to_replace ).toString();
continue;
}

QgsExpression exp( to_replace );
if ( exp.hasParserError() )
{
Expand All @@ -1084,7 +1156,15 @@ QString QgsExpression::replaceExpressionText( QString action, QgsFeature &feat,
continue;
}

QVariant result = exp.evaluate( &feat, layer->pendingFields() );
QVariant result;
if ( layer )
{
result = exp.evaluate( feat, layer->pendingFields() );
}
else
{
result = exp.evaluate( feat );
}
if ( exp.hasEvalError() )
{
QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() );
Expand All @@ -1097,10 +1177,24 @@ QString QgsExpression::replaceExpressionText( QString action, QgsFeature &feat,
}

expr_action += action.mid( index );

// restore overwritten local values
for ( QMap<QString, QVariant>::const_iterator sit = savedValues.begin(); sit != savedValues.end(); ++sit )
{
QgsExpression::setSpecialColumn( sit.key(), sit.value() );
}

return expr_action;
}


QString QgsExpression::replaceExpressionText( QString action, QgsFeature& feat,
QgsVectorLayer* layer,
const QMap<QString, QVariant> *substitutionMap )
{
return replaceExpressionText( action, &feat, layer, substitutionMap );
}

QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &element, QString &errorMessage )
{
if ( element.isNull() )
Expand Down Expand Up @@ -2048,7 +2142,11 @@ QgsExpression::Node* QgsExpression::NodeLiteral::createFromOgcFilter( QDomElemen

QVariant QgsExpression::NodeColumnRef::eval( QgsExpression* /*parent*/, QgsFeature* f )
{
return f->attributeMap()[mIndex];
if ( f )
{
return f->attributeMap()[mIndex];
}
return QVariant("[" + mName + "]");
}

bool QgsExpression::NodeColumnRef::prepare( QgsExpression* parent, const QgsFieldMap& fields )
Expand Down
20 changes: 19 additions & 1 deletion src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ class CORE_EXPORT QgsExpression
//! Return the number used for $rownum special column
int currentRowNumber() { return mRowNumber; }

//! Assign a special column
static void setSpecialColumn( const QString& name, QVariant value );
//! Unset a special column
static void unsetSpecialColumn( const QString& name );
//! Return the value of the given special column or a null QVariant if undefined
static QVariant specialColumn( const QString& name );

void setScale( double scale ) { mScale = scale; }

int scale() {return mScale; }
Expand All @@ -140,10 +147,14 @@ class CORE_EXPORT QgsExpression
Additional substitutions can be passed through the substitutionMap
parameter
*/
static QString replaceExpressionText( QString action, QgsFeature &feat,
static QString replaceExpressionText( QString action, QgsFeature* feat,
QgsVectorLayer* layer,
const QMap<QString, QVariant> *substitutionMap = 0 );


static QString replaceExpressionText( QString action, QgsFeature& feat,
QgsVectorLayer* layer,
const QMap<QString, QVariant> *substitutionMap = 0 );
//

enum UnaryOperator
Expand Down Expand Up @@ -226,6 +237,11 @@ class CORE_EXPORT QgsExpression
*/
static int functionCount();

/**
* Returns a list of special Column definitions
*/
static QList<FunctionDef> specialColumns();

//! return quoted column reference (in double quotes)
static QString quotedColumnRef( QString name ) { return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) ); }
//! return quoted string (in single quotes)
Expand Down Expand Up @@ -532,6 +548,8 @@ class CORE_EXPORT QgsExpression
int mRowNumber;
double mScale;

static QMap<QString, QVariant> gmSpecialColumns;

QgsDistanceArea* mCalc;
};

Expand Down
20 changes: 16 additions & 4 deletions src/core/qgsexpressionparser.yy
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,23 @@ expression:
int fnIndex = QgsExpression::functionIndex(*$1);
if (fnIndex == -1)
{
exp_error("Special column is not known");
YYERROR;
QVariant userVar = QgsExpression::specialColumn( *$1 );
if ( userVar.isNull() )
{
exp_error("Special column is not known");
YYERROR;
}
// $var is equivalent to _specialcol_( "$var" )
QgsExpression::NodeList* args = new QgsExpression::NodeList();
QgsExpression::NodeLiteral* literal = new QgsExpression::NodeLiteral( *$1 );
args->append( literal );
$$ = new QgsExpression::NodeFunction( QgsExpression::functionIndex( "_specialcol_" ), args );
}
$$ = new QgsExpression::NodeFunction( fnIndex, NULL );
delete $1;
else
{
$$ = new QgsExpression::NodeFunction( fnIndex, NULL );
delete $1;
}
}

// literals
Expand Down
14 changes: 7 additions & 7 deletions src/gui/qgscomposerview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,13 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent* e )
}
if ( composition() )
{
QgsComposerMap* composerMap = new QgsComposerMap( composition(), mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
composition()->addComposerMap( composerMap );
scene()->removeItem( mRubberBandItem );
delete mRubberBandItem;
mRubberBandItem = 0;
emit actionFinished();
composition()->pushAddRemoveCommand( composerMap, tr( "Map added" ) );
QgsComposerMap* composerMap = new QgsComposerMap( composition(), mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
composition()->addComposerMap( composerMap );
scene()->removeItem( mRubberBandItem );
delete mRubberBandItem;
mRubberBandItem = 0;
emit actionFinished();
composition()->pushAddRemoveCommand( composerMap, tr( "Map added" ) );
}
break;

Expand Down
20 changes: 18 additions & 2 deletions src/gui/qgsexpressionbuilderwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,20 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
{
QgsExpression::FunctionDef func = QgsExpression::BuiltinFunctions()[i];
QString name = func.mName;
if ( name.startsWith( "_" ) ) // do not display private functions
continue;
if ( func.mParams >= 1 )
name += "(";
registerItem( func.mGroup, func.mName, " " + name + " " );
}

QList<QgsExpression::FunctionDef> specials = QgsExpression::specialColumns();
for ( int i = 0; i < specials.size(); ++i )
{
QString name = specials[i].mName;
registerItem( specials[i].mGroup, name, " " + name + " " );
}

#if QT_VERSION >= 0x040700
txtSearchEdit->setPlaceholderText( tr( "Search" ) );
#endif
Expand Down Expand Up @@ -259,8 +268,6 @@ void QgsExpressionBuilderWidget::on_txtExpressionString_textChanged()

QgsExpression exp( text );

// TODO We could do this without a layer.
// Maybe just calling exp.evaluate()?
if ( mLayer )
{
if ( !mFeature.isValid() )
Expand All @@ -282,6 +289,15 @@ void QgsExpressionBuilderWidget::on_txtExpressionString_textChanged()
lblPreview->setText( "" );
}
}
else
{
// No layer defined
QVariant value = exp.evaluate();
if ( !exp.hasEvalError() )
{
lblPreview->setText( value.toString() );
}
}

if ( exp.hasParserError() || exp.hasEvalError() )
{
Expand Down
29 changes: 18 additions & 11 deletions src/ui/qgscomposerlabelwidgetbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>274</width>
<height>488</height>
<width>307</width>
<height>525</height>
</rect>
</property>
<property name="sizePolicy">
Expand All @@ -30,8 +30,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>271</width>
<height>470</height>
<width>276</width>
<height>503</height>
</rect>
</property>
<attribute name="label">
Expand All @@ -45,14 +45,14 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<widget class="QPushButton" name="mFontColorButton">
<property name="text">
<string>Font color...</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="buttonGroup1">
<property name="title">
<string>Horizontal Alignment:</string>
Expand Down Expand Up @@ -98,7 +98,7 @@
</layout>
</widget>
</item>
<item row="4" column="0" colspan="2">
<item row="5" column="0" colspan="2">
<widget class="QGroupBox" name="buttonGroup2">
<property name="title">
<string>Vertical Alignment:</string>
Expand Down Expand Up @@ -141,7 +141,7 @@
</layout>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="QDoubleSpinBox" name="mMarginDoubleSpinBox">
<property name="prefix">
<string>Margin </string>
Expand All @@ -151,7 +151,7 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="mRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
Expand All @@ -170,14 +170,14 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="mRotationSpinBox">
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="mFontButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
Expand All @@ -190,6 +190,13 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="mInsertExpressionButton">
<property name="text">
<string>Insert an expression</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
Expand Down
10 changes: 5 additions & 5 deletions src/ui/qgscomposerlegendwidgetbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-109</y>
<width>370</width>
<height>523</height>
<y>0</y>
<width>374</width>
<height>549</height>
</rect>
</property>
<attribute name="label">
Expand Down Expand Up @@ -220,8 +220,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>367</width>
<height>170</height>
<width>393</width>
<height>162</height>
</rect>
</property>
<attribute name="label">
Expand Down
274 changes: 212 additions & 62 deletions src/ui/qgscomposermapwidgetbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>265</width>
<height>483</height>
<width>450</width>
<height>501</height>
</rect>
</property>
<property name="sizePolicy">
Expand All @@ -25,23 +25,55 @@
</property>
<item row="0" column="0">
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<property name="lineWidth">
<number>0</number>
</property>
<property name="currentIndex">
<number>3</number>
</property>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>255</width>
<height>392</height>
<width>437</width>
<height>408</height>
</rect>
</property>
<attribute name="label">
<string>Map</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="7" column="0" colspan="2">
<item row="7" column="1">
<widget class="QComboBox" name="mOverviewFrameMapComboBox"/>
</item>
<item row="4" column="0">
<widget class="QComboBox" name="mPreviewModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="9" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
Expand All @@ -57,24 +89,35 @@
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QComboBox" name="mPreviewModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="4" column="1">
<widget class="QPushButton" name="mUpdatePreviewButton">
<property name="text">
<string>Update preview</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="mUpdatePreviewButton">
<item row="8" column="0">
<widget class="QLabel" name="mOverviewFrameStyleLabel">
<property name="text">
<string>Update preview</string>
<string>Overview style</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<item row="8" column="1">
<widget class="QPushButton" name="mOverviewFrameStyleButton">
<property name="text">
<string>Change...</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="mOverviewFrameMapLabel">
<property name="text">
<string>Overview frame</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
Expand Down Expand Up @@ -188,23 +231,7 @@
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<item row="5" column="0" colspan="2">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
Expand All @@ -220,39 +247,15 @@
</property>
</spacer>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="mOverviewFrameMapComboBox"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="mOverviewFrameMapLabel">
<property name="text">
<string>Overview frame</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="mOverviewFrameStyleLabel">
<property name="text">
<string>Overview style</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="mOverviewFrameStyleButton">
<property name="text">
<string>Change...</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>255</width>
<height>392</height>
<width>450</width>
<height>377</height>
</rect>
</property>
<attribute name="label">
Expand Down Expand Up @@ -359,8 +362,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>238</width>
<height>770</height>
<width>437</width>
<height>893</height>
</rect>
</property>
<attribute name="label">
Expand Down Expand Up @@ -787,6 +790,153 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>377</height>
</rect>
</property>
<attribute name="label">
<string>Atlas</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_9">
<item row="4" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>76</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="mIsAtlasCheckBox">
<property name="text">
<string>Make it the atlas map</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QFrame" name="mAtlasFrame">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_7" rowstretch="0,0,0,0,0,0,0,0,0" columnstretch="0,0,0">
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="mAtlasHideCoverageCheckBox">
<property name="toolTip">
<string>Hide the coverage layer when generating the output</string>
</property>
<property name="text">
<string>Hidden coverage layer</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Margin around coverage</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Output filename expression</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="mAtlasMarginSpinBox">
<property name="suffix">
<string> %</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QToolButton" name="mAtlasFilenameExpressionButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="mAtlasFilenamePatternEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Coverage layer</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="mAtlasCoverageLayerComboBox">
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="mAtlasFixedScaleCheckBox">
<property name="text">
<string>Fixed scale</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="mAtlasSingleFileCheckBox">
<property name="text">
<string>Single file export when possible</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
Expand Down
2 changes: 2 additions & 0 deletions tests/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ ADD_QGIS_TEST(rulebasedrenderertest testqgsrulebasedrenderer.cpp)
ADD_QGIS_TEST(ziplayertest testziplayer.cpp)
ADD_QGIS_TEST(dataitemtest testqgsdataitem.cpp)
ADD_QGIS_TEST(composermaptest testqgscomposermap.cpp)
ADD_QGIS_TEST(composermapatlastest testqgscomposermapatlas.cpp)
ADD_QGIS_TEST(composerlabeltest testqgscomposerlabel.cpp)
ADD_QGIS_TEST(stylev2test testqgsstylev2.cpp)
#ADD_QGIS_TEST(composerhtmltest testqgscomposerhtml.cpp )
ADD_QGIS_TEST(rectangletest testqgsrectangle.cpp)
Expand Down
181 changes: 181 additions & 0 deletions tests/src/core/testqgscomposerlabel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/***************************************************************************
testqgscomposerlabel.cpp
----------------------
begin : Sept 2012
copyright : (C) 2012 by Hugo Mercier
email : hugo dot mercier at oslandia dot com
***************************************************************************/

/***************************************************************************
* *
* 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 *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsapplication.h"
#include "qgscomposition.h"
#include "qgscomposerlabel.h"
#include "qgsmaplayerregistry.h"
#include "qgsmaprenderer.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include <QObject>
#include <QtTest>

class TestQgsComposerLabel: public QObject
{
Q_OBJECT;
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.

// test simple expression evaluation
void evaluation();
// test expression evaluation when a feature is set
void feature_evaluation();
// test "$page" expressions
void page_evaluation();
private:
QgsComposition* mComposition;
QgsComposerLabel* mComposerLabel;
QgsMapRenderer* mMapRenderer;
QgsVectorLayer* mVectorLayer;
};

void TestQgsComposerLabel::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();

//create maplayers from testdata and add to layer registry
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + QDir::separator() + "france_parts.shp" );
mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsMapLayerRegistry::instance()->addMapLayers( QList<QgsMapLayer*>() << mVectorLayer );

//create composition with composer map
mMapRenderer = new QgsMapRenderer();
mMapRenderer->setLayerSet( QStringList() << mVectorLayer->id() );
mMapRenderer->setProjectionsEnabled( false );
mComposition = new QgsComposition( mMapRenderer );
mComposition->setPaperSize( 297, 210 ); //A4 landscape

mComposerLabel = new QgsComposerLabel( mComposition );
mComposition->addComposerLabel( mComposerLabel );
}

void TestQgsComposerLabel::cleanupTestCase()
{
delete mComposition;
delete mMapRenderer;
delete mVectorLayer;
}

void TestQgsComposerLabel::init()
{

}

void TestQgsComposerLabel::cleanup()
{

}

void TestQgsComposerLabel::evaluation()
{
{
// $CURRENT_DATE evaluation
QString expected = "__" + QDate::currentDate().toString() + "__";
mComposerLabel->setText( "__$CURRENT_DATE__" );
QString evaluated = mComposerLabel->displayText();
QCOMPARE( evaluated, expected );
}
{
// $CURRENT_DATE() evaluation
QDateTime now = QDateTime::currentDateTime();
QString expected = "__" + now.toString( "dd" ) + "(ok)__";
mComposerLabel->setText( "__$CURRENT_DATE(dd)(ok)__" );
QString evaluated = mComposerLabel->displayText();
QCOMPARE( evaluated, expected );
}
{
// $CURRENT_DATE() evaluation (inside an expression)
QDate now = QDate::currentDate();
int dd = now.day();

QString expected = "__" + QString("%1").arg(dd+1, 2, 10, QChar('0')) + "(ok)__";
mComposerLabel->setText( "__[%$CURRENT_DATE(dd) + 1%](ok)__" );
QString evaluated = mComposerLabel->displayText();
QCOMPARE( evaluated, expected );
}
{
// expression evaluation (without feature)
QString expected = "__[NAME_1]42__";
mComposerLabel->setText( "__[%\"NAME_1\"%][%21*2%]__" );
QString evaluated = mComposerLabel->displayText();
QCOMPARE( evaluated, expected );
}
}

void TestQgsComposerLabel::feature_evaluation()
{
QgsVectorDataProvider* provider = mVectorLayer->dataProvider();

QgsAttributeList allAttrs = provider->attributeIndexes();
provider->select( allAttrs );
QgsFeature feat;

provider->nextFeature( feat );
{
// evaluation with a feature
mComposerLabel->setExpressionContext( &feat, mVectorLayer );
mComposerLabel->setText( "[%\"NAME_1\"||'_ok'%]" );
QString evaluated = mComposerLabel->displayText();
QString expected = "Basse-Normandie_ok";
QCOMPARE( evaluated, expected );
}
provider->nextFeature( feat );
{
// evaluation with a feature
mComposerLabel->setExpressionContext( &feat, mVectorLayer );
mComposerLabel->setText( "[%\"NAME_1\"||'_ok'%]" );
QString evaluated = mComposerLabel->displayText();
QString expected = "Bretagne_ok";
QCOMPARE( evaluated, expected );
}
{
// evaluation with a feature and local variables
QMap<QString, QVariant> locals;
locals.insert( "$test", "OK" );

mComposerLabel->setExpressionContext( &feat, mVectorLayer, locals );
mComposerLabel->setText( "[%\"NAME_1\"||$test%]" );
QString evaluated = mComposerLabel->displayText();
QString expected = "BretagneOK";
QCOMPARE( evaluated, expected );
}
}

void TestQgsComposerLabel::page_evaluation()
{
mComposition->setNumPages( 2 );
{
mComposerLabel->setText( "[%$page||'/'||$numpages%]" );
QString evaluated = mComposerLabel->displayText();
QString expected = "1/2";
QCOMPARE( evaluated, expected );

// move to the second page and re-evaluate
mComposerLabel->setItemPosition( 0, 320 );
QCOMPARE( mComposerLabel->displayText(), QString("2/2") );
}
}

QTEST_MAIN( TestQgsComposerLabel )
#include "moc_testqgscomposerlabel.cxx"
224 changes: 224 additions & 0 deletions tests/src/core/testqgscomposermapatlas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
/***************************************************************************
testqgscomposermapatlas.cpp
---------------------------
begin : Sept 2012
copyright : (C) 2012 by Hugo Mercier
email : hugo dot mercier at oslandia dot com
***************************************************************************/

/***************************************************************************
* *
* 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 *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsapplication.h"
#include "qgscomposition.h"
#include "qgscompositionchecker.h"
#include "qgscomposermap.h"
#include "qgscomposerlabel.h"
#include "qgsmaplayerregistry.h"
#include "qgsmaprenderer.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgssymbolv2.h"
#include "qgssinglesymbolrendererv2.h"
#include <QObject>
#include <QtTest>

class TestQgsComposerMapAtlas: public QObject
{
Q_OBJECT;
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.

// test filename pattern evaluation
void filename();
// test rendering with an autoscale atlas
void autoscale_render();
// test rendering with a fixed scale atlas
void fixedscale_render();
// test rendering with a hidden coverage
void hiding_render();
private:
QgsComposition* mComposition;
QgsComposerLabel* mLabel1;
QgsComposerLabel* mLabel2;
QgsComposerMap* mAtlasMap;
QgsComposerMap* mOverview;
QgsMapRenderer* mMapRenderer;
QgsVectorLayer* mVectorLayer;
};

void TestQgsComposerMapAtlas::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();

//create maplayers from testdata and add to layer registry
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + QDir::separator() + "france_parts.shp" );
mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );

QgsMapLayerRegistry::instance()->addMapLayers( QList<QgsMapLayer*>() << mVectorLayer );

//create composition with composer map
mMapRenderer = new QgsMapRenderer();
mMapRenderer->setLayerSet( QStringList() << mVectorLayer->id() );
mMapRenderer->setProjectionsEnabled( true );

// select epsg:2154
QgsCoordinateReferenceSystem crs;
crs.createFromSrid( 2154 );
mMapRenderer->setDestinationCrs( crs );
mComposition = new QgsComposition( mMapRenderer );
mComposition->setPaperSize( 297, 210 ); //A4 landscape

// fix the renderer, fill with green
QgsStringMap props;
props.insert( "color", "0,127,0" );
QgsFillSymbolV2* fillSymbol = QgsFillSymbolV2::createSimple( props );
QgsSingleSymbolRendererV2* renderer = new QgsSingleSymbolRendererV2( fillSymbol );
mVectorLayer->setRendererV2( renderer );

// the atlas map
mAtlasMap = new QgsComposerMap( mComposition, 20, 20, 130, 130 );
mAtlasMap->setFrameEnabled( true );
mAtlasMap->setAtlasCoverageLayer( mVectorLayer );
mComposition->addComposerMap( mAtlasMap );
mComposition->setAtlasMap( mAtlasMap );

// an overview
mOverview = new QgsComposerMap( mComposition, 180, 20, 50, 50 );
mOverview->setFrameEnabled( true );
mOverview->setOverviewFrameMap( mAtlasMap->id() );
mComposition->addComposerMap( mOverview );
mOverview->setNewExtent( QgsRectangle( 49670.718, 6415139.086, 699672.519, 7065140.887 ) );

// header label
mLabel1 = new QgsComposerLabel( mComposition );
mComposition->addComposerLabel( mLabel1 );
mLabel1->setText( "[% \"NAME_1\" %] area" );
mLabel1->adjustSizeToText();
mLabel1->setItemPosition( 150, 5 );

// feature number label
mLabel2 = new QgsComposerLabel( mComposition );
mComposition->addComposerLabel( mLabel2 );
mLabel2->setText( "# [%$feature || ' / ' || $numfeatures%]" );
mLabel2->adjustSizeToText();
mLabel2->setItemPosition( 150, 200 );
}

void TestQgsComposerMapAtlas::cleanupTestCase()
{
delete mComposition;
delete mMapRenderer;
delete mVectorLayer;
}

void TestQgsComposerMapAtlas::init()
{

}

void TestQgsComposerMapAtlas::cleanup()
{

}

void TestQgsComposerMapAtlas::filename()
{
QgsAtlasRendering atlasRender( mComposition );
atlasRender.begin( "'output_' || $feature" );
for ( size_t fi = 0; fi < atlasRender.numFeatures(); ++fi )
{
atlasRender.prepareForFeature( fi );
QString expected = QString( "output_%1" ).arg( (int)(fi+1) );
QCOMPARE( atlasRender.currentFilename(), expected );
}
atlasRender.end();
}


void TestQgsComposerMapAtlas::autoscale_render()
{
mAtlasMap->setAtlasFixedScale( false );
mAtlasMap->setAtlasMargin( 0.10 );

QgsAtlasRendering atlasRender( mComposition );

atlasRender.begin();

for ( size_t fit = 0; fit < 2; ++fit )
{
atlasRender.prepareForFeature( fit );
mLabel1->adjustSizeToText();

QgsCompositionChecker checker( "Atlas autoscale test", mComposition,
QString( TEST_DATA_DIR ) + QDir::separator() + "control_images" + QDir::separator() +
"expected_composermapatlas" + QDir::separator() +
QString( "autoscale_%1.png" ).arg((int)fit) );
QVERIFY( checker.testComposition( 0 ) );
}
atlasRender.end();
}

void TestQgsComposerMapAtlas::fixedscale_render()
{
mAtlasMap->setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) );
mAtlasMap->setAtlasFixedScale( true );

QgsAtlasRendering atlasRender( mComposition );

atlasRender.begin();

for ( size_t fit = 0; fit < 2; ++fit )
{
atlasRender.prepareForFeature( fit );
mLabel1->adjustSizeToText();

QgsCompositionChecker checker( "Atlas fixedscale test", mComposition,
QString( TEST_DATA_DIR ) + QDir::separator() + "control_images" + QDir::separator() +
"expected_composermapatlas" + QDir::separator() +
QString( "fixedscale_%1.png" ).arg((int)fit) );
QVERIFY( checker.testComposition( 0 ) );
}
atlasRender.end();

}

void TestQgsComposerMapAtlas::hiding_render()
{
mAtlasMap->setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) );
mAtlasMap->setAtlasFixedScale( true );
mAtlasMap->setAtlasHideCoverage( true );

QgsAtlasRendering atlasRender( mComposition );

atlasRender.begin();

for ( size_t fit = 0; fit < 2; ++fit )
{
atlasRender.prepareForFeature( fit );
mLabel1->adjustSizeToText();

QgsCompositionChecker checker( "Atlas hidden test", mComposition,
QString( TEST_DATA_DIR ) + QDir::separator() + "control_images" + QDir::separator() +
"expected_composermapatlas" + QDir::separator() +
QString( "hiding_%1.png" ).arg((int)fit) );
QVERIFY( checker.testComposition( 0 ) );
}
atlasRender.end();

}

QTEST_MAIN( TestQgsComposerMapAtlas )
#include "moc_testqgscomposermapatlas.cxx"
27 changes: 27 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,33 @@ class TestQgsExpression: public QObject
QVariant vPerimeter = exp3.evaluate( &fPolygon );
QCOMPARE( vPerimeter.toDouble(), 20. );
}

void eval_special_columns()
{
QTest::addColumn<QString>( "string" );
QTest::addColumn<QVariant>( "result" );

QgsExpression::setSpecialColumn( "$var1", QVariant((int)42) );

QgsExpression exp( "$var1 + 1" );
QVariant v1 = exp.evaluate();
QCOMPARE( v1.toInt(), 43 );

QgsExpression::setSpecialColumn( "$var1", QVariant((int)100) );
QVariant v2 = exp.evaluate();
QCOMPARE( v2.toInt(), 101 );

QgsExpression exp2( "_specialcol_('$var1')+1" );
QVariant v3 = exp2.evaluate();
QCOMPARE( v3.toInt(), 101 );

QgsExpression exp3( "_specialcol_('undefined')");
QVariant v4 = exp3.evaluate();
QCOMPARE( v4, QVariant() );

QgsExpression::unsetSpecialColumn( "$var1" );
}

};

QTEST_MAIN( TestQgsExpression )
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/testdata/france_parts.dbf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/testdata/france_parts.prj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
1 change: 1 addition & 0 deletions tests/testdata/france_parts.qpj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
Binary file added tests/testdata/france_parts.shp
Binary file not shown.
Binary file added tests/testdata/france_parts.shx
Binary file not shown.