Skip to content
Permalink
Browse files

Add user notification when defined labeling font is not found on syst…

…em, then substituted

- [API] add signal to QgsVectorLayer that is emitted when its labeling font is not found
- Message bar notification on first rendering of layer offers link to open layer's labeling dialog
- Add 'font not found' notification to labeling dialog
- Labeling dialog defaults to 'Text style' section when font is not found, to show notice
- Substituted font is not saved with project, unless user applies changes.
- Change labeling gui font selector dialog to font family combobox (since only family is being chosen)
  • Loading branch information
dakcarto committed Jun 10, 2013
1 parent 44d89a4 commit 430f0fcb1bf44773be10bc463b6f105cfd5a0397
@@ -222,7 +222,9 @@ class QgsPalLayerSettings
QgsExpression* getLabelExpression();

QFont textFont;
//QString textFontFamily;
QString textNamedStyle;
//bool textFontFound;
bool fontSizeInMapUnits; //true if font size is in map units (otherwise in points)
QColor textColor;
int textTransp;
@@ -1047,6 +1047,11 @@ class QgsVectorLayer : QgsMapLayer
void committedAttributeValuesChanges( const QString& layerId, const QgsChangedAttributesMap& changedAttributesValues );
void committedGeometriesChanges( const QString& layerId, const QgsGeometryMap& changedGeometries );

/** Emitted when the font family defined for labeling layer is not found on system
* @note added in 1.9
*/
void labelingFontNotFound( QgsVectorLayer* layer, const QString& fontfamily );

protected:
/** Set the extent */
void setExtent( const QgsRectangle &rect );
@@ -4279,6 +4279,63 @@ void QgisApp::modifyAnnotation()
mMapCanvas->setMapTool( mMapTools.mAnnotation );
}

void QgisApp::labelingFontNotFound( QgsVectorLayer* vlayer, const QString& fontfamily )
{
// TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
QString substitute = tr( "Default system font substituted." );

QWidget* fontMsg = QgsMessageBar::createMessage(
tr( "Labeling" ),
tr( "Font for layer <b><u>%1</u></b> was not found (<i>%2</i>). %3" ).arg( vlayer->name() ).arg( fontfamily ).arg( substitute ),
QgsApplication::getThemeIcon( "/mIconWarn.png" ),
messageBar() );

QToolButton* btnOpenPrefs = new QToolButton( fontMsg );
btnOpenPrefs->setStyleSheet( "QToolButton{ background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline; }" );
btnOpenPrefs->setCursor( Qt::PointingHandCursor );
btnOpenPrefs->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
btnOpenPrefs->setToolButtonStyle( Qt::ToolButtonTextOnly );

// store pointer to vlayer in data of QAction
QAction* act = new QAction( fontMsg );
act->setData( QVariant( QMetaType::QObjectStar, &vlayer ) );
act->setText( tr( "Open labeling dialog" ) );
btnOpenPrefs->addAction( act );
btnOpenPrefs->setDefaultAction( act );
btnOpenPrefs->setToolTip( "" );

connect( btnOpenPrefs, SIGNAL( triggered( QAction* ) ), this, SLOT( labelingDialogFontNotFound( QAction* ) ) );
fontMsg->layout()->addWidget( btnOpenPrefs );

// no timeout set, since notice needs attention and is only shown first time layer is labeled
messageBar()->pushWidget( fontMsg, QgsMessageBar::WARNING );
}

void QgisApp::labelingDialogFontNotFound( QAction* act )
{
if ( !act )
{
return;
}

// get base pointer to layer
QObject* obj = qvariant_cast<QObject*>( act->data() );

// remove calling messagebar widget
messageBar()->popWidget();

if ( !obj )
{
return;
}

QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( obj );
if ( layer && setActiveLayer( layer ) )
{
labeling();
}
}

