Index: src/app/qgslabelinggui.h =================================================================== --- src/app/qgslabelinggui.h (Revision 14234) +++ src/app/qgslabelinggui.h (Arbeitskopie) @@ -45,6 +45,9 @@ void updatePreview(); void updateOptions(); + void on_mXCoordinateComboBox_currentIndexChanged( const QString & text ); + void on_mYCoordinateComboBox_currentIndexChanged( const QString & text ); + protected: void populatePlacementMethods(); void populateFieldNames(); @@ -57,6 +60,9 @@ private: QgsPalLabeling* mLBL; QgsVectorLayer* mLayer; + + void disableDataDefinedAlignment(); + void enableDataDefinedAlignment(); }; #endif Index: src/app/qgslabelinggui.cpp =================================================================== --- src/app/qgslabelinggui.cpp (Revision 14234) +++ src/app/qgslabelinggui.cpp (Arbeitskopie) @@ -123,6 +123,7 @@ chkMergeLines->setChecked( lyr.mergeLines ); chkMultiLine->setChecked( lyr.multiLineLabels ); mMinSizeSpinBox->setValue( lyr.minFeatureSize ); + chkAddDirectionSymbol->setChecked( lyr.addDirectionSymbol ); bool scaleBased = ( lyr.scaleMin != 0 && lyr.scaleMax != 0 ); chkScaleBasedVisibility->setChecked( scaleBased ); @@ -238,8 +239,17 @@ { lyr.bufferSize = 0; } + if ( chkAddDirectionSymbol->isChecked() ) + { + lyr.addDirectionSymbol = true; + } + else + { + lyr.addDirectionSymbol = false; + } lyr.minFeatureSize = mMinSizeSpinBox->value(); + //data defined labeling setDataDefinedProperty( mSizeAttributeComboBox, QgsPalLayerSettings::Size, lyr ); setDataDefinedProperty( mColorAttributeComboBox, QgsPalLayerSettings::Color, lyr ); @@ -250,6 +260,10 @@ setDataDefinedProperty( mFontFamilyAttributeComboBox, QgsPalLayerSettings::Family, lyr ); setDataDefinedProperty( mBufferSizeAttributeComboBox, QgsPalLayerSettings:: BufferSize, lyr ); setDataDefinedProperty( mBufferColorAttributeComboBox, QgsPalLayerSettings::BufferColor, lyr ); + setDataDefinedProperty( mXCoordinateComboBox, QgsPalLayerSettings::PositionX, lyr ); + setDataDefinedProperty( mYCoordinateComboBox, QgsPalLayerSettings::PositionY, lyr ); + setDataDefinedProperty( mHorizontalAlignmentComboBox, QgsPalLayerSettings::Hali, lyr ); + setDataDefinedProperty( mVerticalAlignmentComboBox, QgsPalLayerSettings::Vali, lyr ); return lyr; } @@ -308,6 +322,10 @@ comboList << mFontFamilyAttributeComboBox; comboList << mBufferSizeAttributeComboBox; comboList << mBufferColorAttributeComboBox; + comboList << mXCoordinateComboBox; + comboList << mYCoordinateComboBox; + comboList << mHorizontalAlignmentComboBox; + comboList << mVerticalAlignmentComboBox; QList::iterator comboIt = comboList.begin(); for ( ; comboIt != comboList.end(); ++comboIt ) @@ -335,6 +353,10 @@ setCurrentComboValue( mFontFamilyAttributeComboBox, s, QgsPalLayerSettings::Family ); setCurrentComboValue( mBufferSizeAttributeComboBox, s , QgsPalLayerSettings::BufferSize ); setCurrentComboValue( mBufferColorAttributeComboBox, s, QgsPalLayerSettings::BufferColor ); + setCurrentComboValue( mXCoordinateComboBox, s, QgsPalLayerSettings::PositionX ); + setCurrentComboValue( mYCoordinateComboBox, s, QgsPalLayerSettings::PositionY ); + setCurrentComboValue( mHorizontalAlignmentComboBox, s, QgsPalLayerSettings::Hali ); + setCurrentComboValue( mVerticalAlignmentComboBox, s, QgsPalLayerSettings::Vali ); } void QgsLabelingGui::changeTextColor() @@ -418,3 +440,41 @@ stackedOptions->setCurrentWidget( pageOptionsEmpty ); } } + +void QgsLabelingGui::on_mXCoordinateComboBox_currentIndexChanged( const QString & text ) +{ + if ( text.isEmpty() ) //no data defined alignment without data defined position + { + disableDataDefinedAlignment(); + } + else if ( !mYCoordinateComboBox->currentText().isEmpty() ) + { + enableDataDefinedAlignment(); + } +} + +void QgsLabelingGui::on_mYCoordinateComboBox_currentIndexChanged( const QString & text ) +{ + if ( text.isEmpty() ) //no data defined alignment without data defined position + { + disableDataDefinedAlignment(); + } + else if ( !mXCoordinateComboBox->currentText().isEmpty() ) + { + enableDataDefinedAlignment(); + } +} + +void QgsLabelingGui::disableDataDefinedAlignment() +{ + mHorizontalAlignmentComboBox->setCurrentIndex( mHorizontalAlignmentComboBox->findText( "" ) ); + mHorizontalAlignmentComboBox->setEnabled( false ); + mVerticalAlignmentComboBox->setCurrentIndex( mVerticalAlignmentComboBox->findText( "" ) ); + mVerticalAlignmentComboBox->setEnabled( false ); +} + +void QgsLabelingGui::enableDataDefinedAlignment() +{ + mHorizontalAlignmentComboBox->setEnabled( true ); + mVerticalAlignmentComboBox->setEnabled( true ); +} Index: src/core/qgspallabeling.cpp =================================================================== --- src/core/qgspallabeling.cpp (Revision 14234) +++ src/core/qgspallabeling.cpp (Arbeitskopie) @@ -132,6 +132,7 @@ minFeatureSize = 0.0; vectorScaleFactor = 1.0; rasterCompressFactor = 1.0; + addDirectionSymbol = false; } QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s ) @@ -156,6 +157,7 @@ minFeatureSize = s.minFeatureSize; vectorScaleFactor = s.vectorScaleFactor; rasterCompressFactor = s.rasterCompressFactor; + addDirectionSymbol = s.addDirectionSymbol; dataDefinedProperties = s.dataDefinedProperties; fontMetrics = NULL; @@ -193,7 +195,7 @@ return; } - for ( int i = 0; i < 9; ++i ) + for ( int i = 0; i < 13; ++i ) { QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator it = propertyMap.find(( QgsPalLayerSettings::DataDefinedProperties )i ); QVariant propertyValue; @@ -242,6 +244,10 @@ _readDataDefinedProperty( layer, QgsPalLayerSettings::Family, propertyMap ); _readDataDefinedProperty( layer, QgsPalLayerSettings::BufferSize, propertyMap ); _readDataDefinedProperty( layer, QgsPalLayerSettings::BufferColor, propertyMap ); + _readDataDefinedProperty( layer, QgsPalLayerSettings::PositionX, propertyMap ); + _readDataDefinedProperty( layer, QgsPalLayerSettings::PositionY, propertyMap ); + _readDataDefinedProperty( layer, QgsPalLayerSettings::Hali, propertyMap ); + _readDataDefinedProperty( layer, QgsPalLayerSettings::Vali, propertyMap ); } void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer ) @@ -269,6 +275,7 @@ labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool(); mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool(); multiLineLabels = layer->customProperty( "labeling/multiLineLabels" ).toBool(); + addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool(); minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble(); _readDataDefinedPropertyMap( layer, dataDefinedProperties ); } @@ -299,6 +306,7 @@ layer->setCustomProperty( "labeling/labelPerPart", labelPerPart ); layer->setCustomProperty( "labeling/mergeLines", mergeLines ); layer->setCustomProperty( "labeling/multiLineLabels", multiLineLabels ); + layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol ); layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize ); _writeDataDefinedPropertyMap( layer, dataDefinedProperties ); } @@ -358,6 +366,10 @@ return; } + if ( addDirectionSymbol && !multiLineLabels && placement == QgsPalLayerSettings::Line ) //consider the space needed for the direction symbol + { + text.append( ">" ); + } QRectF labelRect = fm->boundingRect( text ); double w, h; if ( !multiLineLabels ) @@ -391,12 +403,12 @@ { QString labelText = f.attributeMap()[fieldIndex].toString(); double labelX, labelY; // will receive label size + QFont labelFont = textFont; //data defined label size? QMap< DataDefinedProperties, int >::const_iterator it = dataDefinedProperties.find( QgsPalLayerSettings::Size ); if ( it != dataDefinedProperties.constEnd() ) { - QFont labelFont = textFont; //find out size QVariant size = f.attributeMap().value( *it ); if ( size.isValid() ) @@ -430,6 +442,69 @@ return; } + //data defined position / alignment? + bool dataDefinedPosition = false; + double xPos, yPos; + + QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX ); + if ( dPosXIt != dataDefinedProperties.constEnd() ) + { + QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY ); + if ( dPosYIt != dataDefinedProperties.constEnd() ) + { + //data defined position + dataDefinedPosition = true; + xPos = f.attributeMap().value( *dPosXIt ).toDouble(); + yPos = f.attributeMap().value( *dPosYIt ).toDouble(); + + //consider alignment settings (by adjusting xPos and yPos, which are lower/left in pal) + + //horizontal alignment + QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali ); + if ( haliIt != dataDefinedProperties.end() ) + { + QString haliString = f.attributeMap().value( *haliIt ).toString(); + if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 ) + { + xPos -= labelX / 2.0; + } + else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 ) + { + xPos -= labelX; + } + } + + //vertical alignment + QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali ); + if ( valiIt != dataDefinedProperties.constEnd() ) + { + QString valiString = f.attributeMap().value( *valiIt ).toString(); + if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 ) + { + if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 ) + { + yPos -= labelY; + } + else + { + QFontMetrics labelFontMetrics( labelFont ); + double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height(); + + if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 ) + { + yPos -= labelY * descentRatio; + } + else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 ) + { + yPos -= labelY * descentRatio; + yPos -= labelY * 0.5 * ( 1 - descentRatio ); + } + } + } + } + } + } + QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), labelText, GEOSGeom_clone( geos_geom ) ); // record the created geometry - it will be deleted at the end. @@ -438,7 +513,7 @@ // register feature to the layer try { - if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData() ) ) + if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(), xPos, yPos, dataDefinedPosition ) ) return; } catch ( std::exception* e ) @@ -871,6 +946,20 @@ QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text(); QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) ); + //add the direction symbol if needed + if ( !txt.isEmpty() && lyr.placement == QgsPalLayerSettings::Line && + lyr.addDirectionSymbol && !lyr.multiLineLabels ) + { + if ( label->getReversed() ) + { + txt.prepend( "<" ); + } + else + { + txt.append( ">" ); + } + } + //QgsDebugMsg( "drawLabel " + QString::number( drawBuffer ) + " " + txt ); QStringList multiLineList; Index: src/core/pal/feature.h =================================================================== --- src/core/pal/feature.h (Revision 14234) +++ src/core/pal/feature.h (Arbeitskopie) @@ -86,6 +86,9 @@ void setLabelInfo( LabelInfo* info ) { labelInfo = info; } void setDistLabel( double dist ) { distlabel = dist; } + //Set label position of the feature to fixed x/y values + void setFixedPosition( double x, double y ) { fixedPos = true; fixedPosX = x; fixedPosY = y;} + bool fixedPosition() const { return fixedPos; } protected: Layer *layer; @@ -97,6 +100,10 @@ char *uid; + bool fixedPos; //true in case of fixed position (only 1 candidate position with cost 0) + double fixedPosX; + double fixedPosY; + // array of parts - possibly not necessary //int nPart; //FeaturePart** parts; Index: src/core/pal/labelposition.cpp =================================================================== --- src/core/pal/labelposition.cpp (Revision 14234) +++ src/core/pal/labelposition.cpp (Arbeitskopie) @@ -54,8 +54,8 @@ namespace pal { - LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature ) - : id( id ), cost( cost ), feature( feature ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ) + LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed ) + : id( id ), cost( cost ), feature( feature ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed ) { // alpha take his value bw 0 and 2*pi rad Index: src/core/pal/labelposition.h =================================================================== --- src/core/pal/labelposition.h (Revision 14234) +++ src/core/pal/labelposition.h (Arbeitskopie) @@ -74,6 +74,11 @@ LabelPosition* nextPart; int partId; + //True if label direction is the same as line / polygon ring direction. + //Could be used by the application to draw a directional arrow ('<' or '>') + //if the layer arrangement is P_LINE + bool reversed; + bool isInConflictSinglePart( LabelPosition* lp ); bool isInConflictMultiPart( LabelPosition* lp ); @@ -93,7 +98,7 @@ LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, - FeaturePart *feature ); + FeaturePart *feature, bool isReversed = false ); /** copy constructor */ LabelPosition( const LabelPosition& other ); @@ -190,6 +195,7 @@ * \return alpha to rotate text (in rad) */ double getAlpha() const; + bool getReversed() const { return reversed; } void print(); Index: src/core/pal/layer.cpp =================================================================== --- src/core/pal/layer.cpp (Revision 14234) +++ src/core/pal/layer.cpp (Arbeitskopie) @@ -226,7 +226,8 @@ - bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText ) + bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText, + double labelPosX, double labelPosY, bool fixedPos ) { if ( !geom_id || label_x < 0 || label_y < 0 ) return false; @@ -243,6 +244,10 @@ GEOSGeometry *the_geom = userGeom->getGeosGeometry(); Feature* f = new Feature( this, geom_id, userGeom, label_x, label_y ); + if ( fixedPos ) + { + f->setFixedPosition( labelPosX, labelPosY ); + } bool first_feat = true; @@ -314,7 +319,7 @@ modMutex->unlock(); // if using only biggest parts... - if ( mode == LabelPerFeature && biggest_part != NULL ) + if (( mode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL ) { addFeaturePart( biggest_part, labelText ); first_feat = false; Index: src/core/pal/layer.h =================================================================== --- src/core/pal/layer.h (Revision 14234) +++ src/core/pal/layer.h (Arbeitskopie) @@ -283,12 +283,16 @@ * @param label_x label width * @param label_y label height * @param userGeom user's geometry that implements the PalGeometry interface + * @param labelPosX x position of the label (in case of fixed label position) + * @param labelPosY y position of the label (in case of fixed label position) + * @param fixedPos true if a single fixed position for this label is needed * * @throws PalException::FeatureExists * * @return true on success (i.e. valid geometry) */ - bool registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x = -1, double label_y = -1, const char* labelText = NULL ); + bool registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x = -1, double label_y = -1, + const char* labelText = NULL, double labelPosX = 0.0, double labelPosY = 0.0, bool fixedPos = false ); /** return pointer to feature or NULL if doesn't exist */ Feature* getFeature( const char* geom_id ); Index: src/core/pal/feature.cpp =================================================================== --- src/core/pal/feature.cpp (Revision 14234) +++ src/core/pal/feature.cpp (Arbeitskopie) @@ -61,7 +61,7 @@ namespace pal { Feature::Feature( Layer* l, const char* geom_id, PalGeometry* userG, double lx, double ly ) - : layer( l ), userGeom( userG ), label_x( lx ), label_y( ly ), distlabel( 0 ), labelInfo( NULL ) + : layer( l ), userGeom( userG ), label_x( lx ), label_y( ly ), distlabel( 0 ), labelInfo( NULL ), fixedPos( false ) { uid = new char[strlen( geom_id ) +1]; strcpy( uid, geom_id ); @@ -599,11 +599,11 @@ reversed = ( alpha >= M_PI / 2 || alpha < -M_PI / 2 ); if (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) ) - positions->push_back( new LabelPosition( i, bx + cos( beta ) *distlabel , by + sin( beta ) *distlabel, xrm, yrm, alpha, cost, this ) ); // Line + positions->push_back( new LabelPosition( i, bx + cos( beta ) *distlabel , by + sin( beta ) *distlabel, xrm, yrm, alpha, cost, this, reversed ) ); // Line if (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) ) - positions->push_back( new LabelPosition( i, bx - cos( beta ) *( distlabel + yrm ) , by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha, cost, this ) ); // Line + positions->push_back( new LabelPosition( i, bx - cos( beta ) *( distlabel + yrm ) , by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha, cost, this, reversed ) ); // Line if ( flags & FLAG_ON_LINE ) - positions->push_back( new LabelPosition( i, bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha, cost, this ) ); // Line + positions->push_back( new LabelPosition( i, bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha, cost, this, reversed ) ); // Line } else if ( f->layer->arrangement == P_HORIZ ) { @@ -1245,40 +1245,49 @@ double delta = bbox_max[0] - bbox_min[0]; - switch ( type ) + if ( f->fixedPosition() ) { - case GEOS_POINT: - if ( f->layer->getArrangement() == P_POINT_OVER ) - nbp = setPositionOverPoint( x[0], y[0], scale, lPos, delta ); - else - nbp = setPositionForPoint( x[0], y[0], scale, lPos, delta ); - break; - case GEOS_LINESTRING: - if ( f->layer->getArrangement() == P_CURVED ) - nbp = setPositionForLineCurved( lPos, mapShape ); - else - nbp = setPositionForLine( scale, lPos, mapShape, delta ); - break; + nbp = 1; + *lPos = new LabelPosition *[nbp]; + ( *lPos )[0] = new LabelPosition( 0, f->fixedPosX, f->fixedPosY, f->label_x, f->label_y, 0, 0.0, this ); + } + else + { + switch ( type ) + { + case GEOS_POINT: + if ( f->layer->getArrangement() == P_POINT_OVER ) + nbp = setPositionOverPoint( x[0], y[0], scale, lPos, delta ); + else + nbp = setPositionForPoint( x[0], y[0], scale, lPos, delta ); + break; + case GEOS_LINESTRING: + if ( f->layer->getArrangement() == P_CURVED ) + nbp = setPositionForLineCurved( lPos, mapShape ); + else + nbp = setPositionForLine( scale, lPos, mapShape, delta ); + break; - case GEOS_POLYGON: - switch ( f->layer->getArrangement() ) - { - case P_POINT: - case P_POINT_OVER: - double cx, cy; - mapShape->getCentroid( cx, cy ); - if ( f->layer->getArrangement() == P_POINT_OVER ) - nbp = setPositionOverPoint( cx, cy, scale, lPos, delta ); - else - nbp = setPositionForPoint( cx, cy, scale, lPos, delta ); - break; - case P_LINE: - nbp = setPositionForLine( scale, lPos, mapShape, delta ); - break; - default: - nbp = setPositionForPolygon( scale, lPos, mapShape, delta ); - break; - } + case GEOS_POLYGON: + switch ( f->layer->getArrangement() ) + { + case P_POINT: + case P_POINT_OVER: + double cx, cy; + mapShape->getCentroid( cx, cy ); + if ( f->layer->getArrangement() == P_POINT_OVER ) + nbp = setPositionOverPoint( cx, cy, scale, lPos, delta ); + else + nbp = setPositionForPoint( cx, cy, scale, lPos, delta ); + break; + case P_LINE: + nbp = setPositionForLine( scale, lPos, mapShape, delta ); + break; + default: + nbp = setPositionForPolygon( scale, lPos, mapShape, delta ); + break; + } + } } int rnbp = nbp; Index: src/core/qgspallabeling.h =================================================================== --- src/core/qgspallabeling.h (Revision 14234) +++ src/core/qgspallabeling.h (Arbeitskopie) @@ -83,6 +83,10 @@ Family, BufferSize, BufferColor, + PositionX, //x-coordinate data defined label position + PositionY, //y-coordinate data defined label position + Hali, //horizontal alignment for data defined label position (Left, Center, Right) + Vali //vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top) }; QString fieldName; @@ -103,6 +107,9 @@ bool mergeLines; bool multiLineLabels; //draw labels on multiple lines if they contain '\n' double minFeatureSize; // minimum feature size to be labelled (in mm) + // Adds '<' or '>' to the label string pointing to the direction of the line / polygon ring + // Works only if Placement == Line + bool addDirectionSymbol; // called from register feature hook void calculateLabelSize( const QFontMetrics* fm, QString text, double& labelX, double& labelY ); Index: src/ui/qgslabelingguibase.ui =================================================================== --- src/ui/qgslabelingguibase.ui (Revision 14234) +++ src/ui/qgslabelingguibase.ui (Arbeitskopie) @@ -678,7 +678,7 @@ - + @@ -696,7 +696,7 @@ - + @@ -733,13 +733,20 @@ + + + + add direction symbol + + + Data defined settings - + @@ -753,79 +760,75 @@ - - - - - Size - - - - - - - - - - Bold - - - - - - - - - - - - - Color - - - - - - - - - - Italic - - - - - - - - - - Underline - - - - - - - - - - Font family - - - - - - - - - - Strikeout - - - - + + + Size + + + + + + + + + Color + + + + + + + + + + Bold + + + + + + + + + + Italic + + + + + + + + + + Underline + + + + + + + + + + Strikeout + + + + + + + + + + Font family + + + + + + @@ -834,35 +837,80 @@ Buffer properties - + - - - - - Buffer size - - - - - - - - - - Buffer color - - - - - - - + + + Buffer size + + + + + + + + + Buffer color + + + + + + + + + Position + + + + + + X Coordinate + + + + + + + + + + Y Coordinate + + + + + + + + + + Horizontal alignment + + + + + + + + + + Vertical alignment + + + + + + + + + + Qt::Vertical