574 changes: 358 additions & 216 deletions src/core/symbology-ng/qgsrulebasedrendererv2.cpp

Large diffs are not rendered by default.

121 changes: 90 additions & 31 deletions src/core/symbology-ng/qgsrulebasedrendererv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,34 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
{
public:


// TODO: use QVarLengthArray instead of QList

// rendering job: a feature to be rendered with a particular symbol
// (both f, symbol are _not_ owned by this class)
struct RenderJob
{
RenderJob( QgsFeature* _f, QgsSymbolV2* _s ) : f( _f ), symbol( _s ) {}
QgsFeature* f;
QgsSymbolV2* symbol;
};

// render level: a list of jobs to be drawn at particular level
// (jobs are owned by this class)
struct RenderLevel
{
RenderLevel( int z ): zIndex( z ) {}
~RenderLevel() { foreach( RenderJob* j, jobs ) delete j; }
int zIndex;
QList<RenderJob*> jobs;
};

// rendering queue: a list of rendering levels
typedef QList<RenderLevel> RenderQueue;

class Rule;
typedef QList<Rule*> RuleList;

/**
This class keeps data about a rules for rule-based renderer.
A rule consists of a symbol, filter expression and range of scales.
Expand All @@ -47,49 +75,96 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
//! Constructor takes ownership of the symbol
Rule( QgsSymbolV2* symbol, int scaleMinDenom = 0, int scaleMaxDenom = 0, QString filterExp = QString(),
QString label = QString(), QString description = QString() );
Rule( const Rule& other );
//Rule( const Rule& other );
~Rule();
QString dump() const;
QStringList needsFields() const;
QString dump( int offset = 0 ) const;
QSet<QString> usedAttributes();
QgsSymbolV2List symbols();
QgsLegendSymbolList legendSymbolItems();
bool isFilterOK( QgsFeature& f ) const;
bool isScaleOK( double scale ) const;

QgsSymbolV2* symbol() { return mSymbol; }
QString label() const { return mLabel; }
bool dependsOnScale() const { return mScaleMinDenom != 0 || mScaleMaxDenom != 0; }
int scaleMinDenom() const { return mScaleMinDenom; }
int scaleMaxDenom() const { return mScaleMaxDenom; }
QgsExpression* filter() const { return mFilter; }
QString filterExpression() const { return mFilterExp; }
QString label() const { return mLabel; }
QString description() const { return mDescription; }

//! set a new symbol (or NULL). Deletes old symbol.
void setSymbol( QgsSymbolV2* sym );
void setLabel( QString label ) { mLabel = label; }
void setScaleMinDenom( int scaleMinDenom ) { mScaleMinDenom = scaleMinDenom; }
void setScaleMaxDenom( int scaleMaxDenom ) { mScaleMaxDenom = scaleMaxDenom; }
void setFilterExpression( QString filterExp ) { mFilterExp = filterExp; initFilter(); }
void setLabel( QString label ) { mLabel = label; }
void setDescription( QString description ) { mDescription = description; }

Rule& operator=( const Rule& other );
//Rule& operator=( const Rule& other );
//! clone this rule, return new instance
Rule* clone() const;

QDomElement save( QDomDocument& doc, QgsSymbolV2Map& symbolMap );

//! prepare the rule for rendering and its children (build active children array)
bool startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer );
//! get all used z-levels from this rule and children
QSet<int> collectZLevels();
//! assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering
void setNormZLevels( const QMap<int, int>& zLevelsToNormLevels );

void renderFeature( QgsFeature* featPtr, QgsRenderContext& context, RenderQueue& renderQueue );

void stopRender( QgsRenderContext& context );

static Rule* create( QDomElement& ruleElem, QgsSymbolV2Map& symbolMap );

RuleList& children() { return mChildren; }
Rule* parent() { return mParent; }

//! add child rule, take ownership, sets this as parent
void appendChild( Rule* rule ) { mChildren.append( rule ); rule->mParent = this; }
//! add child rule, take ownership, sets this as parent
void insertChild( int i, Rule* rule ) { mChildren.insert( i, rule ); rule->mParent = this; }
//! delete child rule
void removeChild( Rule* rule ) { mChildren.removeAll( rule ); delete rule; }
//! delete child rule
void removeChildAt( int i ) { Rule* rule = mChildren[i]; mChildren.removeAt( i ); delete rule; }
//! take child rule out, set parent as null
void takeChild( Rule* rule ) { mChildren.removeAll( rule ); rule->mParent = NULL; }
//! take child rule out, set parent as null
Rule* takeChildAt( int i ) { Rule* rule = mChildren.takeAt( i ); rule->mParent = NULL; return rule; }

protected:

void initFilter();

Rule* mParent; // parent rule (NULL only for root rule)
QgsSymbolV2* mSymbol;
int mScaleMinDenom, mScaleMaxDenom;
QString mFilterExp, mLabel, mDescription;
bool mElseRule;
RuleList mChildren;

// temporary
QgsExpression* mFilter;
// temporary while rendering
QList<int> mSymbolNormZLevels;
RuleList mActiveChildren;
};

/////

static QgsFeatureRendererV2* create( QDomElement& element );

//! Constructor. Takes ownership of the default symbol.
//! Constructs the renderer from given tree of rules (takes ownership)
QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root );
//! Constructor for convenience. Creates a root rule and adds a default rule with symbol (takes ownership)
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol );

~QgsRuleBasedRendererV2();

//! return symbol for current feature. Should not be used individually: there could be more symbols for a feature
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );

Expand Down Expand Up @@ -120,40 +195,24 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2

/////

//! return the total number of rules
int ruleCount();
//! get reference to rule at index (valid indexes: 0...count-1)
Rule& ruleAt( int index );
//! add rule to the end of the list of rules
void addRule( const Rule& rule );
//! insert rule to a specific position of the list of rules
void insertRule( int index, const Rule& rule );
//! modify the rule at a specific position of the list of rules
void updateRuleAt( int index, const Rule& rule );
//! remove the rule at the specified index
void removeRuleAt( int index );
//! swap the two rules specified by the indices
void swapRules( int index1, int index2 );
Rule* rootRule() { return mRootRule; }

//////

//! take a rule and create a list of new rules based on the categories from categorized symbol renderer
static QList<Rule> refineRuleCategories( Rule& initialRule, QgsCategorizedSymbolRendererV2* r );
static void refineRuleCategories( Rule* initialRule, QgsCategorizedSymbolRendererV2* r );
//! take a rule and create a list of new rules based on the ranges from graduated symbol renderer
static QList<Rule> refineRuleRanges( Rule& initialRule, QgsGraduatedSymbolRendererV2* r );
static void refineRuleRanges( Rule* initialRule, QgsGraduatedSymbolRendererV2* r );
//! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators
static QList<Rule> refineRuleScales( Rule& initialRule, QList<int> scales );
static void refineRuleScales( Rule* initialRule, QList<int> scales );

protected:
//! the list of rules
QList<Rule> mRules;
//! the default symbol, used for the first rule with no filter
QgsSymbolV2* mDefaultSymbol;
//! the root node with hierarchical list of rules
Rule* mRootRule;

// temporary
QList<Rule*> mCurrentRules;
QgsSymbolV2* mCurrentSymbol;

RenderQueue mRenderQueue;
QList<QgsFeature*> mCurrentFeatures;
};

#endif // QGSRULEBASEDRENDERERV2_H
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgssinglesymbolrendererv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2

virtual QgsFeatureRendererV2* clone();

//! returns bitwise OR-ed capabilities of the renderer
//! \note added in 2.0
virtual int capabilities() { return SymbolLevels; }

virtual QgsSymbolV2List symbols();

//! create renderer from XML element
Expand Down
19 changes: 18 additions & 1 deletion src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,19 @@ ${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilder.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilderdialogbase.h
)

# ModelTest
#SET(MODELTEST_SRCS
# ../../tests/qt_modeltest/modeltest.cpp
# ../../tests/qt_modeltest/dynamictreemodel.cpp
#)
#SET(MODELTEST_MOC_HDRS
# ../../tests/qt_modeltest/modeltest.h
# ../../tests/qt_modeltest/dynamictreemodel.h
#)
#QT4_WRAP_CPP(MODELTEST_MOC_SRCS ${MODELTEST_MOC_HDRS})
#INCLUDE_DIRECTORIES(../../tests/qt_modeltest)


INCLUDE_DIRECTORIES(
${QT_QTUITOOLS_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
Expand Down Expand Up @@ -245,7 +258,10 @@ ENDIF(MSVC)
#############################################################
# qgis_gui library

ADD_LIBRARY(qgis_gui SHARED ${QGIS_GUI_SRCS} ${QGIS_GUI_MOC_SRCS} ${QGIS_GUI_HDRS} ${QGIS_GUI_MOC_HDRS})
ADD_LIBRARY(qgis_gui SHARED
${QGIS_GUI_SRCS} ${QGIS_GUI_MOC_SRCS} ${QGIS_GUI_HDRS} ${QGIS_GUI_MOC_HDRS}
#${MODELTEST_SRCS} ${MODELTEST_MOC_SRCS} # for ModelTest
)

SET_TARGET_PROPERTIES(qgis_gui PROPERTIES
PUBLIC_HEADER "${QGIS_GUI_HDRS};${QGIS_GUI_MOC_HDRS}"
Expand All @@ -270,6 +286,7 @@ ADD_DEPENDENCIES(qgis_gui ui)
TARGET_LINK_LIBRARIES(qgis_gui
qgis_core
${QT_QTUITOOLS_LIBRARY}
#${QT_QTTEST_LIBRARY} # for ModelTest
)

IF (NOT WITH_INTERNAL_SPATIALITE)
Expand Down
8 changes: 8 additions & 0 deletions src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV

// menus for data-defined rotation/size
QMenu* advMenu = new QMenu;

advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );

mDataDefinedMenus = new QgsRendererV2DataDefinedMenus( advMenu, mLayer->pendingFields(),
mRenderer->rotationField(), mRenderer->sizeScaleField() );
connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
Expand Down Expand Up @@ -449,3 +452,8 @@ QList<QgsSymbolV2*> QgsCategorizedSymbolRendererV2Widget::selectedSymbols()
}
return selectedSymbols;
}

void QgsCategorizedSymbolRendererV2Widget::showSymbolLevels()
{
showSymbolLevelsDialog( mRenderer );
}
2 changes: 2 additions & 0 deletions src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg
void rotationFieldChanged( QString fldName );
void sizeScaleFieldChanged( QString fldName );

void showSymbolLevels();

protected:

void updateUiFromRenderer();
Expand Down
8 changes: 8 additions & 0 deletions src/gui/symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ QgsGraduatedSymbolRendererV2Widget::QgsGraduatedSymbolRendererV2Widget( QgsVecto

// menus for data-defined rotation/size
QMenu* advMenu = new QMenu;

advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );

mDataDefinedMenus = new QgsRendererV2DataDefinedMenus( advMenu, mLayer->pendingFields(),
mRenderer->rotationField(), mRenderer->sizeScaleField() );
connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
Expand Down Expand Up @@ -391,3 +394,8 @@ void QgsGraduatedSymbolRendererV2Widget::refreshSymbolView()
{
populateRanges();
}

void QgsGraduatedSymbolRendererV2Widget::showSymbolLevels()
{
showSymbolLevelsDialog( mRenderer );
}
2 changes: 2 additions & 0 deletions src/gui/symbology-ng/qgsgraduatedsymbolrendererv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class GUI_EXPORT QgsGraduatedSymbolRendererV2Widget : public QgsRendererV2Widget
void rotationFieldChanged( QString fldName );
void sizeScaleFieldChanged( QString fldName );

void showSymbolLevels();

protected:
void updateUiFromRenderer();

Expand Down
40 changes: 0 additions & 40 deletions src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
#include "qgsrulebasedrendererv2widget.h"
#include "qgspointdisplacementrendererwidget.h"

#include "qgssymbollevelsv2dialog.h"

#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
Expand Down Expand Up @@ -71,7 +69,6 @@ QgsRendererV2PropertiesDialog::QgsRendererV2PropertiesDialog( QgsVectorLayer* la
}

connect( buttonBox, SIGNAL( accepted() ), this, SLOT( onOK() ) );
connect( btnSymbolLevels, SIGNAL( clicked() ), this, SLOT( showSymbolLevels() ) );
connect( btnOldSymbology, SIGNAL( clicked() ), this, SLOT( useOldSymbology() ) );

// initialize registry's widget functions
Expand Down Expand Up @@ -146,15 +143,11 @@ void QgsRendererV2PropertiesDialog::rendererChanged()
mActiveWidget = w;
stackedWidget->addWidget( mActiveWidget );
stackedWidget->setCurrentWidget( mActiveWidget );

btnSymbolLevels->setEnabled( true );
}
else
{
// set default "no edit widget available" page
stackedWidget->setCurrentWidget( pageNoWidget );

btnSymbolLevels->setEnabled( false );
}

}
Expand Down Expand Up @@ -194,39 +187,6 @@ void QgsRendererV2PropertiesDialog::keyPressEvent( QKeyEvent * e )
}


void QgsRendererV2PropertiesDialog::showSymbolLevels()
{
if ( !mActiveWidget )
return;

QgsFeatureRendererV2* r = mActiveWidget->renderer();
QgsSymbolV2List symbols = r->symbols();

QgsSymbolLevelsV2Dialog dlg( symbols, r->usingSymbolLevels(), this );
connect( this, SIGNAL( forceChkUsingFirstRule() ), mActiveWidget, SLOT( forceUsingFirstRule() ) );
connect( this, SIGNAL( forceUncheckSymbolLevels() ), mActiveWidget, SLOT( forceNoSymbolLevels() ) );

if ( dlg.exec() )
{
r->setUsingSymbolLevels( dlg.usingLevels() );

if ( r->type() == "RuleRenderer" )
{
if ( dlg.usingLevels() )
{
r->setUsingFirstRule( true );
emit forceChkUsingFirstRule();
}
else
{
emit forceUncheckSymbolLevels();
}
}
}

disconnect( this, SIGNAL( forceChkUsingFirstRule() ), mActiveWidget, SLOT( forceUsingFirstRule() ) );
disconnect( this, SIGNAL( forceUncheckSymbolLevels() ), mActiveWidget, SLOT( forceNoSymbolLevels() ) );
}


void QgsRendererV2PropertiesDialog::useOldSymbology()
Expand Down
4 changes: 0 additions & 4 deletions src/gui/symbology-ng/qgsrendererv2propertiesdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,10 @@ class GUI_EXPORT QgsRendererV2PropertiesDialog : public QDialog, private Ui::Qgs
void apply();
void onOK();

void showSymbolLevels();

void useOldSymbology();

signals:
void useNewSymbology( bool );
void forceChkUsingFirstRule();
void forceUncheckSymbolLevels();

protected:

Expand Down
13 changes: 13 additions & 0 deletions src/gui/symbology-ng/qgsrendererv2widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <QInputDialog>
#include <QMenu>

#include "qgssymbollevelsv2dialog.h"


QgsRendererV2Widget::QgsRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style )
: QWidget(), mLayer( layer ), mStyle( style )
Expand Down Expand Up @@ -137,6 +139,17 @@ void QgsRendererV2Widget::changeSymbolSize()
}
}

void QgsRendererV2Widget::showSymbolLevelsDialog( QgsFeatureRendererV2* r )
{
QgsLegendSymbolList symbols = r->legendSymbolItems();

QgsSymbolLevelsV2Dialog dlg( symbols, r->usingSymbolLevels(), this );

if ( dlg.exec() )
{
r->setUsingSymbolLevels( dlg.usingLevels() );
}
}


////////////
Expand Down
3 changes: 3 additions & 0 deletions src/gui/symbology-ng/qgsrendererv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class GUI_EXPORT QgsRendererV2Widget : public QWidget
//! return pointer to the renderer (no transfer of ownership)
virtual QgsFeatureRendererV2* renderer() = 0;

//! show a dialog with renderer's symbol level settings
void showSymbolLevelsDialog( QgsFeatureRendererV2* r );

protected:
QgsVectorLayer* mLayer;
QgsStyleV2* mStyle;
Expand Down
800 changes: 404 additions & 396 deletions src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp

Large diffs are not rendered by default.

92 changes: 54 additions & 38 deletions src/gui/symbology-ng/qgsrulebasedrendererv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,58 @@ class QMenu;

///////

#include <QTreeWidget>
#include <QAbstractItemModel>

class GUI_EXPORT QgsRendererRulesTreeWidget : public QTreeWidget
{
Q_OBJECT
/*
Tree model for the rules:
(invalid) == root node
+--- top level rule
+--- top level rule
*/
class QgsRuleBasedRendererV2Model : public QAbstractItemModel
{
public:
QgsRendererRulesTreeWidget( QWidget* parent = 0 );
QgsRuleBasedRendererV2Model( QgsRuleBasedRendererV2* r );

void setRenderer( QgsRuleBasedRendererV2* r );
virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;
virtual QVariant headerData( int section, Qt::Orientation orientation,
int role = Qt::DisplayRole ) const;
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;
virtual int columnCount( const QModelIndex & = QModelIndex() ) const;
//! provide model index for parent's child item
virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const;
//! provide parent model index
virtual QModelIndex parent( const QModelIndex &index ) const;

enum Grouping { NoGrouping, GroupingByScale, GroupingByFilter };
// editing support
virtual bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );

void setGrouping( Grouping g );
// drag'n'drop support
Qt::DropActions supportedDropActions() const;
QStringList mimeTypes() const;
QMimeData *mimeData( const QModelIndexList &indexes ) const;
bool dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent );

void populateRules();
bool removeRows( int row, int count, const QModelIndex & parent = QModelIndex() );

protected:
void populateRulesNoGrouping();
void populateRulesGroupByScale();
void populateRulesGroupByFilter();
// new methods

QString formatScaleRange( int minDenom, int maxDenom );
QgsRuleBasedRendererV2::Rule* ruleForIndex( const QModelIndex& index ) const;

QString formatScale( int denom, int size = 0 );
void insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule );
void updateRule( const QModelIndex& parent, int row );
void removeRule( const QModelIndex& index );

QgsRuleBasedRendererV2* mR;
Grouping mGrouping;
void willAddRules( const QModelIndex& parent, int count ); // call beginInsertRows
void finishedAddingRules(); // call endInsertRows

int mLongestMinDenom;
int mLongestMaxDenom;
protected:
QgsRuleBasedRendererV2* mR;
};


///////

#include "ui_qgsrulebasedrendererv2widget.h"
Expand All @@ -77,36 +96,29 @@ class GUI_EXPORT QgsRuleBasedRendererV2Widget : public QgsRendererV2Widget, priv

void addRule();
void editRule();
void editRule( const QModelIndex& index );
void removeRule();
void increasePriority();
void decreasePriority();

void setGrouping();

void refineRuleScales();
void refineRuleCategories();
void refineRuleRanges();