void QgisApp::labeling()
{
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer*>( activeLayer() );
@@ -7658,6 +7715,9 @@ void QgisApp::layersWereAdded( QList<QgsMapLayer *> theLayers )
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vlayer )
{
// notify user about any font family substitution, but only when rendering labels (i.e. not when opening settings dialog)
connect( vlayer, SIGNAL( labelingFontNotFound( QgsVectorLayer*, QString ) ), this, SLOT( labelingFontNotFound( QgsVectorLayer*, QString ) ) );

QgsVectorDataProvider* vProvider = vlayer->dataProvider();
if ( vProvider && vProvider->capabilities() & QgsVectorDataProvider::EditingCapabilities )
{
@@ -1020,6 +1020,16 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
void addSvgAnnotation();
void modifyAnnotation();

/** Alerts user when labeling font for layer has not been found on system
* @note added in 1.9
*/
void labelingFontNotFound( QgsVectorLayer* vlayer, const QString& fontfamily );

/** Opens the labeling dialog for a layer when called from labelingFontNotFound alert
* @note added in 1.9
*/
void labelingDialogFontNotFound( QAction* act );

//! shows label settings dialog (for labeling-ng)
void labeling();

@@ -73,7 +73,6 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM

// preview and basic option connections
connect( btnTextColor, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( changeTextColor( const QColor& ) ) );
connect( btnChangeFont, SIGNAL( clicked() ), this, SLOT( changeTextFont() ) );
connect( mFontTranspSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( updatePreview() ) );
connect( mBufferDrawChkBx, SIGNAL( toggled( bool ) ), this, SLOT( updatePreview() ) );
connect( btnBufferColor, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( changeBufferColor( const QColor& ) ) );
@@ -366,6 +365,22 @@ void QgsLabelingGui::init()
QgsFontUtils::updateFontViaStyle( mRefFont, lyr.textNamedStyle );
updateFont( mRefFont );

// show 'font not found' if substitution has occurred (should come after updateFont())
if ( !lyr.mTextFontFound )
{
mFontMissingLabel->setVisible( true );
QString missingTxt = tr( "%1 not found. Default substituted." );
QString txtPrepend = tr( "Chosen font" );
if ( !lyr.mTextFontFamily.isEmpty() )
{
txtPrepend = QString( "'%1'" ).arg( lyr.mTextFontFamily );
}
mFontMissingLabel->setText( missingTxt.arg( txtPrepend ) );

// ensure user is sent to 'Text style' section to see notice
mLabelingOptionsListWidget->setCurrentRow( 0 );
}

// shape background
mShapeDrawChkBx->setChecked( lyr.shapeDraw );
mShapeTypeCmbBx->blockSignals( true );
@@ -474,6 +489,7 @@ void QgsLabelingGui::collapseSample( bool collapse )
void QgsLabelingGui::apply()
{
writeSettingsToLayer();
mFontMissingLabel->setVisible( false );
QgisApp::instance()->markDirty();
// trigger refresh
if ( mMapCanvas )
@@ -1019,41 +1035,6 @@ void QgsLabelingGui::changeTextColor( const QColor &color )
updatePreview();
}

void QgsLabelingGui::changeTextFont()
{
// store properties of QFont that might be stripped by font dialog
QFont::Capitalization captials = mRefFont.capitalization();
double wordspacing = mRefFont.wordSpacing();
double letterspacing = mRefFont.letterSpacing();

bool ok;
#if defined(Q_WS_MAC) && QT_VERSION >= 0x040500 && defined(QT_MAC_USE_COCOA)
// Native Mac dialog works only for Qt Carbon
QFont font = QFontDialog::getFont( &ok, mRefFont, 0, QString(), QFontDialog::DontUseNativeDialog );
#else
QFont font = QFontDialog::getFont( &ok, mRefFont );
#endif
if ( ok )
{
if ( mFontSizeUnitComboBox->currentIndex() == 1 )
{
// don't override map units size with selected size from font dialog
font.setPointSizeF( mFontSizeSpinBox->value() );
}
else
{
mFontSizeSpinBox->setValue( font.pointSizeF() );
}

// reassign possibly stripped QFont properties
font.setCapitalization( captials );
font.setWordSpacing( wordspacing );
font.setLetterSpacing( QFont::AbsoluteSpacing, letterspacing );

updateFont( font );
}
}

void QgsLabelingGui::updateFont( QFont font )
{
// update background reference font
@@ -1063,31 +1044,13 @@ void QgsLabelingGui::updateFont( QFont font )
}

// test if font is actually available
QString missingtxt = QString( "" );
bool missing = false;
if ( QApplication::font().toString() != mRefFont.toString() )
{
QFont testfont = mFontDB.font( mRefFont.family(), mFontDB.styleString( mRefFont ), 12 );
if ( QApplication::font().toString() == testfont.toString() )
{
missing = true;
}
}
if ( missing )
{
missingtxt = tr( " (not found!)" );
lblFontName->setStyleSheet( "color: #990000;" );
}
else
{
lblFontName->setStyleSheet( "color: #000000;" );
}
mFontMissingLabel->setVisible( QgsFontUtils::fontMatchOnSystem( mRefFont ) );

lblFontName->setText( QString( "%1%2" ).arg( mRefFont.family() ).arg( missingtxt ) );
mDirectSymbLeftLineEdit->setFont( mRefFont );
mDirectSymbRightLineEdit->setFont( mRefFont );

blockFontChangeSignals( true );
mFontFamilyCmbBx->setCurrentFont( mRefFont );
populateFontStyleComboBox();
int idx = mFontCapitalsComboBox->findData( QVariant(( unsigned int ) mRefFont.capitalization() ) );
mFontCapitalsComboBox->setCurrentIndex( idx == -1 ? 0 : idx );
@@ -1097,13 +1060,13 @@ void QgsLabelingGui::updateFont( QFont font )

// update font name with font face
// font.setPixelSize( 24 );
// lblFontName->setFont( QFont( font ) );

updatePreview();
}

void QgsLabelingGui::blockFontChangeSignals( bool blk )
{
mFontFamilyCmbBx->blockSignals( blk );
mFontStyleComboBox->blockSignals( blk );
mFontCapitalsComboBox->blockSignals( blk );
mFontUnderlineBtn->blockSignals( blk );
@@ -1333,7 +1296,15 @@ void QgsLabelingGui::populateFontStyleComboBox()
{
mFontStyleComboBox->addItem( style );
}
mFontStyleComboBox->setCurrentIndex( mFontStyleComboBox->findText( mFontDB.styleString( mRefFont ) ) );

int curIndx = 0;
int stylIndx = mFontStyleComboBox->findText( mFontDB.styleString( mRefFont ) );
if ( stylIndx > -1 )
{
curIndx = stylIndx;
}

mFontStyleComboBox->setCurrentIndex( curIndx );
}

void QgsLabelingGui::on_mPreviewSizeSlider_valueChanged( int i )
@@ -1355,6 +1326,12 @@ void QgsLabelingGui::on_mFontCapitalsComboBox_currentIndexChanged( int index )
updateFont( mRefFont );
}

