48 changes: 40 additions & 8 deletions src/app/qgslabelinggui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,21 @@

#include "qgspallabeling.h"
#include "qgslabelengineconfigdialog.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsexpression.h"

#include <QColorDialog>
#include <QFontDialog>

#include <QTextEdit>
#include <iostream>
#include <QApplication>


#include <QMessageBox>

QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QWidget* parent )
: QDialog( parent ), mLBL( lbl ), mLayer( layer ), mMapCanvas( mapCanvas )
{
if ( !layer ) return;

setupUi( this );

connect( btnTextColor, SIGNAL( clicked() ), this, SLOT( changeTextColor() ) );
Expand All @@ -44,6 +47,7 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
connect( btnBufferColor, SIGNAL( clicked() ), this, SLOT( changeBufferColor() ) );
connect( spinBufferSize, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
connect( btnEngineSettings, SIGNAL( clicked() ), this, SLOT( showEngineConfigDialog() ) );
connect( btnExpression, SIGNAL( clicked() ), this, SLOT( showExpressionDialog() ) );

// set placement methods page based on geometry type
switch ( layer->geometryType() )
Expand All @@ -61,19 +65,27 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
Q_ASSERT( 0 && "NOOOO!" );
}

//mTabWidget->setEnabled( chkEnableLabeling->isChecked() );
chkMergeLines->setEnabled( layer->geometryType() == QGis::Line );
chkAddDirectionSymbol->setEnabled( layer->geometryType() == QGis::Line );
label_19->setEnabled( layer->geometryType() != QGis::Point );
mMinSizeSpinBox->setEnabled( layer->geometryType() != QGis::Point );

populateFieldNames();

// load labeling settings from layer
QgsPalLayerSettings lyr;
lyr.readFromLayer( layer );

populateFieldNames();
populateDataDefinedCombos( lyr );

chkEnableLabeling->setChecked( lyr.enabled );
mTabWidget->setEnabled( lyr.enabled );
cboFieldName->setEnabled( lyr.enabled );
btnExpression->setEnabled( lyr.enabled );

//Add the current expression to the bottom of the list.
if ( lyr.isExpression && !lyr.fieldName.isEmpty() )
cboFieldName->addItem( lyr.fieldName );

// placement
int distUnitIndex = lyr.distInMapUnits ? 1 : 0;
switch ( lyr.placement )
Expand Down Expand Up @@ -200,7 +212,8 @@ QgsLabelingGui::~QgsLabelingGui()

void QgsLabelingGui::apply()
{
layerSettings().writeToLayer( mLayer );
QgsPalLayerSettings settings = layerSettings();
settings.writeToLayer( mLayer );
// trigger refresh
if ( mMapCanvas )
{
Expand All @@ -212,6 +225,9 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
{
QgsPalLayerSettings lyr;
lyr.fieldName = cboFieldName->currentText();
// Check if we are an expression. Also treats expressions with just a column name as non expressions,
// this saves time later so we don't have to parse the expression tree.
lyr.isExpression = mLayer->fieldNameIndex( lyr.fieldName ) == -1 && !lyr.fieldName.isEmpty();

lyr.dist = 0;
lyr.placementFlags = 0;
Expand Down Expand Up @@ -328,7 +344,6 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
return lyr;
}


void QgsLabelingGui::populateFieldNames()
{
const QgsFieldMap& fields = mLayer->pendingFields();
Expand Down Expand Up @@ -477,6 +492,23 @@ void QgsLabelingGui::showEngineConfigDialog()
dlg.exec();
}

void QgsLabelingGui::showExpressionDialog()
{
//TODO extract this out to a dialog.
QgsExpressionBuilderDialog dlg( mLayer, cboFieldName->currentText() , this );
dlg.setWindowTitle( tr( "Expression based label" ) );
if ( dlg.exec() == QDialog::Accepted )
{
QString expression = dlg.expressionBuilder()->getExpressionString();
//Only add the expression if the user has entered some text.
if ( !expression.isEmpty() )
{
cboFieldName->addItem( expression );
cboFieldName->setCurrentIndex( cboFieldName->count() - 1 );
}
}
}

void QgsLabelingGui::updateUi()
{
// enable/disable scale-based, buffer, decimals
Expand Down
1 change: 1 addition & 0 deletions src/app/qgslabelinggui.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class QgsLabelingGui : public QDialog, private Ui::QgsLabelingGuiBase
void changeTextColor();
void changeTextFont();
void showEngineConfigDialog();
void showExpressionDialog();
void changeBufferColor();

void updateUi();
Expand Down
73 changes: 42 additions & 31 deletions src/core/qgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,40 +375,46 @@ typedef QgsExpression::FunctionDef FnDef;
FnDef QgsExpression::BuiltinFunctions[] =
{
// math
FnDef( "sqrt", 1, fcnSqrt ),
FnDef( "sin", 1, fcnSin ),
FnDef( "cos", 1, fcnCos ),
FnDef( "tan", 1, fcnTan ),
FnDef( "asin", 1, fcnAsin ),
FnDef( "acos", 1, fcnAcos ),
FnDef( "atan", 1, fcnAtan ),
FnDef( "atan2", 2, fcnAtan2 ),
FnDef( "exp", 1, fcnExp ),
FnDef( "ln", 1, fcnLn ),
FnDef( "log10", 1, fcnLog10 ),
FnDef( "log", 2, fcnLog ),
FnDef( "sqrt", 1, fcnSqrt, "Math" ),
FnDef( "sin", 1, fcnSin, "Math" ),
FnDef( "cos", 1, fcnCos, "Math" ),
FnDef( "tan", 1, fcnTan, "Math" ),
FnDef( "asin", 1, fcnAsin, "Math" ),
FnDef( "acos", 1, fcnAcos, "Math" ),
FnDef( "atan", 1, fcnAtan, "Math" ),
FnDef( "atan2", 2, fcnAtan2, "Math" ),
FnDef( "exp", 1, fcnExp, "Math" ),
FnDef( "ln", 1, fcnLn, "Math" ),
FnDef( "log10", 1, fcnLog10, "Math" ),
FnDef( "log", 2, fcnLog, "Math" ),
// casts
FnDef( "toint", 1, fcnToInt ),
FnDef( "toreal", 1, fcnToReal ),
FnDef( "tostring", 1, fcnToString ),
FnDef( "toint", 1, fcnToInt, "Conversions" ),
FnDef( "toreal", 1, fcnToReal, "Conversions" ),
FnDef( "tostring", 1, fcnToString, "Conversions" ),
// string manipulation
FnDef( "lower", 1, fcnLower ),
FnDef( "upper", 1, fcnUpper ),
FnDef( "length", 1, fcnLength ),
FnDef( "replace", 3, fcnReplace ),
FnDef( "regexp_replace", 3, fcnRegexpReplace ),
FnDef( "substr", 3, fcnSubstr ),
FnDef( "lower", 1, fcnLower, "String", "<b>Convert to lower case</b> "\
"<br> Converts a string to lower case letters. " \
"<br> <i>Usage:</i><br>lower('HELLO WORLD') will return 'hello world'" ),
FnDef( "upper", 1, fcnUpper, "String" , "<b>Convert to upper case</b> "\
"<br> Converts a string to upper case letters. " \
"<br> <i>Usage:</i><br>upper('hello world') will return 'HELLO WORLD'" ),
FnDef( "length", 1, fcnLength, "String", "<b>Length of string</b> "\
"<br> Returns the legnth of a string. " \
"<br> <i>Usage:</i><br>length('hello') will return 5" ),
FnDef( "replace", 3, fcnReplace, "String", "<b>Replace a section of a string.</b> " ),
FnDef( "regexp_replace", 3, fcnRegexpReplace, "String" ),
FnDef( "substr", 3, fcnSubstr, "String" ),
// geometry accessors
FnDef( "xat", 1, fcnXat, true ),
FnDef( "yat", 1, fcnYat, true ),
FnDef( "xat", 1, fcnXat, "Geometry", "", true ),
FnDef( "yat", 1, fcnYat, "Geometry", "", true ),
FnDef( "$area", 0, fcnGeomArea, "Geometry", "", true ),
FnDef( "$length", 0, fcnGeomLength, "Geometry", "", true ),
FnDef( "$perimeter", 0, fcnGeomPerimeter, "Geometry", "", true ),
FnDef( "$x", 0, fcnX, "Geometry", "", true ),
FnDef( "$y", 0, fcnY, "Geometry", "" , true ),
// special columns
FnDef( "$rownum", 0, fcnRowNumber ),
FnDef( "$area", 0, fcnGeomArea, true ),
FnDef( "$length", 0, fcnGeomLength, true ),
FnDef( "$perimeter", 0, fcnGeomPerimeter, true ),
FnDef( "$x", 0, fcnX, true ),
FnDef( "$y", 0, fcnY, true ),
FnDef( "$id", 0, fcnFeatureId ),
FnDef( "$rownum", 0, fcnRowNumber, "Record" ),
FnDef( "$id", 0, fcnFeatureId, "Record" )
};


Expand All @@ -419,7 +425,7 @@ bool QgsExpression::isFunctionName( QString name )

int QgsExpression::functionIndex( QString name )
{
int count = sizeof( BuiltinFunctions ) / sizeof( FunctionDef );
int count = functionCount();
for ( int i = 0; i < count; i++ )
{
if ( QString::compare( name, BuiltinFunctions[i].mName, Qt::CaseInsensitive ) == 0 )
Expand All @@ -428,6 +434,11 @@ int QgsExpression::functionIndex( QString name )
return -1;
}

int QgsExpression::functionCount()
{
return ( sizeof( BuiltinFunctions ) / sizeof( FunctionDef ) );
}


QgsExpression::QgsExpression( const QString& expr )
: mExpression( expr ), mRowNumber( 0 ), mCalc( NULL )
Expand Down
19 changes: 16 additions & 3 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The expressions try to follow both syntax and semantics of SQL expressions.
Usage:
QgsExpression exp("gid*2 > 10 and type not in ('D','F');
QgsExpression exp("gid*2 > 10 and type not in ('D','F'));
if (exp.hasParserError())
{
// show error message with parserErrorString() and exit
Expand Down Expand Up @@ -170,12 +170,20 @@ class CORE_EXPORT QgsExpression

struct FunctionDef
{
FunctionDef( QString fnname, int params, FcnEval fcn, bool usesGeometry = false )
: mName( fnname ), mParams( params ), mFcn( fcn ), mUsesGeometry( usesGeometry ) {}
FunctionDef( QString fnname, int params, FcnEval fcn, QString group, QString helpText = QString(), bool usesGeometry = false )
: mName( fnname ), mParams( params ), mFcn( fcn ), mUsesGeometry( usesGeometry ), mGroup( group ), mHelpText( helpText ) {}
/** The name of the function. */
QString mName;
/** The number of parameters this function takes. */
int mParams;
/** Pointer to fucntion. */
FcnEval mFcn;
/** Does this function use a geometry object. */
bool mUsesGeometry;
/** The group the function belongs to. */
QString mGroup;
/** The help text for the function. */
QString mHelpText;
};

static FunctionDef BuiltinFunctions[];
Expand All @@ -186,6 +194,11 @@ class CORE_EXPORT QgsExpression
// return index of the function in BuiltinFunctions array
static int functionIndex( QString name );

/** Returns the number of functions defined in the parser
* @return The number of function defined in the parser.
*/
static int functionCount();

//! return quoted column reference (in double quotes)
static QString quotedColumnRef( QString name ) { return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) ); }

Expand Down
4 changes: 1 addition & 3 deletions src/core/qgslabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,9 @@ QString QgsLabel::fieldValue( int attr, QgsFeature &feature )
}

void QgsLabel::renderLabel( QgsRenderContext &renderContext,
QgsFeature &feature,
bool selected,
QgsFeature &feature, bool selected,
QgsLabelAttributes *classAttributes )
{
Q_UNUSED( classAttributes );
if ( mLabelAttributes->selectedOnly() && !selected )
return;

Expand Down
Loading