void usingFirstRuleChanged( );
void symbolLevelsEnabledChanged();
void forceNoSymbolLevels();
void forceUsingFirstRule();

signals:

void forceChkUsingFirstRule();
void setRenderingOrder();

protected:

void refineRule( int type );
QList<QgsRuleBasedRendererV2::Rule> refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule& initialRule );
QList<QgsRuleBasedRendererV2::Rule> refineRuleRangesGui( QgsRuleBasedRendererV2::Rule& initialRule );
QList<QgsRuleBasedRendererV2::Rule> refineRuleScalesGui( QgsRuleBasedRendererV2::Rule& initialRule );
void refineRuleCategoriesGui( const QModelIndex& index );
void refineRuleRangesGui( const QModelIndex& index );
void refineRuleScalesGui( const QModelIndex& index );

QgsRuleBasedRendererV2::Rule* currentRule();

QList<QgsSymbolV2*> selectedSymbols();
void refreshSymbolView();

QgsRuleBasedRendererV2* mRenderer;
QgsRuleBasedRendererV2Model* mModel;

QMenu* mRefineMenu;
};
Expand All @@ -122,19 +134,23 @@ class GUI_EXPORT QgsRendererRulePropsDialog : public QDialog, private Ui::QgsRen
Q_OBJECT

public:
QgsRendererRulePropsDialog( const QgsRuleBasedRendererV2::Rule& rule, QgsVectorLayer* layer, QgsStyleV2* style );
QgsRendererRulePropsDialog( QgsRuleBasedRendererV2::Rule* rule, QgsVectorLayer* layer, QgsStyleV2* style );
~QgsRendererRulePropsDialog();

