Skip to content

Commit

Permalink
Add user notification when defined labeling font is not found on syst…
Browse files Browse the repository at this point in the history
…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 430f0fc
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 159 deletions.
2 changes: 2 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -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 );
Expand Down
60 changes: 60 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -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() );
Expand Down Expand Up @@ -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 )
{
Expand Down
10 changes: 10 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -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();

Expand Down
93 changes: 35 additions & 58 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -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& ) ) );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -474,6 +489,7 @@ void QgsLabelingGui::collapseSample( bool collapse )
void QgsLabelingGui::apply()
{
writeSettingsToLayer();
mFontMissingLabel->setVisible( false );
QgisApp::instance()->markDirty();
// trigger refresh
if ( mMapCanvas )
Expand Down Expand Up @@ -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
Expand All @@ -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 );
Expand All @@ -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 );
Expand Down Expand Up @@ -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 )
Expand All @@ -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 );
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgslabelinggui.h
Expand Up @@ -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 );
Expand All @@ -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 );
Expand Down
26 changes: 23 additions & 3 deletions src/core/qgspallabeling.cpp
Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions src/core/qgspallabeling.h
Expand Up @@ -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;
Expand All @@ -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:
Expand Down
11 changes: 10 additions & 1 deletion src/core/qgsvectorlayer.cpp
Expand Up @@ -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 )
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down

0 comments on commit 430f0fc

Please sign in to comment.