summaryrefslogtreecommitdiffstats
path: root/src/gui/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/text')
-rw-r--r--src/gui/text/qfontengine_coretext.mm49
-rw-r--r--src/gui/text/qfontengine_coretext_p.h5
-rw-r--r--src/gui/text/qfontengine_ft.cpp35
-rw-r--r--src/gui/text/qfontengine_ft_p.h6
-rw-r--r--src/gui/text/qfontengine_mac.mm2
-rw-r--r--src/gui/text/qfontengine_mac_p.h2
-rw-r--r--src/gui/text/qfontengine_p.h2
-rw-r--r--src/gui/text/qfontengine_win.cpp17
-rw-r--r--src/gui/text/qfontengine_win_p.h3
-rw-r--r--src/gui/text/qfontengine_x11.cpp14
-rw-r--r--src/gui/text/qfontengine_x11_p.h2
-rw-r--r--src/gui/text/qfontenginedirectwrite.cpp11
-rw-r--r--src/gui/text/qfontenginedirectwrite_p.h2
-rw-r--r--src/gui/text/qglyphs.cpp6
-rw-r--r--src/gui/text/qplatformfontdatabase_qpa.cpp10
-rw-r--r--src/gui/text/qplatformfontdatabase_qpa.h2
-rw-r--r--src/gui/text/qrawfont.cpp13
-rw-r--r--src/gui/text/qrawfont_ft.cpp51
-rw-r--r--src/gui/text/qrawfont_mac.cpp22
-rw-r--r--src/gui/text/qrawfont_p.h4
-rw-r--r--src/gui/text/qrawfont_qpa.cpp69
-rw-r--r--src/gui/text/qrawfont_win.cpp47
-rw-r--r--src/gui/text/qtextcursor.cpp42
-rw-r--r--src/gui/text/qtextcursor.h4
-rw-r--r--src/gui/text/qtextdocument.cpp23
-rw-r--r--src/gui/text/qtextdocument.h5
-rw-r--r--src/gui/text/qtextdocument_p.cpp15
-rw-r--r--src/gui/text/qtextdocument_p.h4
-rw-r--r--src/gui/text/qtextengine.cpp339
-rw-r--r--src/gui/text/qtextengine_mac.cpp4
-rw-r--r--src/gui/text/qtextengine_p.h60
-rw-r--r--src/gui/text/qtextlayout.cpp395
-rw-r--r--src/gui/text/qtextlayout.h6
-rw-r--r--src/gui/text/text.pri6
34 files changed, 871 insertions, 406 deletions
diff --git a/src/gui/text/qfontengine_coretext.mm b/src/gui/text/qfontengine_coretext.mm
index 20b3730..d4df218 100644
--- a/src/gui/text/qfontengine_coretext.mm
+++ b/src/gui/text/qfontengine_coretext.mm
@@ -162,8 +162,10 @@ uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef font) const
return engines.count() - 1;
}
-bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
- unsigned short *logClusters, const HB_CharAttributes *) const
+bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *,
+ QScriptItem *si) const
{
QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0,
reinterpret_cast<const UniChar *>(str),
@@ -180,6 +182,8 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
typeSetter = CTTypesetterCreateWithAttributedStringAndOptions(attributedString, options);
} else
+#else
+ Q_UNUSED(flags);
#endif
typeSetter = CTTypesetterCreateWithAttributedString(attributedString);
@@ -219,6 +223,25 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay
Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl);
CFRange stringRange = CTRunGetStringRange(run);
+ int prepend = 0;
+#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5
+ UniChar beginGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location);
+ QChar dir = QChar::direction(beginGlyph);
+ bool beginWithOverride = dir == QChar::DirLRO || dir == QChar::DirRLO || dir == QChar::DirLRE || dir == QChar::DirRLE;
+ if (beginWithOverride) {
+ logClusters[stringRange.location] = 0;
+ outGlyphs[0] = 0xFFFF;
+ outAdvances_x[0] = 0;
+ outAdvances_y[0] = 0;
+ outAttributes[0].clusterStart = true;
+ outAttributes[0].dontPrint = true;
+ outGlyphs++;
+ outAdvances_x++;
+ outAdvances_y++;
+ outAttributes++;
+ prepend = 1;
+ }
+#endif
UniChar endGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location + stringRange.length - 1);
bool endWithPDF = QChar::direction(endGlyph) == QChar::DirPDF;
if (endWithPDF)
@@ -233,7 +256,12 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay
if (!runAttribs)
runAttribs = attributeDict;
CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName));
- const uint fontIndex = (fontIndexForFont(runFont) << 24);
+ uint fontIndex = fontIndexForFont(runFont);
+ const QFontEngine *engine = engineAt(fontIndex);
+ fontIndex <<= 24;
+ si->ascent = qMax(engine->ascent(), si->ascent);
+ si->descent = qMax(engine->descent(), si->descent);
+ si->leading = qMax(engine->leading(), si->leading);
//NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont));
if (endWithPDF)
glyphCount--;
@@ -271,9 +299,9 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay
CFIndex k = 0;
CFIndex i = 0;
- for (i = stringRange.location;
+ for (i = stringRange.location + prepend;
(i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) {
- if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location) {
+ if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location + prepend) {
logClusters[i] = k + firstGlyphIndex;
outAttributes[k].clusterStart = true;
++k;
@@ -308,7 +336,7 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay
: QFixed::fromReal(lastGlyphAdvance.width);
if (endWithPDF) {
- logClusters[stringRange.location + stringRange.length - 1] = glyphCount;
+ logClusters[stringRange.location + stringRange.length - 1] = glyphCount + prepend;
outGlyphs[glyphCount] = 0xFFFF;
outAdvances_x[glyphCount] = 0;
outAdvances_y[glyphCount] = 0;
@@ -837,6 +865,15 @@ QFixed QCoreTextFontEngine::emSquareSize() const
return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont)));
}
+QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef newFontDef = fontDef;
+ newFontDef.pixelSize = pixelSize;
+ newFontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi();
+
+ return new QCoreTextFontEngine(cgFont, fontDef);
+}
+
QT_END_NAMESPACE
#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
diff --git a/src/gui/text/qfontengine_coretext_p.h b/src/gui/text/qfontengine_coretext_p.h
index 1503c3f..bb80a9b 100644
--- a/src/gui/text/qfontengine_coretext_p.h
+++ b/src/gui/text/qfontengine_coretext_p.h
@@ -91,6 +91,8 @@ public:
virtual qreal minLeftBearing() const;
virtual QFixed emSquareSize() const;
+ virtual QFontEngine *cloneWithSize(qreal pixelSize) const;
+
private:
friend class QRawFontPrivate;
@@ -118,7 +120,8 @@ public:
QTextEngine::ShaperFlags flags) const;
bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
QTextEngine::ShaperFlags flags,
- unsigned short *logClusters, const HB_CharAttributes *charAttributes) const;
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes,
+ QScriptItem *si) const;
virtual const char *name() const { return "CoreText"; }
protected:
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
index 8f2da9b..58bcca8 100644
--- a/src/gui/text/qfontengine_ft.cpp
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -2069,6 +2069,41 @@ HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 p
return result;
}
+bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe)
+{
+ if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
+ return false;
+
+ // Increase the reference of this QFreetypeFace since one more QFontEngineFT
+ // will be using it
+ freetype->ref.ref();
+
+ default_load_flags = fe->default_load_flags;
+ default_hint_style = fe->default_hint_style;
+ antialias = fe->antialias;
+ transform = fe->transform;
+ embolden = fe->embolden;
+ subpixelType = fe->subpixelType;
+ lcdFilterType = fe->lcdFilterType;
+ canUploadGlyphsToServer = fe->canUploadGlyphsToServer;
+ embeddedbitmap = fe->embeddedbitmap;
+
+ return true;
+}
+
+QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef fontDef;
+ fontDef.pixelSize = pixelSize;
+ QFontEngineFT *fe = new QFontEngineFT(fontDef);
+ if (!fe->initFromFontEngine(this)) {
+ delete fe;
+ return 0;
+ } else {
+ return fe;
+ }
+}
+
QT_END_NAMESPACE
#endif // QT_NO_FREETYPE
diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h
index 887efed..6dbca7a 100644
--- a/src/gui/text/qfontengine_ft_p.h
+++ b/src/gui/text/qfontengine_ft_p.h
@@ -122,7 +122,7 @@ struct QFreetypeFace
static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool = false);
private:
- friend class QFontEngineFTRawFont;
+ friend class QFontEngineFT;
friend class QScopedPointerDeleter<QFreetypeFace>;
QFreetypeFace() : _lock(QMutex::Recursive) {}
~QFreetypeFace() {}
@@ -319,6 +319,10 @@ private:
};
void setDefaultHintStyle(HintStyle style);
+
+ virtual QFontEngine *cloneWithSize(qreal pixelSize) const;
+ bool initFromFontEngine(const QFontEngineFT *fontEngine);
+
HintStyle defaultHintStyle() const { return default_hint_style; }
protected:
diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm
index 673a7c8..9f094ad 100644
--- a/src/gui/text/qfontengine_mac.mm
+++ b/src/gui/text/qfontengine_mac.mm
@@ -377,7 +377,7 @@ bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *
}
bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
- unsigned short *logClusters, const HB_CharAttributes *charAttributes) const
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const
{
if (*nglyphs < len) {
*nglyphs = len;
diff --git a/src/gui/text/qfontengine_mac_p.h b/src/gui/text/qfontengine_mac_p.h
index 385fa83..292ea98 100644
--- a/src/gui/text/qfontengine_mac_p.h
+++ b/src/gui/text/qfontengine_mac_p.h
@@ -131,7 +131,7 @@ public:
virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
- unsigned short *logClusters, const HB_CharAttributes *charAttributes) const;
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const;
virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const;
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index 5b39fd3..6db0aa6 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -235,6 +235,8 @@ public:
virtual int glyphCount() const;
+ virtual QFontEngine *cloneWithSize(qreal /*pixelSize*/) const { return 0; }
+
HB_Font harfbuzzFont() const;
HB_Face harfbuzzFace() const;
diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp
index 82d9da0..54d7ec2 100644
--- a/src/gui/text/qfontengine_win.cpp
+++ b/src/gui/text/qfontengine_win.cpp
@@ -1284,6 +1284,23 @@ QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, co
return rgbMask;
}
+// From qfontdatabase_win.cpp
+extern QFontEngine *qt_load_font_engine_win(const QFontDef &request);
+QFontEngine *QFontEngineWin::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef request = fontDef;
+ QString actualFontName = request.family;
+ if (!uniqueFamilyName.isEmpty())
+ request.family = uniqueFamilyName;
+ request.pixelSize = pixelSize;
+
+ QFontEngine *fontEngine = qt_load_font_engine_win(request);
+ if (fontEngine != NULL)
+ fontEngine->fontDef.family = actualFontName;
+
+ return fontEngine;
+}
+
// -------------------------------------- Multi font engine
QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks)
diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h
index 28d8000..114149d 100644
--- a/src/gui/text/qfontengine_win_p.h
+++ b/src/gui/text/qfontengine_win_p.h
@@ -106,6 +106,8 @@ public:
virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform);
virtual QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform);
+ virtual QFontEngine *cloneWithSize(qreal pixelSize) const;
+
#ifndef Q_CC_MINGW
virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0);
#endif
@@ -118,6 +120,7 @@ public:
#endif
QString _name;
+ QString uniqueFamilyName;
HFONT hfont;
LOGFONT logfont;
uint stockFont : 1;
diff --git a/src/gui/text/qfontengine_x11.cpp b/src/gui/text/qfontengine_x11.cpp
index 9f3f8d3..4260b85 100644
--- a/src/gui/text/qfontengine_x11.cpp
+++ b/src/gui/text/qfontengine_x11.cpp
@@ -1196,6 +1196,20 @@ bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *
#endif
}
+QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef fontDef;
+ fontDef.pixelSize = pixelSize;
+ QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef);
+ if (!fe->initFromFontEngine(this)) {
+ delete fe;
+ return 0;
+ } else {
+ fe->xglyph_format = xglyph_format;
+ return fe;
+ }
+}
+
#endif // QT_NO_FONTCONFIG
QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_x11_p.h b/src/gui/text/qfontengine_x11_p.h
index ad68fac..d7eb39d 100644
--- a/src/gui/text/qfontengine_x11_p.h
+++ b/src/gui/text/qfontengine_x11_p.h
@@ -161,6 +161,8 @@ public:
explicit QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen);
~QFontEngineX11FT();
+ QFontEngine *cloneWithSize(qreal pixelSize) const;
+
#ifndef QT_NO_XRENDER
int xglyph_format;
#endif
diff --git a/src/gui/text/qfontenginedirectwrite.cpp b/src/gui/text/qfontenginedirectwrite.cpp
index f0a3644..aab00c0 100644
--- a/src/gui/text/qfontenginedirectwrite.cpp
+++ b/src/gui/text/qfontenginedirectwrite.cpp
@@ -630,6 +630,17 @@ QFontEngine::Type QFontEngineDirectWrite::type() const
return QFontEngine::DirectWrite;
}
+QFontEngine *QFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
+{
+ QFontEngine *fontEngine = new QFontEngineDirectWrite(m_directWriteFactory, m_directWriteFontFace,
+ pixelSize);
+
+ fontEngine->fontDef = fontDef;
+ fontEngine->fontDef.pixelSize = pixelSize;
+
+ return fontEngine;
+}
+
QT_END_NAMESPACE
#endif // QT_NO_DIRECTWRITE
diff --git a/src/gui/text/qfontenginedirectwrite_p.h b/src/gui/text/qfontenginedirectwrite_p.h
index c440a6c..53a4b0a 100644
--- a/src/gui/text/qfontenginedirectwrite_p.h
+++ b/src/gui/text/qfontenginedirectwrite_p.h
@@ -101,6 +101,8 @@ public:
QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin,
const QTransform &xform);
+ QFontEngine *cloneWithSize(qreal pixelSize) const;
+
bool canRender(const QChar *string, int len);
Type type() const;
diff --git a/src/gui/text/qglyphs.cpp b/src/gui/text/qglyphs.cpp
index b8a418d..cfea6ec 100644
--- a/src/gui/text/qglyphs.cpp
+++ b/src/gui/text/qglyphs.cpp
@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QGlyphs
- \brief the QGlyphs class provides direct access to the internal glyphs in a font
+ \brief The QGlyphs class provides direct access to the internal glyphs in a font.
\since 4.8
\ingroup text
@@ -76,8 +76,8 @@ QT_BEGIN_NAMESPACE
QTextLayout::glyphs() or QTextFragment::glyphs() can be used to convert unicode encoded text
into a list of QGlyphs objects, and QPainter::drawGlyphs() can be used to draw the glyphs.
- \note Please note that QRawFont is considered local to the thread in which it is constructed,
- which in turn means that a new QRawFont will have to be created and set on the QGlyphs if it is
+ \note Please note that QRawFont is considered local to the thread in which it is constructed.
+ This in turn means that a new QRawFont will have to be created and set on the QGlyphs if it is
moved to a different thread. If the QGlyphs contains a reference to a QRawFont from a different
thread than the current, it will not be possible to draw the glyphs using a QPainter, as the
QRawFont is considered invalid and inaccessible in this case.
diff --git a/src/gui/text/qplatformfontdatabase_qpa.cpp b/src/gui/text/qplatformfontdatabase_qpa.cpp
index 6fa25e7..82ec279 100644
--- a/src/gui/text/qplatformfontdatabase_qpa.cpp
+++ b/src/gui/text/qplatformfontdatabase_qpa.cpp
@@ -218,6 +218,16 @@ QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, QUnicode
return engine;
}
+QFontEngine *QPlatformFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
+ QFont::HintingPreference hintingPreference)
+{
+ Q_UNUSED(fontData);
+ Q_UNUSED(pixelSize);
+ Q_UNUSED(hintingPreference);
+ qWarning("This plugin does not support font engines created directly from font data");
+ return 0;
+}
+
/*!
*/
diff --git a/src/gui/text/qplatformfontdatabase_qpa.h b/src/gui/text/qplatformfontdatabase_qpa.h
index e0e4f04..046311f 100644
--- a/src/gui/text/qplatformfontdatabase_qpa.h
+++ b/src/gui/text/qplatformfontdatabase_qpa.h
@@ -92,6 +92,8 @@ public:
virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName);
virtual void releaseHandle(void *handle);
+ virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
+
virtual QString fontDir() const;
//callback
diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp
index 4a715c2..6ac2677 100644
--- a/src/gui/text/qrawfont.cpp
+++ b/src/gui/text/qrawfont.cpp
@@ -579,8 +579,19 @@ QRawFont QRawFont::fromFont(const QFont &font, QFontDatabase::WritingSystem writ
*/
void QRawFont::setPixelSize(int pixelSize)
{
+ if (d->fontEngine == 0)
+ return;
+
detach();
- d->platformSetPixelSize(pixelSize);
+ QFontEngine *oldFontEngine = d->fontEngine;
+
+ d->fontEngine = d->fontEngine->cloneWithSize(pixelSize);
+ if (d->fontEngine != 0)
+ d->fontEngine->ref.ref();
+
+ oldFontEngine->ref.deref();
+ if (oldFontEngine->cache_count == 0 && oldFontEngine->ref == 0)
+ delete oldFontEngine;
}
/*!
diff --git a/src/gui/text/qrawfont_ft.cpp b/src/gui/text/qrawfont_ft.cpp
index eefbd92..23d47eb 100644
--- a/src/gui/text/qrawfont_ft.cpp
+++ b/src/gui/text/qrawfont_ft.cpp
@@ -90,32 +90,6 @@ public:
return init(faceId, true, Format_None, fontData);
}
-
- bool initFromFontEngine(QFontEngine *oldFontEngine)
- {
- QFontEngineFT *fe = static_cast<QFontEngineFT *>(oldFontEngine);
-
- // Increase the reference of this QFreetypeFace since one more QFontEngineFT
- // will be using it
- fe->freetype->ref.ref();
- if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
- return false;
-
- default_load_flags = fe->default_load_flags;
- default_hint_style = fe->default_hint_style;
- antialias = fe->antialias;
- transform = fe->transform;
- embolden = fe->embolden;
- subpixelType = fe->subpixelType;
- lcdFilterType = fe->lcdFilterType;
- canUploadGlyphsToServer = fe->canUploadGlyphsToServer;
- embeddedbitmap = fe->embeddedbitmap;
-
-#if defined(Q_WS_X11)
- xglyph_format = static_cast<QFontEngineX11FT *>(fe)->xglyph_format;
-#endif
- return true;
- }
};
@@ -159,31 +133,6 @@ void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, int pixel
fontEngine->ref.ref();
}
-void QRawFontPrivate::platformSetPixelSize(int pixelSize)
-{
- if (fontEngine == NULL)
- return;
-
- QFontEngine *oldFontEngine = fontEngine;
-
- QFontDef fontDef;
- fontDef.pixelSize = pixelSize;
- QFontEngineFTRawFont *fe = new QFontEngineFTRawFont(fontDef);
- if (!fe->initFromFontEngine(oldFontEngine)) {
- delete fe;
- return;
- }
-
- fontEngine = fe;
- fontEngine->fontDef = oldFontEngine->fontDef;
- fontEngine->fontDef.pixelSize = pixelSize;
- fontEngine->ref.ref();
- Q_ASSERT(fontEngine != oldFontEngine);
- oldFontEngine->ref.deref();
- if (oldFontEngine->cache_count == 0 && oldFontEngine->ref == 0)
- delete oldFontEngine;
-}
-
QT_END_NAMESPACE
#endif // QT_NO_RAWFONT
diff --git a/src/gui/text/qrawfont_mac.cpp b/src/gui/text/qrawfont_mac.cpp
index 56005c6..1ed4185 100644
--- a/src/gui/text/qrawfont_mac.cpp
+++ b/src/gui/text/qrawfont_mac.cpp
@@ -78,28 +78,6 @@ void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData,
}
}
-void QRawFontPrivate::platformSetPixelSize(int pixelSize)
-{
- if (fontEngine == NULL)
- return;
-
- QFontEngine *oldFontEngine = fontEngine;
-
- QFontDef fontDef = oldFontEngine->fontDef;
- fontDef.pixelSize = pixelSize;
- fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi();
-
- QCoreTextFontEngine *ctFontEngine = static_cast<QCoreTextFontEngine *>(oldFontEngine);
- Q_ASSERT(ctFontEngine->cgFont);
-
- fontEngine = new QCoreTextFontEngine(ctFontEngine->cgFont, fontDef);
- fontEngine->ref.ref();
- Q_ASSERT(fontEngine != oldFontEngine);
- oldFontEngine->ref.deref();
- if (oldFontEngine->cache_count == 0 && oldFontEngine->ref == 0)
- delete oldFontEngine;
-}
-
QT_END_NAMESPACE
#endif // QT_NO_RAWFONT
diff --git a/src/gui/text/qrawfont_p.h b/src/gui/text/qrawfont_p.h
index f9a9ab5..e29ea9c 100644
--- a/src/gui/text/qrawfont_p.h
+++ b/src/gui/text/qrawfont_p.h
@@ -83,7 +83,6 @@ public:
, fontHandle(NULL)
, ptrAddFontMemResourceEx(other.ptrAddFontMemResourceEx)
, ptrRemoveFontMemResourceEx(other.ptrRemoveFontMemResourceEx)
- , uniqueFamilyName(other.uniqueFamilyName)
#endif
{
fontEngine = other.fontEngine;
@@ -102,7 +101,6 @@ public:
void platformLoadFromData(const QByteArray &fontData,
int pixelSize,
QFont::HintingPreference hintingPreference);
- void platformSetPixelSize(int pixelSize);
static QRawFontPrivate *get(const QRawFont &font) { return font.d.data(); }
@@ -120,8 +118,6 @@ public:
PtrAddFontMemResourceEx ptrAddFontMemResourceEx;
PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx;
- QString uniqueFamilyName;
-
#endif // Q_WS_WIN
};
diff --git a/src/gui/text/qrawfont_qpa.cpp b/src/gui/text/qrawfont_qpa.cpp
new file mode 100644
index 0000000..103619c
--- /dev/null
+++ b/src/gui/text/qrawfont_qpa.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_RAWFONT)
+
+#include "qrawfont_p.h"
+#include <QtGui/qplatformfontdatabase_qpa.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QRawFontPrivate::platformCleanUp()
+{
+}
+
+void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, int pixelSize,
+ QFont::HintingPreference hintingPreference)
+{
+ Q_ASSERT(fontEngine == 0);
+
+ QPlatformFontDatabase *pfdb = QApplicationPrivate::platformIntegration()->fontDatabase();
+ fontEngine = pfdb->fontEngine(fontData, pixelSize, hintingPreference);
+ if (fontEngine != 0)
+ fontEngine->ref.ref();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RAWFONT
diff --git a/src/gui/text/qrawfont_win.cpp b/src/gui/text/qrawfont_win.cpp
index fb5c6f4..d8acf57 100644
--- a/src/gui/text/qrawfont_win.cpp
+++ b/src/gui/text/qrawfont_win.cpp
@@ -559,7 +559,7 @@ void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData,
GUID guid;
CoCreateGuid(&guid);
- uniqueFamilyName = QString::fromLatin1("f")
+ QString uniqueFamilyName = QString::fromLatin1("f")
+ QString::number(guid.Data1, 36) + QLatin1Char('-')
+ QString::number(guid.Data2, 36) + QLatin1Char('-')
+ QString::number(guid.Data3, 36) + QLatin1Char('-')
@@ -613,6 +613,7 @@ void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData,
Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref == 0);
// Override the generated font name
+ static_cast<QFontEngineWin *>(fontEngine)->uniqueFamilyName = uniqueFamilyName;
fontEngine->fontDef.family = actualFontName;
fontEngine->ref.ref();
}
@@ -701,50 +702,6 @@ void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData,
}
}
-void QRawFontPrivate::platformSetPixelSize(int pixelSize)
-{
- if (fontEngine == NULL)
- return;
-
- QFontEngine *oldFontEngine = fontEngine;
-
-#if !defined(QT_NO_DIRECTWRITE)
- if (fontEngine->type() == QFontEngine::Win)
-#endif
-
- {
- QFontDef request = fontEngine->fontDef;
- QString actualFontName = request.family;
- if (!uniqueFamilyName.isEmpty())
- request.family = uniqueFamilyName;
- request.pixelSize = pixelSize;
-
- fontEngine = qt_load_font_engine_win(request);
- if (fontEngine != NULL) {
- fontEngine->fontDef.family = actualFontName;
- fontEngine->ref.ref();
- }
- }
-
-#if !defined(QT_NO_DIRECTWRITE)
- else {
- QFontEngineDirectWrite *dWriteFE = static_cast<QFontEngineDirectWrite *>(fontEngine);
- fontEngine = new QFontEngineDirectWrite(dWriteFE->m_directWriteFactory,
- dWriteFE->m_directWriteFontFace,
- pixelSize);
-
- fontEngine->fontDef = dWriteFE->fontDef;
- fontEngine->fontDef.pixelSize = pixelSize;
- fontEngine->ref.ref();
- }
-#endif
-
- Q_ASSERT(fontEngine != oldFontEngine);
- oldFontEngine->ref.deref();
- if (oldFontEngine->cache_count == 0 && oldFontEngine->ref == 0)
- delete oldFontEngine;
-}
-
QT_END_NAMESPACE
#endif // QT_NO_RAWFONT
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
index 6ddfdb0..4f6857a 100644
--- a/src/gui/text/qtextcursor.cpp
+++ b/src/gui/text/qtextcursor.cpp
@@ -362,20 +362,23 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
currentCharFormat = -1;
bool adjustX = true;
QTextBlock blockIt = block();
+ bool visualMovement = priv->defaultCursorMoveStyle == QTextCursor::Visual;
if (!blockIt.isValid())
return false;
- if (op >= QTextCursor::Left && op <= QTextCursor::WordRight
- && blockIt.textDirection() == Qt::RightToLeft) {
- if (op == QTextCursor::Left)
- op = QTextCursor::NextCharacter;
- else if (op == QTextCursor::Right)
- op = QTextCursor::PreviousCharacter;
- else if (op == QTextCursor::WordLeft)
+ if (blockIt.textDirection() == Qt::RightToLeft) {
+ if (op == QTextCursor::WordLeft)
op = QTextCursor::NextWord;
else if (op == QTextCursor::WordRight)
op = QTextCursor::PreviousWord;
+
+ if (!visualMovement) {
+ if (op == QTextCursor::Left)
+ op = QTextCursor::NextCharacter;
+ else if (op == QTextCursor::Right)
+ op = QTextCursor::PreviousCharacter;
+ }
}
const QTextLayout *layout = blockLayout(blockIt);
@@ -418,9 +421,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
break;
}
case QTextCursor::PreviousCharacter:
- case QTextCursor::Left:
newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
break;
+ case QTextCursor::Left:
+ newPosition = visualMovement ? priv->leftCursorPosition(position)
+ : priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
+ break;
case QTextCursor::StartOfWord: {
if (relativePos == 0)
break;
@@ -529,9 +535,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
break;
}
case QTextCursor::NextCharacter:
- case QTextCursor::Right:
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
break;
+ case QTextCursor::Right:
+ newPosition = visualMovement ? priv->rightCursorPosition(position)
+ : priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
+ break;
case QTextCursor::NextWord:
case QTextCursor::WordRight:
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
@@ -2558,4 +2567,19 @@ QTextDocument *QTextCursor::document() const
return 0; // document went away
}
+/*!
+ \enum QTextCursor::MoveStyle
+
+ This enum describes the movement style available to QTextCursor. The options
+ are:
+
+ \value Logical Within a left-to-right text block, increase cursor position
+ when pressing left arrow key, decrease cursor position when pressing the
+ right arrow key. If the text block is right-to-left, the opposite behavior
+ applies.
+ \value Visual Pressing the left arrow key will always cause the cursor to move
+ left, regardless of the text's writing direction. The same behavior applies to
+ right arrow key.
+*/
+
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h
index 4eaeb41..9e4c0b8 100644
--- a/src/gui/text/qtextcursor.h
+++ b/src/gui/text/qtextcursor.h
@@ -86,6 +86,10 @@ public:
MoveAnchor,
KeepAnchor
};
+ enum MoveStyle {
+ Logical,
+ Visual
+ };
void setPosition(int pos, MoveMode mode = MoveAnchor);
int position() const;
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 6dbd755..36f3c6c 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -586,6 +586,29 @@ void QTextDocument::setDefaultTextOption(const QTextOption &option)
}
/*!
+ \since 4.8
+
+ The default cursor movement style is used by all QTextCursor objects
+ created from the document. The default is QTextCursor::Logical.
+*/
+QTextCursor::MoveStyle QTextDocument::defaultCursorMoveStyle() const
+{
+ Q_D(const QTextDocument);
+ return d->defaultCursorMoveStyle;
+}
+
+/*!
+ \since 4.8
+
+ Set the default cursor movement style.
+*/
+void QTextDocument::setDefaultCursorMoveStyle(QTextCursor::MoveStyle style)
+{
+ Q_D(QTextDocument);
+ d->defaultCursorMoveStyle = style;
+}
+
+/*!
\fn void QTextDocument::markContentsDirty(int position, int length)
Marks the contents specified by the given \a position and \a length
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index f87ccc9..e515b36 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -46,6 +46,7 @@
#include <QtCore/qsize.h>
#include <QtCore/qrect.h>
#include <QtGui/qfont.h>
+#include <QtGui/qtextcursor.h>
QT_BEGIN_HEADER
@@ -60,7 +61,6 @@ class QPainter;
class QPrinter;
class QAbstractTextDocumentLayout;
class QPoint;
-class QTextCursor;
class QTextObject;
class QTextFormat;
class QTextFrame;
@@ -269,6 +269,9 @@ public:
QTextOption defaultTextOption() const;
void setDefaultTextOption(const QTextOption &option);
+ QTextCursor::MoveStyle defaultCursorMoveStyle() const;
+ void setDefaultCursorMoveStyle(QTextCursor::MoveStyle style);
+
Q_SIGNALS:
void contentsChange(int from, int charsRemoves, int charsAdded);
void contentsChanged();
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index a997720..779b1ff 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -209,6 +209,7 @@ QTextDocumentPrivate::QTextDocumentPrivate()
defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ defaultCursorMoveStyle = QTextCursor::Logical;
indentWidth = 40;
documentMargin = 4;
@@ -1382,6 +1383,20 @@ int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::Curs
return it.layout()->previousCursorPosition(position-start, mode) + start;
}
+int QTextDocumentPrivate::leftCursorPosition(int position) const
+{
+ QTextBlock it = blocksFind(position);
+ int start = it.position();
+ return it.layout()->leftCursorPosition(position-start) + start;
+}
+
+int QTextDocumentPrivate::rightCursorPosition(int position) const
+{
+ QTextBlock it = blocksFind(position);
+ int start = it.position();
+ return it.layout()->rightCursorPosition(position-start) + start;
+}
+
void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
{
beginEditBlock();
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index b464f2e..6563920 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -64,6 +64,7 @@
#include "private/qtextformat_p.h"
#include "QtGui/qtextdocument.h"
#include "QtGui/qtextobject.h"
+#include "QtGui/qtextcursor.h"
#include "QtCore/qmap.h"
#include "QtCore/qvariant.h"
#include "QtCore/qurl.h"
@@ -244,6 +245,8 @@ public:
int nextCursorPosition(int position, QTextLayout::CursorMode mode) const;
int previousCursorPosition(int position, QTextLayout::CursorMode mode) const;
+ int leftCursorPosition(int position) const;
+ int rightCursorPosition(int position) const;
void changeObjectFormat(QTextObject *group, int format);
@@ -339,6 +342,7 @@ private:
public:
QTextOption defaultTextOption;
+ QTextCursor::MoveStyle defaultCursorMoveStyle;
#ifndef QT_NO_CSSPARSER
QCss::StyleSheet parsedDefaultStyleSheet;
#endif
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 08d0eca..ff27bc6 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -856,6 +856,21 @@ void QTextEngine::shapeLine(const QScriptLine &line)
}
}
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+static bool enableHarfBuzz()
+{
+ static enum { Yes, No, Unknown } status = Unknown;
+
+ if (status == Unknown) {
+ QByteArray v = qgetenv("QT_ENABLE_HARFBUZZ");
+ bool value = !v.isEmpty() && v != "0" && v != "false";
+ if (value) status = Yes;
+ else status = No;
+ }
+ return status == Yes;
+}
+#endif
+
void QTextEngine::shapeText(int item) const
{
Q_ASSERT(item < layoutData->items.size());
@@ -865,7 +880,24 @@ void QTextEngine::shapeText(int item) const
return;
#if defined(Q_WS_MAC)
- shapeTextMac(item);
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+ if (enableHarfBuzz()) {
+#endif
+ QFontEngine *actualFontEngine = fontEngine(si, &si.ascent, &si.descent, &si.leading);
+ if (actualFontEngine->type() == QFontEngine::Multi)
+ actualFontEngine = static_cast<QFontEngineMulti *>(actualFontEngine)->engine(0);
+
+ HB_Face face = actualFontEngine->harfbuzzFace();
+ HB_Script script = (HB_Script) si.analysis.script;
+ if (face->supported_scripts[script])
+ shapeTextWithHarfbuzz(item);
+ else
+ shapeTextMac(item);
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+ } else {
+ shapeTextMac(item);
+ }
+#endif
#elif defined(Q_WS_WINCE)
shapeTextWithCE(item);
#else
@@ -1242,6 +1274,10 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const
actualFontEngine = static_cast<QFontEngineMulti *>(font)->engine(engineIdx);
}
+ si.ascent = qMax(actualFontEngine->ascent(), si.ascent);
+ si.descent = qMax(actualFontEngine->descent(), si.descent);
+ si.leading = qMax(actualFontEngine->leading(), si.leading);
+
shaper_item.font = actualFontEngine->harfbuzzFont();
shaper_item.face = actualFontEngine->harfbuzzFace();
@@ -1304,6 +1340,7 @@ static void init(QTextEngine *e)
e->ignoreBidi = false;
e->cacheGlyphs = false;
e->forceJustification = false;
+ e->visualMovement = false;
e->layoutData = 0;
@@ -1565,6 +1602,8 @@ bool QTextEngine::isRightToLeft() const
default:
break;
}
+ if (!layoutData)
+ itemize();
// this places the cursor in the right position depending on the keyboard layout
if (layoutData->string.isEmpty())
return QApplication::keyboardInputDirection() == Qt::RightToLeft;
@@ -2737,6 +2776,182 @@ QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line)
return width(line.from + pos, line.length - pos);
}
+QFixed QTextEngine::alignLine(const QScriptLine &line)
+{
+ QFixed x = 0;
+ justify(line);
+ // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
+ if (!line.justified && line.width != QFIXED_MAX) {
+ int align = option.alignment();
+ if (align & Qt::AlignLeft)
+ x -= leadingSpaceWidth(line);
+ if (align & Qt::AlignJustify && isRightToLeft())
+ align = Qt::AlignRight;
+ if (align & Qt::AlignRight)
+ x = line.width - (line.textAdvance + leadingSpaceWidth(line));
+ else if (align & Qt::AlignHCenter)
+ x = (line.width - (line.textAdvance + leadingSpaceWidth(line)))/2;
+ }
+ return x;
+}
+
+QFixed QTextEngine::offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos)
+{
+ unsigned short *logClusters = this->logClusters(si);
+ const QGlyphLayout &glyphs = shapedGlyphs(si);
+
+ int offsetInCluster = 0;
+ for (int i = pos - 1; i >= 0; i--) {
+ if (logClusters[i] == glyph_pos)
+ offsetInCluster++;
+ else
+ break;
+ }
+
+ // in the case that the offset is inside a (multi-character) glyph,
+ // interpolate the position.
+ if (offsetInCluster > 0) {
+ int clusterLength = 0;
+ for (int i = pos - offsetInCluster; i < max; i++) {
+ if (logClusters[i] == glyph_pos)
+ clusterLength++;
+ else
+ break;
+ }
+ if (clusterLength)
+ return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
+ }
+
+ return 0;
+}
+
+int QTextEngine::previousLogicalPosition(int oldPos) const
+{
+ const HB_CharAttributes *attrs = attributes();
+ if (!attrs || oldPos < 0)
+ return oldPos;
+
+ if (oldPos <= 0)
+ return 0;
+ oldPos--;
+ while (oldPos && !attrs[oldPos].charStop)
+ oldPos--;
+ return oldPos;
+}
+
+int QTextEngine::nextLogicalPosition(int oldPos) const
+{
+ const HB_CharAttributes *attrs = attributes();
+ int len = block.isValid() ? block.length() - 1
+ : layoutData->string.length();
+ Q_ASSERT(len <= layoutData->string.length());
+ if (!attrs || oldPos < 0 || oldPos >= len)
+ return oldPos;
+
+ oldPos++;
+ while (oldPos < len && !attrs[oldPos].charStop)
+ oldPos++;
+ return oldPos;
+}
+
+int QTextEngine::lineNumberForTextPosition(int pos)
+{
+ if (!layoutData)
+ itemize();
+ if (pos == layoutData->string.length() && lines.size())
+ return lines.size() - 1;
+ for (int i = 0; i < lines.size(); ++i) {
+ const QScriptLine& line = lines[i];
+ if (line.from + line.length > pos)
+ return i;
+ }
+ return -1;
+}
+
+void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints)
+{
+ QTextLineItemIterator iterator(this, lineNum);
+ bool rtl = isRightToLeft();
+ bool lastLine = lineNum >= lines.size() - 1;
+
+ while (!iterator.atEnd()) {
+ iterator.next();
+ const QScriptItem *si = &layoutData->items[iterator.item];
+ if (si->analysis.bidiLevel % 2) {
+ int i = iterator.itemEnd - 1, min = iterator.itemStart;
+ if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
+ i++;
+ for (; i >= min; i--)
+ insertionPoints.push_back(i);
+ } else {
+ int i = iterator.itemStart, max = iterator.itemEnd;
+ if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
+ max++;
+ for (; i < max; i++)
+ insertionPoints.push_back(i);
+ }
+ }
+}
+
+int QTextEngine::endOfLine(int lineNum)
+{
+ QVector<int> insertionPoints;
+ insertionPointsForLine(lineNum, insertionPoints);
+
+ if (insertionPoints.size() > 0)
+ return insertionPoints.last();
+ return 0;
+}
+
+int QTextEngine::beginningOfLine(int lineNum)
+{
+ QVector<int> insertionPoints;
+ insertionPointsForLine(lineNum, insertionPoints);
+
+ if (insertionPoints.size() > 0)
+ return insertionPoints.first();
+ return 0;
+}
+
+int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation op)
+{
+ if (!layoutData)
+ itemize();
+
+ bool moveRight = (op == QTextCursor::Right);
+ bool alignRight = isRightToLeft();
+ if (!layoutData->hasBidi)
+ return moveRight ^ alignRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos);
+
+ int lineNum = lineNumberForTextPosition(pos);
+ Q_ASSERT(lineNum >= 0);
+
+ QVector<int> insertionPoints;
+ insertionPointsForLine(lineNum, insertionPoints);
+ int i, max = insertionPoints.size();
+ for (i = 0; i < max; i++)
+ if (pos == insertionPoints[i]) {
+ if (moveRight) {
+ if (i + 1 < max)
+ return insertionPoints[i + 1];
+ } else {
+ if (i > 0)
+ return insertionPoints[i - 1];
+ }
+
+ if (moveRight ^ alignRight) {
+ if (lineNum + 1 < lines.size())
+ return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1);
+ }
+ else {
+ if (lineNum > 0)
+ return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1);
+ }
+ }
+
+ return pos;
+}
+
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
: QTextEngine(string, f),
_layoutData(string, _memory, MemSize)
@@ -2841,5 +3056,127 @@ glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const
return m;
}
+QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, const QPointF &pos,
+ const QTextLayout::FormatRange *_selection)
+ : eng(_eng),
+ line(eng->lines[_lineNum]),
+ si(0),
+ lineNum(_lineNum),
+ lineEnd(line.from + line.length),
+ firstItem(eng->findItem(line.from)),
+ lastItem(eng->findItem(lineEnd - 1)),
+ nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
+ logicalItem(-1),
+ item(-1),
+ visualOrder(nItems),
+ levels(nItems),
+ selection(_selection)
+{
+ pos_x = x = QFixed::fromReal(pos.x());
+
+ x += line.x;
+
+ x += eng->alignLine(line);
+
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ eng->shapeLine(line);
+}
+
+QScriptItem &QTextLineItemIterator::next()
+{
+ x += itemWidth;
+
+ ++logicalItem;
+ item = visualOrder[logicalItem] + firstItem;
+ itemLength = eng->length(item);
+ si = &eng->layoutData->items[item];
+ if (!si->num_glyphs)
+ eng->shape(item);
+
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ itemWidth = si->width;
+ return *si;
+ }
+
+ unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+
+ itemStart = qMax(line.from, si->position);
+ glyphsStart = logClusters[itemStart - si->position];
+ if (lineEnd < si->position + itemLength) {
+ itemEnd = lineEnd;
+ glyphsEnd = logClusters[itemEnd-si->position];
+ } else {
+ itemEnd = si->position + itemLength;
+ glyphsEnd = si->num_glyphs;
+ }
+ // show soft-hyphen at line-break
+ if (si->position + itemLength >= lineEnd
+ && eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
+ glyphs.attributes[glyphsEnd - 1].dontPrint = false;
+
+ itemWidth = 0;
+ for (int g = glyphsStart; g < glyphsEnd; ++g)
+ itemWidth += glyphs.effectiveAdvance(g);
+
+ return *si;
+}
+
+bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
+{
+ *selectionX = *selectionWidth = 0;
+
+ if (!selection)
+ return false;
+
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if (si->position >= selection->start + selection->length
+ || si->position + itemLength <= selection->start)
+ return false;
+
+ *selectionX = x;
+ *selectionWidth = itemWidth;
+ } else {
+ unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+
+ int from = qMax(itemStart, selection->start) - si->position;
+ int to = qMin(itemEnd, selection->start + selection->length) - si->position;
+ if (from >= to)
+ return false;
+
+ int start_glyph = logClusters[from];
+ int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
+ QFixed soff;
+ QFixed swidth;
+ if (si->analysis.bidiLevel %2) {
+ for (int g = glyphsEnd - 1; g >= end_glyph; --g)
+ soff += glyphs.effectiveAdvance(g);
+ for (int g = end_glyph - 1; g >= start_glyph; --g)
+ swidth += glyphs.effectiveAdvance(g);
+ } else {
+ for (int g = glyphsStart; g < start_glyph; ++g)
+ soff += glyphs.effectiveAdvance(g);
+ for (int g = start_glyph; g < end_glyph; ++g)
+ swidth += glyphs.effectiveAdvance(g);
+ }
+
+ // If the starting character is in the middle of a ligature,
+ // selection should only contain the right part of that ligature
+ // glyph, so we need to get the width of the left part here and
+ // add it to *selectionX
+ QFixed leftOffsetInLigature = eng->offsetInLigature(si, from, to, start_glyph);
+ *selectionX = x + soff + leftOffsetInLigature;
+ *selectionWidth = swidth - leftOffsetInLigature;
+ // If the ending character is also part of a ligature, swidth does
+ // not contain that part yet, we also need to find out the width of
+ // that left part
+ *selectionWidth += eng->offsetInLigature(si, to, eng->length(item), end_glyph);
+ }
+ return true;
+}
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp
index 97e8c5b..2c6e579 100644
--- a/src/gui/text/qtextengine_mac.cpp
+++ b/src/gui/text/qtextengine_mac.cpp
@@ -605,11 +605,11 @@ void QTextEngine::shapeTextMac(int item) const
unsigned short *log_clusters = logClusters(&si);
bool stringToCMapFailed = false;
- if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes())) {
+ if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) {
ensureSpace(num_glyphs);
g = availableGlyphs(&si);
stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters,
- attributes());
+ attributes(), &si);
}
if (!stringToCMapFailed) {
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index 366c5c3..c476485 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -64,6 +64,7 @@
#include "QtGui/qpaintengine.h"
#include "QtGui/qtextobject.h"
#include "QtGui/qtextoption.h"
+#include "QtGui/qtextcursor.h"
#include "QtCore/qset.h"
#include "QtCore/qdebug.h"
#ifndef QT_BUILD_COMPAT_LIB
@@ -471,6 +472,7 @@ public:
void shape(int item) const;
void justify(const QScriptLine &si);
+ QFixed alignLine(const QScriptLine &line);
QFixed width(int charFrom, int numChars) const;
glyph_metrics_t boundingBox(int from, int len) const;
@@ -586,12 +588,18 @@ public:
uint cacheGlyphs : 1;
uint stackEngine : 1;
uint forceJustification : 1;
+ uint visualMovement : 1;
int *underlinePositions;
mutable LayoutData *layoutData;
inline bool hasFormats() const { return (block.docHandle() || specialData); }
+ inline bool visualCursorMovement() const
+ {
+ return (visualMovement ||
+ (block.docHandle() ? block.docHandle()->defaultCursorMoveStyle == QTextCursor::Visual : false));
+ }
struct SpecialData {
int preeditPosition;
@@ -611,6 +619,13 @@ public:
void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
+ QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
+ int previousLogicalPosition(int oldPos) const;
+ int nextLogicalPosition(int oldPos) const;
+ int lineNumberForTextPosition(int pos);
+ int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op);
+ void insertionPointsForLine(int lineNum, QVector<int> &insertionPoints);
+
private:
void setBoundary(int strPos) const;
void addRequiredBoundaries() const;
@@ -625,6 +640,8 @@ private:
void splitItem(int item, int pos) const;
void resolveAdditionalFormats() const;
+ int endOfLine(int lineNum);
+ int beginningOfLine(int lineNum);
};
class QStackTextEngine : public QTextEngine {
@@ -635,6 +652,49 @@ public:
void *_memory[MemSize];
};
+struct QTextLineItemIterator
+{
+ QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
+ const QTextLayout::FormatRange *_selection = 0);
+
+ inline bool atEnd() const { return logicalItem >= nItems - 1; }
+ inline bool atBeginning() const { return logicalItem <= 0; }
+ QScriptItem &next();
+
+ bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
+ inline bool isOutsideSelection() const {
+ QFixed tmp1, tmp2;
+ return !getSelectionBounds(&tmp1, &tmp2);
+ }
+
+ QTextEngine *eng;
+
+ QFixed x;
+ QFixed pos_x;
+ const QScriptLine &line;
+ QScriptItem *si;
+
+ int lineNum;
+ int lineEnd;
+ int firstItem;
+ int lastItem;
+ int nItems;
+ int logicalItem;
+ int item;
+ int itemLength;
+
+ int glyphsStart;
+ int glyphsEnd;
+ int itemStart;
+ int itemEnd;
+
+ QFixed itemWidth;
+
+ QVarLengthArray<int> visualOrder;
+ QVarLengthArray<uchar> levels;
+
+ const QTextLayout::FormatRange *selection;
+};
Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags)
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 93f71d3..122382f 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -72,23 +72,6 @@ QT_BEGIN_NAMESPACE
#define SuppressText 0x5012
#define SuppressBackground 0x513
-static QFixed alignLine(QTextEngine *eng, const QScriptLine &line)
-{
- QFixed x = 0;
- eng->justify(line);
- // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
- if (!line.justified && line.width != QFIXED_MAX) {
- int align = eng->option.alignment();
- if (align & Qt::AlignJustify && eng->isRightToLeft())
- align = Qt::AlignRight;
- if (align & Qt::AlignRight)
- x = line.width - (line.textAdvance + eng->leadingSpaceWidth(line));
- else if (align & Qt::AlignHCenter)
- x = (line.width - line.textAdvance)/2;
- }
- return x;
-}
-
/*!
\class QTextLayout::FormatRange
\reentrant
@@ -596,6 +579,30 @@ bool QTextLayout::cacheEnabled() const
}
/*!
+ Set the visual cursor movement style. If the QTextLayout is backed by
+ a document, you can ignore this and use the option in QTextDocument,
+ this option is for widgets like QLineEdit or custom widgets without
+ a QTextDocument. Default value is QTextCursor::Logical.
+
+ \sa setCursorMoveStyle()
+*/
+void QTextLayout::setCursorMoveStyle(QTextCursor::MoveStyle style)
+{
+ d->visualMovement = style == QTextCursor::Visual ? true : false;
+}
+
+/*!
+ The cursor movement style of this QTextLayout. The default is
+ QTextCursor::Logical.
+
+ \sa setCursorMoveStyle()
+*/
+QTextCursor::MoveStyle QTextLayout::cursorMoveStyle() const
+{
+ return d->visualMovement ? QTextCursor::Visual : QTextCursor::Logical;
+}
+
+/*!
Begins the layout process.
\sa endLayout()
@@ -718,6 +725,34 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
}
/*!
+ Returns the cursor position to the right of \a oldPos, next to it.
+ It's dependent on the visual position of characters, after bi-directional
+ reordering.
+
+ \sa leftCursorPosition(), nextCursorPosition()
+*/
+int QTextLayout::rightCursorPosition(int oldPos) const
+{
+ int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
+// qDebug("%d -> %d", oldPos, newPos);
+ return newPos;
+}
+
+/*!
+ Returns the cursor position to the left of \a oldPos, next to it.
+ It's dependent on the visual position of characters, after bi-directional
+ reordering.
+
+ \sa rightCursorPosition(), previousCursorPosition()
+*/
+int QTextLayout::leftCursorPosition(int oldPos) const
+{
+ int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
+// qDebug("%d -> %d", oldPos, newPos);
+ return newPos;
+}
+
+/*!/
Returns true if position \a pos is a valid cursor position.
In a Unicode context some positions in the text are not valid
@@ -815,16 +850,8 @@ QTextLine QTextLayout::lineAt(int i) const
*/
QTextLine QTextLayout::lineForTextPosition(int pos) const
{
- for (int i = 0; i < d->lines.size(); ++i) {
- const QScriptLine& line = d->lines[i];
- if (line.from + (int)line.length > pos)
- return QTextLine(i, d);
- }
- if (!d->layoutData)
- d->itemize();
- if (pos == d->layoutData->string.length() && d->lines.size())
- return QTextLine(d->lines.size()-1, d);
- return QTextLine();
+ int lineNum = d->lineNumberForTextPosition(pos);
+ return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
}
/*!
@@ -919,201 +946,6 @@ void QTextLayout::setFlags(int flags)
}
}
-struct QTextLineItemIterator
-{
- QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
- const QTextLayout::FormatRange *_selection = 0);
-
- inline bool atEnd() const { return logicalItem >= nItems - 1; }
- QScriptItem &next();
-
- bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
- inline bool isOutsideSelection() const {
- QFixed tmp1, tmp2;
- return !getSelectionBounds(&tmp1, &tmp2);
- }
-
- QTextEngine *eng;
-
- QFixed x;
- QFixed pos_x;
- const QScriptLine &line;
- QScriptItem *si;
-
- int lineEnd;
- int firstItem;
- int lastItem;
- int nItems;
- int logicalItem;
- int item;
- int itemLength;
-
- int glyphsStart;
- int glyphsEnd;
- int itemStart;
- int itemEnd;
-
- QFixed itemWidth;
-
- QVarLengthArray<int> visualOrder;
- QVarLengthArray<uchar> levels;
-
- const QTextLayout::FormatRange *selection;
-};
-
-QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos,
- const QTextLayout::FormatRange *_selection)
- : eng(_eng),
- line(eng->lines[lineNum]),
- si(0),
- lineEnd(line.from + line.length),
- firstItem(eng->findItem(line.from)),
- lastItem(eng->findItem(lineEnd - 1)),
- nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
- logicalItem(-1),
- item(-1),
- visualOrder(nItems),
- levels(nItems),
- selection(_selection)
-{
- pos_x = x = QFixed::fromReal(pos.x());
-
- x += line.x;
-
- x += alignLine(eng, line);
-
- for (int i = 0; i < nItems; ++i)
- levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
- QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
-
- eng->shapeLine(line);
-}
-
-QScriptItem &QTextLineItemIterator::next()
-{
- x += itemWidth;
-
- ++logicalItem;
- item = visualOrder[logicalItem] + firstItem;
- itemLength = eng->length(item);
- si = &eng->layoutData->items[item];
- if (!si->num_glyphs)
- eng->shape(item);
-
- if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
- itemWidth = si->width;
- return *si;
- }
-
- unsigned short *logClusters = eng->logClusters(si);
- QGlyphLayout glyphs = eng->shapedGlyphs(si);
-
- itemStart = qMax(line.from, si->position);
- glyphsStart = logClusters[itemStart - si->position];
- if (lineEnd < si->position + itemLength) {
- itemEnd = lineEnd;
- glyphsEnd = logClusters[itemEnd-si->position];
- } else {
- itemEnd = si->position + itemLength;
- glyphsEnd = si->num_glyphs;
- }
- // show soft-hyphen at line-break
- if (si->position + itemLength >= lineEnd
- && eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
- glyphs.attributes[glyphsEnd - 1].dontPrint = false;
-
- itemWidth = 0;
- for (int g = glyphsStart; g < glyphsEnd; ++g)
- itemWidth += glyphs.effectiveAdvance(g);
-
- return *si;
-}
-
-static QFixed offsetInLigature(const unsigned short *logClusters,
- const QGlyphLayout &glyphs,
- int pos, int max, int glyph_pos)
-{
- int offsetInCluster = 0;
- for (int i = pos - 1; i >= 0; i--) {
- if (logClusters[i] == glyph_pos)
- offsetInCluster++;
- else
- break;
- }
-
- // in the case that the offset is inside a (multi-character) glyph,
- // interpolate the position.
- if (offsetInCluster > 0) {
- int clusterLength = 0;
- for (int i = pos - offsetInCluster; i < max; i++) {
- if (logClusters[i] == glyph_pos)
- clusterLength++;
- else
- break;
- }
- if (clusterLength)
- return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
- }
-
- return 0;
-}
-
-bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
-{
- *selectionX = *selectionWidth = 0;
-
- if (!selection)
- return false;
-
- if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
- if (si->position >= selection->start + selection->length
- || si->position + itemLength <= selection->start)
- return false;
-
- *selectionX = x;
- *selectionWidth = itemWidth;
- } else {
- unsigned short *logClusters = eng->logClusters(si);
- QGlyphLayout glyphs = eng->shapedGlyphs(si);
-
- int from = qMax(itemStart, selection->start) - si->position;
- int to = qMin(itemEnd, selection->start + selection->length) - si->position;
- if (from >= to)
- return false;
-
- int start_glyph = logClusters[from];
- int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
- QFixed soff;
- QFixed swidth;
- if (si->analysis.bidiLevel %2) {
- for (int g = glyphsEnd - 1; g >= end_glyph; --g)
- soff += glyphs.effectiveAdvance(g);
- for (int g = end_glyph - 1; g >= start_glyph; --g)
- swidth += glyphs.effectiveAdvance(g);
- } else {
- for (int g = glyphsStart; g < start_glyph; ++g)
- soff += glyphs.effectiveAdvance(g);
- for (int g = start_glyph; g < end_glyph; ++g)
- swidth += glyphs.effectiveAdvance(g);
- }
-
- // If the starting character is in the middle of a ligature,
- // selection should only contain the right part of that ligature
- // glyph, so we need to get the width of the left part here and
- // add it to *selectionX
- QFixed leftOffsetInLigature = offsetInLigature(logClusters, glyphs, from,
- to, start_glyph);
- *selectionX = x + soff + leftOffsetInLigature;
- *selectionWidth = swidth - leftOffsetInLigature;
- // If the ending character is also part of a ligature, swidth does
- // not contain that part yet, we also need to find out the width of
- // that left part
- *selectionWidth += offsetInLigature(logClusters, glyphs, to,
- eng->length(item), end_glyph);
- }
- return true;
-}
-
static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
QPainterPath *region, QRectF boundingRect)
{
@@ -1228,6 +1060,7 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRang
QRectF lineRect(tl.naturalTextRect());
lineRect.translate(position);
+ lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
bool isLastLineInBlock = (line == d->lines.size()-1);
int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
@@ -1382,18 +1215,9 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QFixed pos_y = QFixed::fromReal(position.y());
cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
- int line = 0;
- if (cursorPosition == d->layoutData->string.length()) {
- line = d->lines.size() - 1;
- } else {
- // ### binary search
- for (line = 0; line < d->lines.size(); line++) {
- const QScriptLine &sl = d->lines[line];
- if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition)
- break;
- }
- }
-
+ int line = d->lineNumberForTextPosition(cursorPosition);
+ if (line < 0)
+ line = 0;
if (line >= d->lines.size())
return;
@@ -1402,7 +1226,15 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
qreal x = position.x() + l.cursorToX(cursorPosition);
- int itm = d->findItem(cursorPosition - 1);
+ int itm;
+
+ if (d->visualCursorMovement()) {
+ if (cursorPosition == sl.from + sl.length)
+ cursorPosition--;
+ itm = d->findItem(cursorPosition);
+ } else
+ itm = d->findItem(cursorPosition - 1);
+
QFixed base = sl.base();
QFixed descent = sl.descent;
bool rightToLeft = d->isRightToLeft();
@@ -1512,7 +1344,7 @@ QRectF QTextLine::rect() const
QRectF QTextLine::naturalTextRect() const
{
const QScriptLine& sl = eng->lines[i];
- QFixed x = sl.x + alignLine(eng, sl);
+ QFixed x = sl.x + eng->alignLine(sl);
QFixed width = sl.textWidth;
if (sl.justified)
@@ -2632,9 +2464,10 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
eng->itemize();
const QScriptLine &line = eng->lines[i];
+ bool lastLine = i >= eng->lines.size() - 1;
QFixed x = line.x;
- x += alignLine(eng, line);
+ x += eng->alignLine(line);
if (!i && !eng->layoutData->items.size()) {
*cursorPos = 0;
@@ -2720,21 +2553,29 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
logClusters = eng->logClusters(si);
glyphs = eng->shapedGlyphs(si);
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
- if(pos == l)
+ if (pos == (reverse ? 0 : l))
x += si->width;
} else {
+ bool rtl = eng->isRightToLeft();
+ bool visual = eng->visualCursorMovement();
if (reverse) {
int end = qMin(lineEnd, si->position + l) - si->position;
int glyph_end = end == l ? si->num_glyphs : logClusters[end];
- for (int i = glyph_end - 1; i >= glyph_pos; i--)
+ int glyph_start = glyph_pos;
+ if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
+ glyph_start++;
+ for (int i = glyph_end - 1; i >= glyph_start; i--)
x += glyphs.effectiveAdvance(i);
} else {
int start = qMax(line.from - si->position, 0);
int glyph_start = logClusters[start];
- for (int i = glyph_start; i < glyph_pos; i++)
+ int glyph_end = glyph_pos;
+ if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
+ glyph_end--;
+ for (int i = glyph_start; i <= glyph_end; i++)
x += glyphs.effectiveAdvance(i);
}
- x += offsetInLigature(logClusters, glyphs, pos, line.length, glyph_pos);
+ x += eng->offsetInLigature(si, pos, line.length, glyph_pos);
}
*cursorPos = pos + si->position;
@@ -2753,6 +2594,8 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
{
QFixed x = QFixed::fromReal(_x);
const QScriptLine &line = eng->lines[i];
+ bool lastLine = i >= eng->lines.size() - 1;
+ int lineNum = i;
if (!eng->layoutData)
eng->itemize();
@@ -2770,7 +2613,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
return 0;
x -= line.x;
- x -= alignLine(eng, line);
+ x -= eng->alignLine(line);
// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
QVarLengthArray<int> visualOrder(nItems);
@@ -2779,6 +2622,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ bool visual = eng->visualCursorMovement();
if (x <= 0) {
// left of first item
int item = visualOrder[0]+firstItem;
@@ -2795,8 +2639,13 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|| (line.justified && x < line.width)) {
// has to be in one of the runs
QFixed pos;
+ bool rtl = eng->isRightToLeft();
eng->shapeLine(line);
+ QVector<int> insertionPoints;
+ if (visual && rtl)
+ eng->insertionPointsForLine(lineNum, insertionPoints);
+ int nchars = 0;
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem;
QScriptItem &si = eng->layoutData->items[item];
@@ -2826,6 +2675,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
if (pos + item_width < x) {
pos += item_width;
+ nchars += end;
continue;
}
// qDebug(" inside run");
@@ -2870,27 +2720,60 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
} else {
QFixed dist = INT_MAX/256;
if (si.analysis.bidiLevel % 2) {
- pos += item_width;
- while (gs <= ge) {
- if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
- glyph_pos = gs;
- dist = qAbs(x-pos);
+ if (!visual || rtl || (lastLine && i == nItems - 1)) {
+ pos += item_width;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ pos -= glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ while (ge >= gs) {
+ if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = ge;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(ge);
+ --ge;
}
- pos -= glyphs.effectiveAdvance(gs);
- ++gs;
}
} else {
- while (gs <= ge) {
- if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
- glyph_pos = gs;
- dist = qAbs(x-pos);
+ if (!visual || !rtl || (lastLine && i == 0)) {
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(gs);
+ ++gs;
}
- pos += glyphs.effectiveAdvance(gs);
- ++gs;
+ } else {
+ QFixed oldPos = pos;
+ while (gs <= ge) {
+ pos += glyphs.effectiveAdvance(gs);
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ ++gs;
+ }
+ pos = oldPos;
}
}
- if (qAbs(x-pos) < dist)
+ if (qAbs(x-pos) < dist) {
+ if (visual) {
+ if (!rtl && i < nItems - 1) {
+ nchars += end;
+ continue;
+ }
+ if (rtl && nchars > 0)
+ return insertionPoints[lastLine ? nchars : nchars - 1];
+ }
return si.position + end;
+ }
}
Q_ASSERT(glyph_pos != -1);
int j;
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index 9dd8ebd..6aa81f9 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -50,6 +50,7 @@
#include <QtGui/qevent.h>
#include <QtGui/qtextformat.h>
#include <QtGui/qglyphs.h>
+#include <QtGui/qtextcursor.h>
QT_BEGIN_HEADER
@@ -136,6 +137,9 @@ public:
void setCacheEnabled(bool enable);
bool cacheEnabled() const;
+ void setCursorMoveStyle(QTextCursor::MoveStyle style);
+ QTextCursor::MoveStyle cursorMoveStyle() const;
+
void beginLayout();
void endLayout();
void clearLayout();
@@ -153,6 +157,8 @@ public:
bool isValidCursorPosition(int pos) const;
int nextCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
int previousCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
+ int leftCursorPosition(int oldPos) const;
+ int rightCursorPosition(int oldPos) const;
void draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections = QVector<FormatRange>(),
const QRectF &clip = QRectF()) const;
diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri
index df9398c..e5d57d0 100644
--- a/src/gui/text/text.pri
+++ b/src/gui/text/text.pri
@@ -114,6 +114,9 @@ unix:x11 {
OBJECTIVE_SOURCES += \
text/qfontengine_coretext.mm \
text/qfontengine_mac.mm
+ contains(QT_CONFIG, harfbuzz) {
+ DEFINES += QT_ENABLE_HARFBUZZ_FOR_MAC
+ }
}
embedded {
@@ -136,7 +139,8 @@ qpa {
SOURCES += \
text/qfont_qpa.cpp \
text/qfontengine_qpa.cpp \
- text/qplatformfontdatabase_qpa.cpp
+ text/qplatformfontdatabase_qpa.cpp \
+ text/qrawfont_qpa.cpp
HEADERS += \
text/qplatformfontdatabase_qpa.h