void updateRuleFromGui();
const QgsRuleBasedRendererV2::Rule& rule() { return mRule; }
QgsRuleBasedRendererV2::Rule* rule() { return mRule; }

public slots:
void testFilter();
void buildExpression();
void accept();

protected:
QgsRuleBasedRendererV2::Rule mRule;
QgsRuleBasedRendererV2::Rule* mRule; // borrowed
QgsVectorLayer* mLayer;
QgsStyleV2* mStyle;

QgsSymbolV2SelectorDialog* mSymbolSelector;
QgsSymbolV2* mSymbol; // a clone of original symbol
};


Expand Down
7 changes: 7 additions & 0 deletions src/gui/symbology-ng/qgssinglesymbolrendererv2widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ QgsSingleSymbolRendererV2Widget::QgsSingleSymbolRendererV2Widget( QgsVectorLayer
// advanced actions - data defined rendering
QMenu* advMenu = mSelector->advancedMenu();

advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );

mDataDefinedMenus = new QgsRendererV2DataDefinedMenus( advMenu, mLayer->pendingFields(),
mRenderer->rotationField(), mRenderer->sizeScaleField() );
connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
Expand Down Expand Up @@ -85,3 +87,8 @@ void QgsSingleSymbolRendererV2Widget::sizeScaleFieldChanged( QString fldName )
{
mRenderer->setSizeScaleField( fldName );
}

void QgsSingleSymbolRendererV2Widget::showSymbolLevels()
{
showSymbolLevelsDialog( mRenderer );
}
2 changes: 2 additions & 0 deletions src/gui/symbology-ng/qgssinglesymbolrendererv2widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class GUI_EXPORT QgsSingleSymbolRendererV2Widget : public QgsRendererV2Widget
void rotationFieldChanged( QString fldName );
void sizeScaleFieldChanged( QString fldName );

void showSymbolLevels();

protected:

QgsSingleSymbolRendererV2* mRenderer;
Expand Down
33 changes: 24 additions & 9 deletions src/gui/symbology-ng/qgssymbollevelsv2dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class SpinBoxDelegate : public QItemDelegate

////////////////