void QgsLabelingGui::on_mFontFamilyCmbBx_currentFontChanged( const QFont& f )
{
mRefFont.setFamily( f.family() );
updateFont( mRefFont );
}

void QgsLabelingGui::on_mFontStyleComboBox_currentIndexChanged( const QString & text )
{
QgsFontUtils::updateFontViaStyle( mRefFont, text );
@@ -44,7 +44,6 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void collapseSample( bool collapse );
void apply();
void changeTextColor( const QColor &color );
void changeTextFont();
void showEngineConfigDialog();
void showExpressionDialog();
void changeBufferColor( const QColor &color );
@@ -58,6 +57,7 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void on_mPreviewSizeSlider_valueChanged( int i );
void on_mFontSizeSpinBox_valueChanged( double d );
void on_mFontCapitalsComboBox_currentIndexChanged( int index );
void on_mFontFamilyCmbBx_currentFontChanged( const QFont& f );
void on_mFontStyleComboBox_currentIndexChanged( const QString & text );
void on_mFontUnderlineBtn_toggled( bool ckd );
void on_mFontStrikethroughBtn_toggled( bool ckd );
@@ -212,12 +212,15 @@ QgsPalLayerSettings::QgsPalLayerSettings()

// text style
textFont = QApplication::font();
fontSizeInMapUnits = false;
textNamedStyle = QString( "" );
fontSizeInMapUnits = false;
textColor = Qt::black;
textTransp = 0;
blendMode = QPainter::CompositionMode_SourceOver;
previewBkgrdColor = Qt::white;
// font processing info
mTextFontFound = true;
mTextFontFamily = QApplication::font().family();

// text formatting
wrapChar = "";
@@ -432,12 +435,15 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
fieldName = s.fieldName;
isExpression = s.isExpression;
textFont = s.textFont;
fontSizeInMapUnits = s.fontSizeInMapUnits;
textNamedStyle = s.textNamedStyle;
fontSizeInMapUnits = s.fontSizeInMapUnits;
textColor = s.textColor;
textTransp = s.textTransp;
blendMode = s.blendMode;
previewBkgrdColor = s.previewBkgrdColor;
// font processing info
mTextFontFound = s.mTextFontFound;
mTextFontFamily = s.mTextFontFamily;

// text formatting
wrapChar = s.wrapChar;
@@ -796,7 +802,21 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
// text style
fieldName = layer->customProperty( "labeling/fieldName" ).toString();
isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
QString fontFamily = layer->customProperty( "labeling/fontFamily", QVariant( QApplication::font().family() ) ).toString();
QFont appFont = QApplication::font();
mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
QString fontFamily = mTextFontFamily;
if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
{
// trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
mTextFontFound = false;

// TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
// currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)

// for now, do not use matching algorithm for substitution if family not found, substitute default instead
fontFamily = appFont.family();
}

double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
@@ -492,6 +492,7 @@ class CORE_EXPORT QgsPalLayerSettings
QMap<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > dataDefinedNames() const { return mDataDefinedNames; }

// temporary stuff: set when layer gets prepared or labeled
// NOTE: not in Python binding
pal::Layer* palLayer;
QgsFeature* mCurFeat;
const QgsFields* mCurFields;
@@ -505,6 +506,9 @@ class CORE_EXPORT QgsPalLayerSettings
int mFeatsSendingToPal; // total features tested for sending into PAL (relative to maxNumLabels)
int mFeatsRegPal; // number of features registered in PAL, when using limitNumLabels

QString mTextFontFamily;
bool mTextFontFound;

bool showingShadowRects; // whether to show debug rectangles for drop shadows

private:
@@ -122,6 +122,7 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
, mRendererV2( NULL )
, mLabel( 0 )
, mLabelOn( false )
, mLabelFontNotFoundNotified( false )
, mFeatureBlendMode( QPainter::CompositionMode_SourceOver ) // Default to normal feature blending
, mLayerTransparency( 0 )
, mVertexMarkerOnlyForSelection( false )
@@ -3477,8 +3478,9 @@ void QgsVectorLayer::prepareLabelingAndDiagrams( QgsRenderContext& rendererConte

if ( labeling )
{
// see if feature count limit is set for labeling
QgsPalLayerSettings& palyr = rendererContext.labelingEngine()->layer( this->id() );

// see if feature count limit is set for labeling
if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 )
{
QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
@@ -3494,6 +3496,13 @@ void QgsVectorLayer::prepareLabelingAndDiagrams( QgsRenderContext& rendererConte
}
palyr.mFeaturesToLabel = nFeatsToLabel;
}

// notify user about any font substitution
if ( !palyr.mTextFontFound && !mLabelFontNotFoundNotified )
{
emit labelingFontNotFound( this, palyr.mTextFontFamily );
mLabelFontNotFoundNotified = true;
}
}

//register diagram layers

0 comments on commit 430f0fc

Please sign in to comment.
You can’t perform that action at this time.