QgsSymbolLevelsV2Dialog::QgsSymbolLevelsV2Dialog( QgsSymbolV2List symbols, bool usingSymbolLevels, QWidget* parent )
: QDialog( parent ), mSymbols( symbols )
QgsSymbolLevelsV2Dialog::QgsSymbolLevelsV2Dialog( QgsLegendSymbolList list, bool usingSymbolLevels, QWidget* parent )
: QDialog( parent ), mList( list ), mForceOrderingEnabled( false )
{
setupUi( this );

Expand All @@ -60,14 +60,15 @@ QgsSymbolLevelsV2Dialog::QgsSymbolLevelsV2Dialog( QgsSymbolV2List symbols, bool
connect( chkEnable, SIGNAL( clicked() ), this, SLOT( updateUi() ) );

int maxLayers = 0;
tableLevels->setRowCount( symbols.count() );
for ( int i = 0; i < symbols.count(); i++ )
tableLevels->setRowCount( list.count() );
for ( int i = 0; i < list.count(); i++ )
{
QgsSymbolV2* sym = symbols[i];
QgsSymbolV2* sym = list[i].second;
QString label = list[i].first;

// set icons for the rows
QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( sym, QSize( 16, 16 ) );
tableLevels->setVerticalHeaderItem( i, new QTableWidgetItem( icon, QString() ) );
tableLevels->setVerticalHeaderItem( i, new QTableWidgetItem( icon, label ) );

// find out max. number of layers per symbol
int layers = sym->symbolLayerCount();
Expand Down Expand Up @@ -96,9 +97,9 @@ QgsSymbolLevelsV2Dialog::QgsSymbolLevelsV2Dialog( QgsSymbolV2List symbols, bool

void QgsSymbolLevelsV2Dialog::populateTable()
{
for ( int row = 0; row < mSymbols.count(); row++ )
for ( int row = 0; row < mList.count(); row++ )
{
QgsSymbolV2* sym = mSymbols[row];
QgsSymbolV2* sym = mList[row].second;
for ( int layer = 0; layer < mMaxLayers; layer++ )
{
QTableWidgetItem* item;
Expand All @@ -109,7 +110,9 @@ void QgsSymbolLevelsV2Dialog::populateTable()
}
else
{
item = new QTableWidgetItem( QString::number( sym->symbolLayer( layer )->renderingPass() ) );
QgsSymbolLayerV2* sl = sym->symbolLayer( layer );
QIcon icon = QgsSymbolLayerV2Utils::symbolLayerPreviewIcon( sl, QgsSymbolV2::MM, QSize( 16, 16 ) );
item = new QTableWidgetItem( icon, QString::number( sl->renderingPass() ) );
}
tableLevels->setItem( row, layer, item );
}
Expand Down Expand Up @@ -149,3 +152,15 @@ void QgsSymbolLevelsV2Dialog::renderingPassChanged( int row, int column )
return;
sym->symbolLayer( column )->setRenderingPass( tableLevels->item( row, column )->text().toInt() );
}

void QgsSymbolLevelsV2Dialog::setForceOrderingEnabled( bool enabled )
{
mForceOrderingEnabled = enabled;
if ( enabled )
{
chkEnable->setChecked( true );
chkEnable->hide();
}
else
chkEnable->show();
}
7 changes: 7 additions & 0 deletions src/gui/symbology-ng/qgssymbollevelsv2dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ class GUI_EXPORT QgsSymbolLevelsV2Dialog : public QDialog, private Ui::QgsSymbol
Q_OBJECT
public:
QgsSymbolLevelsV2Dialog( QgsSymbolV2List symbols, bool usingSymbolLevels, QWidget* parent = NULL );
QgsSymbolLevelsV2Dialog( QgsLegendSymbolList list, bool usingSymbolLevels, QWidget* parent = NULL );

bool usingLevels() const;

// used by rule-based renderer (to hide checkbox to enable/disable ordering)
void setForceOrderingEnabled( bool enabled );

public slots:
void updateUi();

Expand All @@ -30,6 +34,9 @@ class GUI_EXPORT QgsSymbolLevelsV2Dialog : public QDialog, private Ui::QgsSymbol
//! maximal number of layers from all symbols
int mMaxLayers;
QgsSymbolV2List mSymbols;
QgsLegendSymbolList mList;
//! whether symbol layers always should be used (default false)
bool mForceOrderingEnabled;
};

#endif // QGSSYMBOLLEVELSV2DIALOG_H
38 changes: 4 additions & 34 deletions src/ui/qgsrendererrulepropsdialogbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@
<property name="title">
<string>Symbol</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
Expand All @@ -182,38 +185,5 @@
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsRendererRulePropsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsRendererRulePropsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>
8 changes: 0 additions & 8 deletions src/ui/qgsrendererv2propsdialogbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnSymbolLevels">
<property name="text">
<string>Symbol levels</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOldSymbology">
<property name="text">
Expand Down Expand Up @@ -86,7 +79,6 @@
</widget>
<tabstops>
<tabstop>cboRenderers</tabstop>
<tabstop>btnSymbolLevels</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
Expand Down
194 changes: 29 additions & 165 deletions src/ui/qgsrulebasedrendererv2widget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -10,71 +10,34 @@
<height>401</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QgsRendererRulesTreeWidget" name="treeRules">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="viewRules">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="styleSheet">
<string notr="true"/>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Label</string>
</property>
</column>
<column>
<property name="text">
<string>Rule</string>
</property>
</column>
<column>
<property name="text">
<string>Min. scale</string>
</property>
<property name="textAlignment">
<set>AlignHCenter|AlignVCenter|AlignCenter</set>
</property>
</column>
<column>
<property name="text">
<string>Max. scale</string>
</property>
<property name="textAlignment">
<set>AlignHCenter|AlignVCenter|AlignCenter</set>
</property>
</column>
<column>
<property name="text">
<string>Priority</string>
</property>
<property name="toolTip">
<string>Priority when symbol levels are enabled (only first matching rule will be applied)</string>
</property>
<property name="textAlignment">
<set>AlignHCenter|AlignVCenter|AlignCenter</set>
</property>
</column>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnAddRule">
<property name="text">
Expand Down Expand Up @@ -104,128 +67,29 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="btnIncreasePriority">
<property name="text">
<string>Increase priority</string>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnDecreasePriority">
<widget class="QPushButton" name="btnRenderingOrder">
<property name="text">
<string>Decrease priority</string>
<string>Rendering order...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>35</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string/>
</property>
<widget class="QCheckBox" name="chkEnableSymbolLevels">
<property name="geometry">
<rect>
<x>113</x>
<y>10</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>Enable symbol levels</string>
</property>
</widget>
<widget class="QCheckBox" name="chkUsingFirstRule">
<property name="geometry">
<rect>
<x>360</x>
<y>10</y>
<width>271</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>Use only first matched rule</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>84</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>Behavior</string>
</property>
</widget>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Rule grouping</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radNoGrouping">
<property name="text">
<string comment="No grouping for displaying rules">None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radGroupFilter">
<property name="text">
<string comment="Group rules by filter">By filter</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radGroupScale">
<property name="text">
<string comment="Group rules by scale">By scale</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsRendererRulesTreeWidget</class>
<extends>QTreeWidget</extends>
<header>qgsrulebasedrendererv2widget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
336 changes: 336 additions & 0 deletions tests/qt_modeltest/dynamictreemodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
/****************************************************************************
**
** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com>
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "dynamictreemodel.h"

#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QTimer>


DynamicTreeModel::DynamicTreeModel(QObject *parent)
: QAbstractItemModel(parent),
nextId(1)
{
}

QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const
{
// if (column != 0)
// return QModelIndex();


if ( column < 0 || row < 0 )
return QModelIndex();

QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId());

const qint64 grandParent = findParentId(parent.internalId());
if (grandParent >= 0) {
QList<QList<qint64> > parentTable = m_childItems.value(grandParent);
Q_ASSERT(parent.column() < parentTable.size());
QList<qint64> parentSiblings = parentTable.at(parent.column());
Q_ASSERT(parent.row() < parentSiblings.size());
}

if (childIdColumns.size() == 0)
return QModelIndex();

if (column >= childIdColumns.size())
return QModelIndex();

QList<qint64> rowIds = childIdColumns.at(column);

if ( row >= rowIds.size())
return QModelIndex();

qint64 id = rowIds.at(row);

return createIndex(row, column, reinterpret_cast<void *>(id));

}

qint64 DynamicTreeModel::findParentId(qint64 searchId) const
{
if (searchId <= 0)
return -1;

QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems);
while (i.hasNext())
{
i.next();
QListIterator<QList<qint64> > j(i.value());
while (j.hasNext())
{
QList<qint64> l = j.next();
if (l.contains(searchId))
{
return i.key();
}
}
}
return -1;
}

QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();

qint64 searchId = index.internalId();
qint64 parentId = findParentId(searchId);
// Will never happen for valid index, but what the hey...
if (parentId <= 0)
return QModelIndex();

qint64 grandParentId = findParentId(parentId);
if (grandParentId < 0)
grandParentId = 0;

int column = 0;
QList<qint64> childList = m_childItems.value(grandParentId).at(column);

int row = childList.indexOf(parentId);

return createIndex(row, column, reinterpret_cast<void *>(parentId));

}

int DynamicTreeModel::rowCount(const QModelIndex &index ) const
{
QList<QList<qint64> > cols = m_childItems.value(index.internalId());

if (cols.size() == 0 )
return 0;

if (index.column() > 0)
return 0;

return cols.at(0).size();
}

int DynamicTreeModel::columnCount(const QModelIndex &index ) const
{
// Q_UNUSED(index);
return m_childItems.value(index.internalId()).size();
}

QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();

if (Qt::DisplayRole == role)
{
return m_items.value(index.internalId());
}
return QVariant();
}

void DynamicTreeModel::clear()
{
beginResetModel();
m_items.clear();
m_childItems.clear();
nextId = 1;
endResetModel();
}


ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent )
: QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1)
{

}

QModelIndex ModelChangeCommand::findIndex(QList<int> rows)
{
const int col = 0;
QModelIndex parent = QModelIndex();
QListIterator<int> i(rows);
while (i.hasNext())
{
parent = m_model->index(i.next(), col, parent);
Q_ASSERT(parent.isValid());
}
return parent;
}

ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent )
: ModelChangeCommand(model, parent)
{

}

void ModelInsertCommand::doCommand()
{
QModelIndex parent = findIndex(m_rowNumbers);
m_model->beginInsertRows(parent, m_startRow, m_endRow);
qint64 parentId = parent.internalId();
for (int row = m_startRow; row <= m_endRow; row++)
{
for(int col = 0; col < m_numCols; col++ )
{
if (m_model->m_childItems[parentId].size() <= col)
{
m_model->m_childItems[parentId].append(QList<qint64>());
}
// QString name = QUuid::createUuid().toString();
qint64 id = m_model->newId();
QString name = QString::number(id);

m_model->m_items.insert(id, name);
m_model->m_childItems[parentId][col].insert(row, id);

}
}
m_model->endInsertRows();
}


ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent)
: ModelChangeCommand(model, parent)
{

}
bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
{
return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow);
}

void ModelMoveCommand::doCommand()
{
QModelIndex srcParent = findIndex(m_rowNumbers);
QModelIndex destParent = findIndex(m_destRowNumbers);

if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow))
{
return;
}

for (int column = 0; column < m_numCols; ++column)
{
QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 );

for (int i = m_startRow; i <= m_endRow ; i++)
{
m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow);
}
int d;
if (m_destRow < m_startRow)
d = m_destRow;
else
{
if (srcParent == destParent)
d = m_destRow - (m_endRow - m_startRow + 1);
else
d = m_destRow - (m_endRow - m_startRow) + 1;
}

foreach(const qint64 id, l)
{
m_model->m_childItems[destParent.internalId()][column].insert(d++, id);
}
}

emitPostSignal();
}

void ModelMoveCommand::emitPostSignal()
{
m_model->endMoveRows();
}

ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent)
: ModelMoveCommand(model, parent)
{

}

ModelResetCommand::~ModelResetCommand()
{

}

bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
{
Q_UNUSED(srcParent);
Q_UNUSED(srcStart);
Q_UNUSED(srcEnd);
Q_UNUSED(destParent);
Q_UNUSED(destRow);

return true;
}

void ModelResetCommand::emitPostSignal()
{
m_model->reset();
}

ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent)
: ModelMoveCommand(model, parent)
{

}

ModelResetCommandFixed::~ModelResetCommandFixed()
{

}

bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
{
Q_UNUSED(srcParent);
Q_UNUSED(srcStart);
Q_UNUSED(srcEnd);
Q_UNUSED(destParent);
Q_UNUSED(destRow);

m_model->beginResetModel();
return true;
}

void ModelResetCommandFixed::emitPostSignal()
{
m_model->endResetModel();
}

197 changes: 197 additions & 0 deletions tests/qt_modeltest/dynamictreemodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/****************************************************************************
**
** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com>
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef DYNAMICTREEMODEL_H
#define DYNAMICTREEMODEL_H

#include <QtCore/QAbstractItemModel>

#include <QtCore/QHash>
#include <QtCore/QList>


class DynamicTreeModel : public QAbstractItemModel
{
Q_OBJECT

public:
DynamicTreeModel(QObject *parent = 0);

QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &index = QModelIndex()) const;
int columnCount(const QModelIndex &index = QModelIndex()) const;

QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

void clear();

protected slots:

/**
Finds the parent id of the string with id @p searchId.
Returns -1 if not found.
*/
qint64 findParentId(qint64 searchId) const;

private:
QHash<qint64, QString> m_items;
QHash<qint64, QList<QList<qint64> > > m_childItems;
qint64 nextId;
qint64 newId() { return nextId++; };

QModelIndex m_nextParentIndex;
int m_nextRow;

int m_depth;
int maxDepth;

friend class ModelInsertCommand;
friend class ModelMoveCommand;
friend class ModelResetCommand;
friend class ModelResetCommandFixed;

};


class ModelChangeCommand : public QObject
{
Q_OBJECT
public:

ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 );

virtual ~ModelChangeCommand() {}

void setAncestorRowNumbers(QList<int> rowNumbers) { m_rowNumbers = rowNumbers; }

QModelIndex findIndex(QList<int> rows);

void setStartRow(int row) { m_startRow = row; }

void setEndRow(int row) { m_endRow = row; }

void setNumCols(int cols) { m_numCols = cols; }

virtual void doCommand() = 0;

protected:
DynamicTreeModel* m_model;
QList<int> m_rowNumbers;
int m_numCols;
int m_startRow;
int m_endRow;

};

typedef QList<ModelChangeCommand*> ModelChangeCommandList;

class ModelInsertCommand : public ModelChangeCommand
{
Q_OBJECT

public:

ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 );
virtual ~ModelInsertCommand() {}

virtual void doCommand();
};


class ModelMoveCommand : public ModelChangeCommand
{
Q_OBJECT
public:
ModelMoveCommand(DynamicTreeModel *model, QObject *parent);

virtual ~ModelMoveCommand() {}

virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);

virtual void doCommand();

virtual void emitPostSignal();

void setDestAncestors( QList<int> rows ) { m_destRowNumbers = rows; }

void setDestRow(int row) { m_destRow = row; }

protected:
QList<int> m_destRowNumbers;
int m_destRow;
};

/**
A command which does a move and emits a reset signal.
*/
class ModelResetCommand : public ModelMoveCommand
{
Q_OBJECT
public:
ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0);

virtual ~ModelResetCommand();

virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);
virtual void emitPostSignal();

};

/**
A command which does a move and emits a beginResetModel and endResetModel signals.
*/
class ModelResetCommandFixed : public ModelMoveCommand
{
Q_OBJECT
public:
ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0);

virtual ~ModelResetCommandFixed();

virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow);
virtual void emitPostSignal();

};


#endif
566 changes: 566 additions & 0 deletions tests/qt_modeltest/modeltest.cpp

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions tests/qt_modeltest/modeltest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/


#ifndef MODELTEST_H
#define MODELTEST_H

#include <QtCore/QObject>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QStack>

class ModelTest : public QObject
{
Q_OBJECT

public:
ModelTest( QAbstractItemModel *model, QObject *parent = 0 );

private Q_SLOTS:
void nonDestructiveBasicTest();
void rowCount();
void columnCount();
void hasIndex();
void index();
void parent();
void data();

protected Q_SLOTS:
void runAllTests();
void layoutAboutToBeChanged();
void layoutChanged();
void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end );
void rowsInserted( const QModelIndex & parent, int start, int end );
void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end );
void rowsRemoved( const QModelIndex & parent, int start, int end );

private:
void checkChildren( const QModelIndex &parent, int currentDepth = 0 );

QAbstractItemModel *model;

struct Changing {
QModelIndex parent;
int oldSize;
QVariant last;
QVariant next;
};
QStack<Changing> insert;
QStack<Changing> remove;

bool fetchingMore;

QList<QPersistentModelIndex> changing;
};

#endif
7 changes: 7 additions & 0 deletions tests/qt_modeltest/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Model Test
----------

This is a simple tool from Qt for checking various errors of custom
implementations of models for item views.

http://developer.qt.nokia.com/wiki/Model_Test
309 changes: 309 additions & 0 deletions tests/qt_modeltest/tst_modeltest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtTest/QtTest>
#include <QtGui/QtGui>

#include "modeltest.h"
#include "dynamictreemodel.h"


class tst_ModelTest : public QObject
{
Q_OBJECT

public:
tst_ModelTest() {}
virtual ~tst_ModelTest() {}

public slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();

private slots:
void stringListModel();
void treeWidgetModel();
void standardItemModel();
void testInsertThroughProxy();
void moveSourceItems();
void testResetThroughProxy();
};



void tst_ModelTest::initTestCase()
{
}

void tst_ModelTest::cleanupTestCase()
{
}

void tst_ModelTest::init()
{

}

void tst_ModelTest::cleanup()
{
}
/*
tests
*/

void tst_ModelTest::stringListModel()
{
QStringListModel model;
QSortFilterProxyModel proxy;

ModelTest t1(&model);
ModelTest t2(&proxy);

proxy.setSourceModel(&model);

model.setStringList(QStringList() << "2" << "3" << "1");
model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" );

proxy.setDynamicSortFilter(true);
proxy.setFilterRegExp(QRegExp("[^b]"));
}

void tst_ModelTest::treeWidgetModel()
{
QTreeWidget widget;

ModelTest t1(widget.model());

QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root"));
for (int i = 0; i < 20; ++i) {
new QTreeWidgetItem(root, QStringList(QString::number(i)));
}
QTreeWidgetItem *remove = root->child(2);
root->removeChild(remove);
QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent"));
new QTreeWidgetItem(parent, QStringList("child"));
widget.setItemHidden(parent, true);

widget.sortByColumn(0);
}

void tst_ModelTest::standardItemModel()
{
QStandardItemModel model(10,10);
QSortFilterProxyModel proxy;


ModelTest t1(&model);
ModelTest t2(&proxy);

proxy.setSourceModel(&model);

model.insertRows(2, 5);
model.removeRows(4, 5);

model.insertColumns(2, 5);
model.removeColumns(4, 5);

model.insertRows(0,5, model.index(1,1));
model.insertColumns(0,5, model.index(1,3));
}

void tst_ModelTest::testInsertThroughProxy()
{
DynamicTreeModel *model = new DynamicTreeModel(this);

QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(model);

new ModelTest(proxy, this);

ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setNumCols(4);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
// Parent is QModelIndex()
insertCommand->doCommand();

insertCommand = new ModelInsertCommand(model, this);
insertCommand->setNumCols(4);
insertCommand->setAncestorRowNumbers(QList<int>() << 5);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
insertCommand->doCommand();

ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this);
moveCommand->setNumCols(4);
moveCommand->setStartRow(0);
moveCommand->setEndRow(0);
moveCommand->setDestRow(9);
moveCommand->setDestAncestors(QList<int>() << 5);
moveCommand->doCommand();
}

/**
Makes the persistent index list publicly accessible
*/
class AccessibleProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {}

QModelIndexList persistent()
{
return persistentIndexList();
}
};

class ObservingObject : public QObject
{
Q_OBJECT
public:
ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0)
: QObject(parent),
m_proxy(proxy)
{
connect(m_proxy, SIGNAL(layoutAboutToBeChanged()), SLOT(storePersistent()));
connect(m_proxy, SIGNAL(layoutChanged()), SLOT(checkPersistent()));
}

public slots:

void storePersistent(const QModelIndex &parent)
{
for (int row = 0; row < m_proxy->rowCount(parent); ++row) {
QModelIndex proxyIndex = m_proxy->index(row, 0, parent);
QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex);
Q_ASSERT(proxyIndex.isValid());
Q_ASSERT(sourceIndex.isValid());
m_persistentSourceIndexes.append(sourceIndex);
m_persistentProxyIndexes.append(proxyIndex);
if (m_proxy->hasChildren(proxyIndex))
storePersistent(proxyIndex);
}
}

void storePersistent()
{
foreach(const QModelIndex &idx, m_persistentProxyIndexes)
Q_ASSERT(idx.isValid()); // This is called from layoutAboutToBeChanged. Persistent indexes should be valid

Q_ASSERT(m_proxy->persistent().isEmpty());
storePersistent(QModelIndex());
Q_ASSERT(!m_proxy->persistent().isEmpty());
}

void checkPersistent()
{
for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) {
QModelIndex updatedProxy = m_persistentProxyIndexes.at(row);
QModelIndex updatedSource = m_persistentSourceIndexes.at(row);
}
for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) {
QModelIndex updatedProxy = m_persistentProxyIndexes.at(row);
QModelIndex updatedSource = m_persistentSourceIndexes.at(row);
QCOMPARE(m_proxy->mapToSource(updatedProxy), updatedSource);
}
m_persistentSourceIndexes.clear();
m_persistentProxyIndexes.clear();
}

private:
AccessibleProxyModel *m_proxy;
QList<QPersistentModelIndex> m_persistentSourceIndexes;
QList<QPersistentModelIndex> m_persistentProxyIndexes;
};

void tst_ModelTest::moveSourceItems()
{
DynamicTreeModel *model = new DynamicTreeModel(this);
AccessibleProxyModel *proxy = new AccessibleProxyModel(this);
proxy->setSourceModel(model);

ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();

insertCommand = new ModelInsertCommand(model, this);
insertCommand->setAncestorRowNumbers(QList<int>() << 1);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();

ObservingObject observer(proxy);

ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this);
moveCommand->setStartRow(0);
moveCommand->setEndRow(0);
moveCommand->setDestAncestors(QList<int>() << 1);
moveCommand->setDestRow(0);
moveCommand->doCommand();
}

void tst_ModelTest::testResetThroughProxy()
{
DynamicTreeModel *model = new DynamicTreeModel(this);

ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();

QPersistentModelIndex persistent = model->index(0, 0);

AccessibleProxyModel *proxy = new AccessibleProxyModel(this);
proxy->setSourceModel(model);

ObservingObject observer(proxy);
observer.storePersistent();

ModelResetCommand *resetCommand = new ModelResetCommand(model, this);
resetCommand->setNumCols(0);
resetCommand->doCommand();
}


QTEST_MAIN(tst_ModelTest)
#include "tst_modeltest.moc"
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ ADD_QGIS_TEST(coordinatereferencesystemtest testqgscoordinatereferencesystem.cpp
ADD_QGIS_TEST(pointtest testqgspoint.cpp)
ADD_QGIS_TEST(searchstringtest testqgssearchstring.cpp)
ADD_QGIS_TEST(vectorlayertest testqgsvectorlayer.cpp)
ADD_QGIS_TEST(rulebasedrenderertest testqgsrulebasedrenderer.cpp)

96 changes: 96 additions & 0 deletions tests/src/core/testqgsrulebasedrenderer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/***************************************************************************
test_template.cpp
--------------------------------------
Date : Sun Sep 16 12:22:23 AKDT 2007
Copyright : (C) 2007 by Gary E. Sherman
Email : sherman at mrcc 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 <QtTest>
#include <QDomDocument>
#include <QFile>
//header for class being tested
#include <qgsrulebasedrendererv2.h>

#if QT_VERSION < 0x40701
// See http://hub.qgis.org/issues/4284
Q_DECLARE_METATYPE( QVariant )
#endif


class TestQgsRuleBasedRenderer: public QObject
{
Q_OBJECT
private slots:

void test_load_xml()
{
QDomDocument doc;
xml2domElement( "rulebasedrenderer_simple.xml", doc );
QDomElement elem = doc.documentElement();

QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) );
QVERIFY( r );
check_tree_valid( r->rootRule() );
delete r;
}

void test_load_invalid_xml()
{
QDomDocument doc;
xml2domElement( "rulebasedrenderer_invalid.xml", doc );
QDomElement elem = doc.documentElement();

QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) );
QVERIFY( r == NULL );
}

private:
void xml2domElement( QString testFile, QDomDocument& doc )
{
QString fileName = QString( TEST_DATA_DIR ) + QDir::separator() + testFile;
QFile f(fileName);
bool fileOpen = f.open(QIODevice::ReadOnly);
QVERIFY( fileOpen );

QString msg;
int line, col;
bool parse = doc.setContent( &f, &msg, &line, &col );
QVERIFY( parse );
}

void check_tree_valid( QgsRuleBasedRendererV2::Rule* root )
{
// root must always exist (although it does not have children)
QVERIFY( root );
// and does not have a parent
QVERIFY( root->parent() == NULL );

foreach ( QgsRuleBasedRendererV2::Rule* node, root->children() )
check_non_root_rule( node );
}

void check_non_root_rule( QgsRuleBasedRendererV2::Rule* node )
{
qDebug() << node->dump();
// children must not be NULL
QVERIFY( node );
// and must have a parent
QVERIFY( node->parent() );
// check that all children are okay
foreach ( QgsRuleBasedRendererV2::Rule* child, node->children() )
check_non_root_rule( child );
}

};

QTEST_MAIN( TestQgsRuleBasedRenderer )

#include "moc_testqgsrulebasedrenderer.cxx"

22 changes: 22 additions & 0 deletions tests/src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ SET (util_SRCS ../core/qgsrenderchecker.cpp)
# the UI file won't be wrapped!
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/../../../src/ui
${CMAKE_CURRENT_SOURCE_DIR}/../core #for render checker class
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/gui
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/gui/symbology-ng
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/raster
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/symbology-ng
${QT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${PROJ_INCLUDE_DIR}
Expand Down Expand Up @@ -73,4 +76,23 @@ ENDIF (APPLE)
#ENDIF (APPLE)


# a simple app for testing GUI of renderers

SET(rendererv2gui_SRCS testrendererv2gui.cpp)
SET(rendererv2gui_HDRS testrendererv2gui.h)

QT4_WRAP_CPP(rendererv2gui_MOC_SRCS ${rendererv2gui_HDRS})

ADD_EXECUTABLE(qgis_rendererv2gui ${rendererv2gui_SRCS} ${rendererv2gui_MOC_SRCS})

TARGET_LINK_LIBRARIES(qgis_rendererv2gui
qgis_core
qgis_gui
${QT_QTCORE_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${QT_QTSVG_LIBRARY}
${QT_QTXML_LIBRARY}
${QT_QTWEBKIT_LIBRARY}
${QT_QTMAIN_LIBRARY}
#${QT_QTTEST_LIBRARY}
)
84 changes: 84 additions & 0 deletions tests/src/gui/testrendererv2gui.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "testrendererv2gui.h"

#include <qgsapplication.h>
#include <qgsmapcanvas.h>
#include <qgsvectorlayer.h>
#include <qgsmaplayerregistry.h>
#include <qgsproject.h>
#include <qgsrendererv2propertiesdialog.h>
#include <qgsstylev2.h>

#include <QApplication>
#include <QToolBar>

TestRendererV2GUI::TestRendererV2GUI(QWidget *parent) :
QMainWindow(parent)
{
resize(640,480);

QToolBar* toolBar = addToolBar("Actions");
toolBar->addAction( "set renderer", this, SLOT(setRenderer()) );

mMapCanvas = new QgsMapCanvas(this);
mMapCanvas->setCanvasColor( Qt::white );
setCentralWidget(mMapCanvas);

connect( QgsProject::instance(), SIGNAL(readProject(QDomDocument)), mMapCanvas, SLOT(readProject(QDomDocument)));
}

void TestRendererV2GUI::loadLayers()
{
// load just first vector layer
QList<QgsMapCanvasLayer> canvasLayers;
foreach (QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers().values())
{
if ( layer->type() == QgsMapLayer::VectorLayer )
canvasLayers << QgsMapCanvasLayer( layer );
}

mMapCanvas->setLayerSet(canvasLayers);
}

void TestRendererV2GUI::setRenderer()
{
QgsMapLayer* layer = mMapCanvas->layer(0);
Q_ASSERT( layer );
Q_ASSERT( layer->type() == QgsMapLayer::VectorLayer );
QgsVectorLayer* vlayer = static_cast<QgsVectorLayer*>(layer);

QgsRendererV2PropertiesDialog dlg( vlayer, QgsStyleV2::defaultStyle() );
dlg.exec();

mMapCanvas->refresh();
}

int main(int argc, char* argv[])
{
QApplication app(argc, argv);

if ( argc < 2 )
{
qDebug( "Provide a project file name with at least one vector layer!" );
return 1;
}

QgsApplication::init();
QgsApplication::initQgis();

TestRendererV2GUI gui;

QString projectFileName( argv[1] );
QgsProject::instance()->setFileName( projectFileName );
bool res = QgsProject::instance()->read();
if ( !res )
{
qDebug("Failed to open project!");
return 1;
}

// the layers are in the registry - now load them!
gui.loadLayers();

gui.show();
return app.exec();
}
24 changes: 24 additions & 0 deletions tests/src/gui/testrendererv2gui.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef TESTRENDERERV2GUI_H
#define TESTRENDERERV2GUI_H

#include <QMainWindow>

class QgsMapCanvas;

class TestRendererV2GUI : public QMainWindow
{
Q_OBJECT
public:
explicit TestRendererV2GUI(QWidget *parent = 0);
void loadLayers();

signals:

public slots:
void setRenderer();

protected:
QgsMapCanvas* mMapCanvas;
};

#endif // TESTRENDERERV2GUI_H
2 changes: 2 additions & 0 deletions tests/testdata/rulebasedrenderer_invalid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<renderer-v2 symbollevels="0" type="RuleRenderer">
</renderer-v2>
32 changes: 32 additions & 0 deletions tests/testdata/rulebasedrenderer_simple.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<renderer-v2 symbollevels="0" type="RuleRenderer">
<rules>
<rule filter="type=3" symbol="0" label="type3"/>
<rule filter="type=1" symbol="1" label="type1"/>
</rules>
<symbols>
<symbol outputUnit="MM" alpha="1" type="line" name="0">
<layer pass="0" class="SimpleLine" locked="0">
<prop k="capstyle" v="square"/>
<prop k="color" v="220,0,0,255"/>
<prop k="customdash" v="5;2"/>
<prop k="joinstyle" v="bevel"/>
<prop k="offset" v="0"/>
<prop k="penstyle" v="solid"/>
<prop k="use_custom_dash" v="0"/>
<prop k="width" v="0.26"/>
</layer>
</symbol>
<symbol outputUnit="MM" alpha="1" type="line" name="1">
<layer pass="0" class="SimpleLine" locked="0">
<prop k="capstyle" v="square"/>
<prop k="color" v="94,116,254,255"/>
<prop k="customdash" v="5;2"/>
<prop k="joinstyle" v="bevel"/>
<prop k="offset" v="0"/>
<prop k="penstyle" v="solid"/>
<prop k="use_custom_dash" v="0"/>
<prop k="width" v="0.26"/>
</layer>
</symbol>
</symbols>
</renderer-v2>