diff options
author | David Boddie <dboddie@trolltech.com> | 2010-03-30 14:56:41 (GMT) |
---|---|---|
committer | David Boddie <dboddie@trolltech.com> | 2010-03-30 14:56:41 (GMT) |
commit | 87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3 (patch) | |
tree | f8b8c24056d54e19937dea1b0301af558597726a /src/gui/text | |
parent | 09ce407aaa4a00013a606bf0011faf6cbc654c72 (diff) | |
parent | 00f7426f3906361fb5addb36e428648eee5e2983 (diff) | |
download | Qt-87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3.zip Qt-87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3.tar.gz Qt-87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3.tar.bz2 |
Merge branch '4.7' of git@scm.dev.nokia.troll.no:qt/oslo-staging-2 into 4.7
Conflicts:
doc/src/modules.qdoc
mkspecs/common/symbian/symbian.conf
src/gui/graphicsview/qgraphicswidget.h
src/s60installs/bwins/QtGuiu.def
src/s60installs/eabi/QtGuiu.def
Diffstat (limited to 'src/gui/text')
34 files changed, 1386 insertions, 164 deletions
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index dd9e69e..99b9f40 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -73,7 +73,7 @@ #endif #endif #ifdef Q_OS_SYMBIAN -#include "qt_s60_p.h" +#include <private/qt_s60_p.h> #endif #include <QMutexLocker> @@ -1297,6 +1297,8 @@ QFont::StyleHint QFont::styleHint() const \value PreferQuality prefer the best quality font. The font matcher will use the nearest standard point size that the font supports. + \value ForceIntegerMetrics forces the use of integer values in font engines that support fractional + font metrics. */ /*! diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index a2fff70..6f62424 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -76,17 +76,18 @@ public: }; enum StyleStrategy { - PreferDefault = 0x0001, - PreferBitmap = 0x0002, - PreferDevice = 0x0004, - PreferOutline = 0x0008, - ForceOutline = 0x0010, - PreferMatch = 0x0020, - PreferQuality = 0x0040, - PreferAntialias = 0x0080, - NoAntialias = 0x0100, - OpenGLCompatible = 0x0200, - NoFontMerging = 0x8000 + PreferDefault = 0x0001, + PreferBitmap = 0x0002, + PreferDevice = 0x0004, + PreferOutline = 0x0008, + ForceOutline = 0x0010, + PreferMatch = 0x0020, + PreferQuality = 0x0040, + PreferAntialias = 0x0080, + NoAntialias = 0x0100, + OpenGLCompatible = 0x0200, + ForceIntegerMetrics = 0x0400, + NoFontMerging = 0x8000 }; enum Weight { @@ -291,6 +292,7 @@ private: friend class QFontMetricsF; friend class QFontInfo; friend class QPainter; + friend class QPainterPrivate; friend class QPSPrintEngineFont; friend class QApplication; friend class QWidget; diff --git a/src/gui/text/qfont_s60.cpp b/src/gui/text/qfont_s60.cpp index 52c77d6..ccd17a2 100644 --- a/src/gui/text/qfont_s60.cpp +++ b/src/gui/text/qfont_s60.cpp @@ -40,8 +40,8 @@ ****************************************************************************/ #include "qfont.h" -#include "qt_s60_p.h" -#include "qpixmap_s60_p.h" +#include <private/qt_s60_p.h> +#include <private/qpixmap_s60_p.h> #include "qmutex.h" QT_BEGIN_NAMESPACE diff --git a/src/gui/text/qfontdatabase_qws.cpp b/src/gui/text/qfontdatabase_qws.cpp index 62d7793..a3d8d65 100644 --- a/src/gui/text/qfontdatabase_qws.cpp +++ b/src/gui/text/qfontdatabase_qws.cpp @@ -632,8 +632,9 @@ QFontEngine *loadSingleEngine(int script, const QFontPrivate *fp, #ifndef QT_NO_FREETYPE QScopedPointer<QFontEngineFT> fte(new QFontEngineFT(def)); - if (fte->init(faceId, style->antialiased, - style->antialiased ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono)) { + bool antialias = style->antialiased && !(request.styleStrategy & QFont::NoAntialias); + if (fte->init(faceId, antialias, + antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono)) { #ifdef QT_NO_QWS_QPF2 return fte.take(); #else @@ -793,7 +794,7 @@ QFontDatabase::findFont(int script, const QFontPrivate *fp, " family: %s [%s], script: %d\n" " weight: %d, style: %d\n" " stretch: %d\n" - " pixelSize: %d\n" + " pixelSize: %g\n" " pitch: %c", family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(), foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp index fceb401..ef5e0c4 100644 --- a/src/gui/text/qfontdatabase_s60.cpp +++ b/src/gui/text/qfontdatabase_s60.cpp @@ -45,8 +45,8 @@ #include "qfontengine_s60_p.h" #include "qabstractfileengine.h" #include "qdesktopservices.h" -#include "qpixmap_s60_p.h" -#include "qt_s60_p.h" +#include <private/qpixmap_s60_p.h> +#include <private/qt_s60_p.h> #include "qendian.h" #include <private/qcore_symbian_p.h> #if defined(QT_NO_FREETYPE) diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index a6ceee1..c50d363 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -1111,7 +1111,6 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) return; #else - // supported from 2000 on, so no need to deal with the *A variant PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"), "AddFontResourceExW"); if (!ptrAddFontResourceExW diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index bb1e60d..b1ab478 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -41,9 +41,9 @@ #include <qplatformdefs.h> -#include <qdatetime.h> #include <qdebug.h> #include <qpaintdevice.h> +#include <qelapsedtimer.h> #include <private/qt_x11_p.h> #include "qx11info_x11.h" @@ -1218,7 +1218,7 @@ static void load(const QString &family = QString(), int script = -1, bool forceX } #ifdef QFONTDATABASE_DEBUG - QTime t; + QElapsedTimer t; t.start(); #endif @@ -1301,7 +1301,7 @@ static void initializeDb() if (!db || db->count) return; - QTime t; + QElapsedTimer t; t.start(); #ifndef QT_NO_FONTCONFIG @@ -1314,7 +1314,7 @@ static void initializeDb() } loadFontConfig(); - FD_DEBUG("QFontDatabase: loaded FontConfig: %d ms", t.elapsed()); + FD_DEBUG("QFontDatabase: loaded FontConfig: %d ms", int(t.elapsed())); #endif t.start(); diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 629db66..194c5f3 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -596,8 +596,9 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t) { QImage i = alphaMapForGlyph(glyph); if (t.type() > QTransform::TxTranslate) - i = i.transformed(t); + i = i.transformed(t).convertToFormat(QImage::Format_Indexed8); Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format... + return i; } @@ -606,11 +607,14 @@ QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int /* margin */, const Q QImage alphaMask = alphaMapForGlyph(glyph, t); QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32); + QVector<QRgb> colorTable = alphaMask.colorTable(); for (int y=0; y<alphaMask.height(); ++y) { uint *dst = (uint *) rgbMask.scanLine(y); uchar *src = (uchar *) alphaMask.scanLine(y); - for (int x=0; x<alphaMask.width(); ++x) - dst[x] = qRgb(src[x], src[x], src[x]); + for (int x=0; x<alphaMask.width(); ++x) { + int val = qAlpha(colorTable.at(src[x])); + dst[x] = qRgb(val, val, val); + } } return rgbMask; diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 17ade64..a9def8e 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -619,7 +619,7 @@ QFontEngineFT::QFontEngineFT(const QFontDef &fd) transform = false; antialias = true; freetype = 0; - default_load_flags = 0; + default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; default_hint_style = HintNone; subpixelType = Subpixel_None; lcdFilterType = 0; @@ -746,7 +746,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph) const { - Glyph *g = set->glyph_data.value(glyph); + Glyph *g = set->getGlyph(glyph); if (g) return g; @@ -858,10 +858,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph } } - Glyph *g = set->glyph_data.value(glyph); + Glyph *g = set->getGlyph(glyph); if (g && g->format == format) { if (uploadToServer && !g->uploadedToServer) { - set->glyph_data[glyph] = 0; + set->setGlyph(glyph, 0); delete g; g = 0; } else { @@ -1158,7 +1158,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph uploadGlyphToServer(set, glyph, g, &info, glyph_buffer_size); } - set->glyph_data[glyph] = g; + set->setGlyph(glyph, g); return g; } @@ -1381,8 +1381,7 @@ QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransfor } gs = &transformedGlyphSets[0]; - qDeleteAll(gs->glyph_data); - gs->glyph_data.clear(); + gs->clear(); gs->id = allocateServerGlyphSet(); @@ -1398,7 +1397,7 @@ bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, G FT_Face face = 0; for (int i = 0; i < num_glyphs; ++i) { - Glyph *glyph = gs->glyph_data.value(glyphs[i]); + Glyph *glyph = gs->getGlyph(glyphs[i]); if (glyph == 0 || glyph->format != format) { if (!face) { face = lockFace(); @@ -1635,7 +1634,7 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlag FT_Face face = 0; if (flags & QTextEngine::DesignMetrics) { for (int i = 0; i < glyphs->numGlyphs; i++) { - Glyph *g = defaultGlyphSet.glyph_data.value(glyphs->glyphs[i]); + Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]); if (g) { glyphs->advances_x[i] = QFixed::fromFixed(g->linearAdvance); } else { @@ -1648,7 +1647,7 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlag } } else { for (int i = 0; i < glyphs->numGlyphs; i++) { - Glyph *g = defaultGlyphSet.glyph_data.value(glyphs->glyphs[i]); + Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]); if (g) { glyphs->advances_x[i] = QFixed(g->advance); } else { @@ -1677,7 +1676,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) QFixed ymax = 0; QFixed xmax = 0; for (int i = 0; i < glyphs.numGlyphs; i++) { - Glyph *g = defaultGlyphSet.glyph_data.value(glyphs.glyphs[i]); + Glyph *g = defaultGlyphSet.getGlyph(glyphs.glyphs[i]); if (!g) { if (!face) face = lockFace(); @@ -1719,7 +1718,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) { FT_Face face = 0; glyph_metrics_t overall; - Glyph *g = defaultGlyphSet.glyph_data.value(glyph); + Glyph *g = defaultGlyphSet.getGlyph(glyph); if (!g) { face = lockFace(); g = loadGlyph(glyph, Format_None, true); @@ -1783,8 +1782,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matr transformedGlyphSets.prepend(QGlyphSet()); } glyphSet = &transformedGlyphSets[0]; - qDeleteAll(glyphSet->glyph_data); - glyphSet->glyph_data.clear(); + glyphSet->clear(); glyphSet->id = allocateServerGlyphSet(); glyphSet->transformationMatrix = m; } @@ -1792,7 +1790,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matr } else { glyphSet = &defaultGlyphSet; } - Glyph * g = glyphSet->glyph_data.value(glyph); + Glyph * g = glyphSet->getGlyph(glyph); if (!g) { face = lockFace(); g = loadGlyphMetrics(glyphSet, glyph); @@ -1881,7 +1879,7 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, int margin, const QTransfor void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) { - delete defaultGlyphSet.glyph_data.take(glyph); + defaultGlyphSet.removeGlyphFromCache(glyph); } int QFontEngineFT::glyphCount() const @@ -1937,11 +1935,53 @@ QFontEngineFT::QGlyphSet::QGlyphSet() transformationMatrix.yy = 0x10000; transformationMatrix.xy = 0; transformationMatrix.yx = 0; + memset(fast_glyph_data, 0, sizeof(fast_glyph_data)); + fast_glyph_count = 0; } QFontEngineFT::QGlyphSet::~QGlyphSet() { + clear(); +} + +void QFontEngineFT::QGlyphSet::clear() +{ + if (fast_glyph_count > 0) { + for (int i = 0; i < 256; ++i) { + if (fast_glyph_data[i]) { + delete fast_glyph_data[i]; + fast_glyph_data[i] = 0; + } + } + fast_glyph_count = 0; + } qDeleteAll(glyph_data); + glyph_data.clear(); +} + +void QFontEngineFT::QGlyphSet::removeGlyphFromCache(int index) +{ + if (index < 256) { + if (fast_glyph_data[index]) { + delete fast_glyph_data[index]; + fast_glyph_data[index] = 0; + if (fast_glyph_count > 0) + --fast_glyph_count; + } + } else { + delete glyph_data.take(index); + } +} + +void QFontEngineFT::QGlyphSet::setGlyph(int index, Glyph *glyph) +{ + if (index < 256) { + if (!fast_glyph_data[index]) + ++fast_glyph_count; + fast_glyph_data[index] = glyph; + } else { + glyph_data.insert(index, glyph); + } } unsigned long QFontEngineFT::allocateServerGlyphSet() diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index 98dd002..12b7da8 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -180,7 +180,21 @@ public: FT_Matrix transformationMatrix; unsigned long id; // server sided id, GlyphSet for X11 bool outline_drawing; + + void removeGlyphFromCache(int index); + void clear(); + inline Glyph *getGlyph(int index) const + { + if (index < 256) + return fast_glyph_data[index]; + return glyph_data.value(index); + } + void setGlyph(int index, Glyph *glyph); + +private: mutable QHash<int, Glyph *> glyph_data; // maps from glyph index to glyph data + mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 + mutable int fast_glyph_count; }; virtual QFontEngine::FaceId faceId() const; @@ -252,7 +266,7 @@ public: QGlyphSet *defaultGlyphs() { return &defaultGlyphSet; } GlyphFormat defaultGlyphFormat() const { return defaultFormat; } - inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.glyph_data.value(g); } + inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.getGlyph(g); } QGlyphSet *loadTransformedGlyphSet(const QTransform &matrix); bool loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format = Format_Render); diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 48bc635..0bfdbc0 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -53,6 +53,7 @@ #include <qvarlengtharray.h> #include <qdebug.h> #include <qendian.h> +#include <qmath.h> #include <ApplicationServices/ApplicationServices.h> #include <AppKit/AppKit.h> @@ -303,12 +304,20 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay outGlyphs[idx] = tmpGlyphs[i] | fontIndex; outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x); outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + outAdvances_x[idx] = outAdvances_x[idx].round(); + outAdvances_y[idx] = outAdvances_y[idx].round(); + } } CGSize lastGlyphAdvance; CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; - outAdvances_x[rtl ? 0 : (glyphCount - 1)] = QFixed::fromReal(lastGlyphAdvance.width).ceil(); + outAdvances_x[rtl ? 0 : (glyphCount - 1)] = + (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(lastGlyphAdvance.width).round() + : QFixed::fromReal(lastGlyphAdvance.width); } outGlyphs += glyphCount; outAttributes += glyphCount; @@ -378,8 +387,11 @@ bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int * glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) { QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0); } glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) @@ -393,33 +405,51 @@ glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) ret.y = -QFixed::fromReal(rect.origin.y) - ret.height; CGSize advances[1]; CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1); - ret.xoff = QFixed::fromReal(advances[0].width).ceil(); - ret.yoff = QFixed::fromReal(advances[0].height).ceil(); + ret.xoff = QFixed::fromReal(advances[0].width); + ret.yoff = QFixed::fromReal(advances[0].height); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + ret.xoff = ret.xoff.round(); + ret.yoff = ret.yoff.round(); + } + return ret; } QFixed QCoreTextFontEngine::ascent() const { - return QFixed::fromReal(CTFontGetAscent(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() + : QFixed::fromReal(CTFontGetAscent(ctfont)); } QFixed QCoreTextFontEngine::descent() const { + QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + d = d.round(); + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). // Fix in Qt 5. - return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil() - 1; + return d - 1; } QFixed QCoreTextFontEngine::leading() const { - return QFixed::fromReal(CTFontGetLeading(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() + : QFixed::fromReal(CTFontGetLeading(ctfont)); } QFixed QCoreTextFontEngine::xHeight() const { - return QFixed::fromReal(CTFontGetXHeight(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() + : QFixed::fromReal(CTFontGetXHeight(ctfont)); } QFixed QCoreTextFontEngine::averageCharWidth() const { // ### Need to implement properly and get the information from the OS/2 Table. - return QFontEngine::averageCharWidth(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFontEngine::averageCharWidth().round() + : QFontEngine::averageCharWidth(); } qreal QCoreTextFontEngine::maxCharWidth() const @@ -787,6 +817,7 @@ struct QGlyphLayoutInfo int *mappedFonts; QTextEngine::ShaperFlags flags; QFontEngineMacMulti::ShaperItem *shaperItem; + unsigned int styleStrategy; }; static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon, @@ -856,6 +887,11 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]); QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos); + if (nfo->styleStrategy & QFont::ForceIntegerMetrics) { + yAdvance = yAdvance.round(); + xAdvance = xAdvance.round(); + } + if (glyphId != 0xffff || i == 0) { if (i < nfo->glyphs->numGlyphs) { @@ -1032,6 +1068,7 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph nfo.callbackCalled = false; nfo.flags = flags; nfo.shaperItem = shaperItem; + nfo.styleStrategy = fontDef.styleStrategy; int prevNumGlyphs = *nglyphs; @@ -1061,8 +1098,6 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph | kATSLineDisableAllJustification ; - layopts |= kATSLineUseDeviceMetrics; - if (fontDef.styleStrategy & QFont::NoAntialias) layopts |= kATSLineNoAntiAliasing; @@ -1366,14 +1401,22 @@ void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla for (int i = 0; i < glyphs->numGlyphs; ++i) { glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x); glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = glyphs->advances_y[i].round(); + } } } glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs) { QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0); } @@ -1398,39 +1441,58 @@ glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph) gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x); gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + gm.x = gm.x.floor(); + gm.y = gm.y.floor(); + gm.xoff = gm.xoff.round(); + gm.yoff = gm.yoff.round(); + } + return gm; } QFixed QFontEngineMac::ascent() const { - return m_ascent; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_ascent.round() + : m_ascent; } QFixed QFontEngineMac::descent() const { // subtract a pixel to even out the historical +1 in QFontMetrics::height(). // Fix in Qt 5. - return m_descent - 1; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_descent.round() - 1 + : m_descent; } QFixed QFontEngineMac::leading() const { - return m_leading; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_leading.round() + : m_leading; } qreal QFontEngineMac::maxCharWidth() const { - return m_maxCharWidth; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(m_maxCharWidth) + : m_maxCharWidth; } QFixed QFontEngineMac::xHeight() const { - return m_xHeight; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_xHeight.round() + : m_xHeight; } QFixed QFontEngineMac::averageCharWidth() const { - return m_averageCharWidth; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_averageCharWidth.round() + : m_averageCharWidth; } static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path) diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp index 136737d..a0593cc 100644 --- a/src/gui/text/qfontengine_qpf.cpp +++ b/src/gui/text/qfontengine_qpf.cpp @@ -920,8 +920,18 @@ void QFontEngineQPF::loadGlyph(glyph_t glyph) if (!renderingFontEngine) return; - - QImage img = renderingFontEngine->alphaMapForGlyph(glyph).convertToFormat(QImage::Format_Indexed8); + QImage img = renderingFontEngine->alphaMapForGlyph(glyph); + if (img.format() != QImage::Format_Indexed8) { + bool mono = img.depth() == 1; + img = img.convertToFormat(QImage::Format_Indexed8); + if (mono) { + //### we know that 1 is opaque and 0 is transparent + uchar *byte = img.bits(); + int count = img.byteCount(); + while (count--) + *byte++ *= 0xff; + } + } glyph_metrics_t metrics = renderingFontEngine->boundingBox(glyph); renderingFontEngine->removeGlyphFromCache(glyph); diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index eea196e..a805612 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -213,7 +213,7 @@ void QFontEngineWin::getCMap() unitsPerEm = otm->otmEMSquare; x_height = (int)otm->otmsXHeight; loadKerningPairs(designToDevice); - _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (int)otm->otmpFullName)).toLatin1(); + _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1(); lineWidth = otm->otmsUnderscoreSize; fsType = otm->otmfsType; free(otm); @@ -1037,8 +1037,8 @@ QFontEngine::Properties QFontEngineWin::properties() const Properties p; p.emSquare = unitsPerEm; p.italicAngle = otm->otmItalicAngle; - p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (int)otm->otmpFamilyName)).toLatin1(); - p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (int)otm->otmpStyleName)).toLatin1(); + p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFamilyName)).toLatin1(); + p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpStyleName)).toLatin1(); #ifndef QT_NO_PRINTER p.postscriptName = QPdf::stripSpecialCharacters(p.postscriptName); #endif diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 44a18de..3a52e8e 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -328,7 +328,7 @@ int QFontMetrics::height() const { QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); Q_ASSERT(engine != 0); - return qRound(engine->ascent() + engine->descent()) + 1; + return qRound(engine->ascent()) + qRound(engine->descent()) + 1; } /*! @@ -356,7 +356,7 @@ int QFontMetrics::lineSpacing() const { QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); Q_ASSERT(engine != 0); - return qRound(engine->leading() + engine->ascent() + engine->descent()) + 1; + return qRound(engine->leading()) + qRound(engine->ascent()) + qRound(engine->descent()) + 1; } /*! @@ -535,7 +535,7 @@ int QFontMetrics::width(const QString &text, int len) const if (len == 0) return 0; - QTextEngine layout(text, d.data()); + QStackTextEngine layout(text, d.data()); layout.ignoreBidi = true; return qRound(layout.width(0, len)); } @@ -611,7 +611,7 @@ int QFontMetrics::charWidth(const QString &text, int pos) const int from = qMax(0, pos - 8); int to = qMin(text.length(), pos + 8); QString cstr = QString::fromRawData(text.unicode() + from, to - from); - QTextEngine layout(cstr, d.data()); + QStackTextEngine layout(cstr, d.data()); layout.ignoreBidi = true; layout.itemize(); width = qRound(layout.width(pos-from, 1)); @@ -660,7 +660,7 @@ QRect QFontMetrics::boundingRect(const QString &text) const if (text.length() == 0) return QRect(); - QTextEngine layout(text, d.data()); + QStackTextEngine layout(text, d.data()); layout.ignoreBidi = true; layout.itemize(); glyph_metrics_t gm = layout.boundingBox(0, text.length()); @@ -830,7 +830,7 @@ QRect QFontMetrics::tightBoundingRect(const QString &text) const if (text.length() == 0) return QRect(); - QTextEngine layout(text, d.data()); + QStackTextEngine layout(text, d.data()); layout.ignoreBidi = true; layout.itemize(); glyph_metrics_t gm = layout.tightBoundingBox(0, text.length()); @@ -1375,7 +1375,7 @@ qreal QFontMetricsF::width(const QString &text) const int pos = text.indexOf(QLatin1Char('\x9c')); int len = (pos != -1) ? pos : text.length(); - QTextEngine layout(text, d.data()); + QStackTextEngine layout(text, d.data()); layout.ignoreBidi = true; layout.itemize(); return layout.width(0, len).toReal(); @@ -1452,7 +1452,7 @@ QRectF QFontMetricsF::boundingRect(const QString &text) const if (len == 0) return QRectF(); - QTextEngine layout(text, d.data()); + QStackTextEngine layout(text, d.data()); layout.ignoreBidi = true; layout.itemize(); glyph_metrics_t gm = layout.boundingBox(0, len); @@ -1625,7 +1625,7 @@ QRectF QFontMetricsF::tightBoundingRect(const QString &text) const if (text.length() == 0) return QRect(); - QTextEngine layout(text, d.data()); + QStackTextEngine layout(text, d.data()); layout.ignoreBidi = true; layout.itemize(); glyph_metrics_t gm = layout.tightBoundingBox(0, text.length()); diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp new file mode 100644 index 0000000..5f31ef0 --- /dev/null +++ b/src/gui/text/qstatictext.cpp @@ -0,0 +1,654 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 "qstatictext.h" +#include "qstatictext_p.h" +#include <private/qtextengine_p.h> +#include <private/qfontengine_p.h> + +#include <QtGui/qapplication.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QStaticText + \brief The QStaticText class enables optimized drawing of text when the text and its layout + is updated rarely. + \since 4.7 + + \ingroup multimedia + \ingroup text + \mainclass + + QStaticText provides a way to cache layout data for a block of text so that it can be drawn + more efficiently than by using QPainter::drawText() in which the layout information is + recalculated with every call. + + The class primarily provides an optimization for cases where the text, its font and the + transformations on the painter are static over several paint events. If the text or its layout + is changed for every iteration, QPainter::drawText() is the more efficient alternative, since + the static text's layout would have to be recalculated to take the new state into consideration. + + Translating the painter will not cause the layout of the text to be recalculated, but will cause + a very small performance impact on drawStaticText(). Altering any other parts of the painter's + transformation or the painter's font will cause the layout of the static text to be + recalculated. This should be avoided as often as possible to maximize the performance + benefit of using QStaticText. + + In addition, only affine transformations are supported by drawStaticText(). Calling + drawStaticText() on a projected painter will perform slightly worse than using the regular + drawText() call, so this should be avoided. + + \code + class MyWidget: public QWidget + { + public: + MyWidget(QWidget *parent = 0) : QWidget(parent), m_staticText("This is static text") + + protected: + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + painter.drawStaticText(0, 0, m_staticText); + } + + private: + QStaticText m_staticText; + }; + \endcode + + The QStaticText class can be used to mimic the behavior of QPainter::drawText() to a specific + point with no boundaries, and also when QPainter::drawText() is called with a bounding + rectangle. + + If a bounding rectangle is not required, create a QStaticText object without setting a preferred + text width. The text will then occupy a single line. + + If you set a text width on the QStaticText object, this will bound the text. The text will + be formatted so that no line exceeds the given width. The text width set for QStaticText will + not automatically be used for clipping. To achieve clipping in addition to line breaks, use + QPainter::setClipRect(). The position of the text is decided by the argument passed to + QPainter::drawStaticText() and can change from call to call with a minimal impact on + performance. + + QStaticText will attempt to guess the format of the input text using Qt::mightBeRichText(). + To force QStaticText to display its contents as either plain text or rich text, use the + function QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and + Qt::RichText. + + If it's the first time the static text is drawn, or if the static text, or the painter's font + or matrix have been altered since the last time it was drawn, the text's layout has to be + recalculated. This will impose an overhead on the QPainter::drawStaticText() call where the + relayout occurs. To avoid this overhead in the paint event, you can call prepare() ahead of + time to ensure that the layout is calculated. + + \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument +*/ + +/*! + \enum QStaticText::PerformanceHint + + This enum the different performance hints that can be set on the QStaticText. These hints + can be used to indicate that the QStaticText should use additional caches, if possible, + to improve performance at the expense of memory. In particular, setting the performance hint + AggressiveCaching on the QStaticText will improve performance when using the OpenGL graphics + system or when drawing to a QGLWidget. + + \value ModerateCaching Do basic caching for high performance at a low memory cost. + \value AggressiveCaching Use additional caching when available. This may improve performance + at a higher memory cost. +*/ + +/*! + Constructs an empty QStaticText +*/ +QStaticText::QStaticText() + : data(new QStaticTextPrivate) +{ +} + +/*! + Constructs a QStaticText object with the given \a text and bounded by the given \a size. + + If an invalid size is passed for \a size the text will be unbounded. +*/ +QStaticText::QStaticText(const QString &text) + : data(new QStaticTextPrivate) +{ + data->text = text; + data->invalidate(); +} + +/*! + Constructs a QStaticText object which is a copy of \a other. +*/ +QStaticText::QStaticText(const QStaticText &other) +{ + data = other.data; +} + +/*! + Destroys the QStaticText. +*/ +QStaticText::~QStaticText() +{ + Q_ASSERT(!data || data->ref >= 1); +} + +/*! + \internal +*/ +void QStaticText::detach() +{ + if (data->ref != 1) + data.detach(); +} + +/*! + Prepares the QStaticText object for being painted with the given \a matrix and the given \a font + to avoid overhead when the actual drawStaticText() call is made. + + When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part + of the QStaticText object has changed since the last time it was drawn. It will also be + recalculated if the painter's font or matrix are not the same as when the QStaticText was last + drawn. + + To avoid the overhead of creating the layout the first time you draw the QStaticText after + making changes, you can use the prepare() function and pass in the \a matrix and \a font you + expect to use when drawing the text. + + \sa QPainter::setFont(), QPainter::setMatrix() +*/ +void QStaticText::prepare(const QTransform &matrix, const QFont &font) +{ + data->matrix = matrix; + data->font = font; + data->init(); +} + + +/*! + Assigns \a other to this QStaticText. +*/ +QStaticText &QStaticText::operator=(const QStaticText &other) +{ + data = other.data; + return *this; +} + +/*! + Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths + are equal. +*/ +bool QStaticText::operator==(const QStaticText &other) const +{ + return (data == other.data + || (data->text == other.data->text + && data->font == other.data->font + && data->textWidth == other.data->textWidth)); +} + +/*! + Compares \a other to this QStaticText. Returns true if the texts, fonts or maximum sizes + are different. +*/ +bool QStaticText::operator!=(const QStaticText &other) const +{ + return !(*this == other); +} + +/*! + Sets the text of the QStaticText to \a text. + + \note This function will cause the layout of the text to require recalculation. + + \sa text() +*/ +void QStaticText::setText(const QString &text) +{ + detach(); + data->text = text; + data->invalidate(); +} + +/*! + Sets the text format of the QStaticText to \a textFormat. If \a textFormat is set to + Qt::AutoText (the default), the format of the text will try to be determined using the + function Qt::mightBeRichText(). If the text format is Qt::PlainText, then the text will be + displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags + that alter the font of the text, its color, or its layout are supported by QStaticText. + + \note This function will cause the layout of the text to require recalculation. + + \sa textFormat(), setText(), text() +*/ +void QStaticText::setTextFormat(Qt::TextFormat textFormat) +{ + detach(); + data->textFormat = textFormat; + data->invalidate(); +} + +/*! + Returns the text format of the QStaticText. + + \sa setTextFormat(), setText(), text() +*/ +Qt::TextFormat QStaticText::textFormat() const +{ + return Qt::TextFormat(data->textFormat); +} + +/*! + Returns the text of the QStaticText. + + \sa setText() +*/ +QString QStaticText::text() const +{ + return data->text; +} + +/*! + Sets the performance hint of the QStaticText according to the \a + performanceHint provided. The \a performanceHint is used to + customize how much caching is done internally to improve + performance. + + The default is QStaticText::ModerateCaching. + + \note This function will cause the layout of the text to require recalculation. + + \sa performanceHint() +*/ +void QStaticText::setPerformanceHint(PerformanceHint performanceHint) +{ + if ((performanceHint == ModerateCaching && !data->useBackendOptimizations) + || (performanceHint == AggressiveCaching && data->useBackendOptimizations)) { + return; + } + detach(); + data->useBackendOptimizations = (performanceHint == AggressiveCaching); + data->invalidate(); +} + +/*! + Returns which performance hint is set for the QStaticText. + + \sa setPerformanceHint() +*/ +QStaticText::PerformanceHint QStaticText::performanceHint() const +{ + return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching; +} + +/*! + Sets the preferred width for this QStaticText. If the text is wider than the specified width, + it will be broken into multiple lines and grow vertically. If the text cannot be split into + multiple lines, it will be larger than the specified \a textWidth. + + Setting the preferred text width to a negative number will cause the text to be unbounded. + + Use size() to get the actual size of the text. + + \note This function will cause the layout of the text to require recalculation. + + \sa textWidth(), size() +*/ +void QStaticText::setTextWidth(qreal textWidth) +{ + detach(); + data->textWidth = textWidth; + data->invalidate(); +} + +/*! + Returns the preferred width for this QStaticText. + + \sa setTextWidth() +*/ +qreal QStaticText::textWidth() const +{ + return data->textWidth; +} + +/*! + Returns the size of the bounding rect for this QStaticText. + + \sa textWidth() +*/ +QSizeF QStaticText::size() const +{ + if (data->needsRelayout) + data->init(); + return data->actualSize; +} + +QStaticTextPrivate::QStaticTextPrivate() + : items(0), itemCount(0), glyphPool(0), positionPool(0), textWidth(-1.0), + needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText) +{ +} + +QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) + : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix), + items(0), itemCount(0), glyphPool(0), positionPool(0), needsRelayout(true), + useBackendOptimizations(other.useBackendOptimizations), textFormat(other.textFormat) +{ +} + +QStaticTextPrivate::~QStaticTextPrivate() +{ + delete[] items; + delete[] glyphPool; + delete[] positionPool; +} + +QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q) +{ + return q->data.data(); +} + +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); + +namespace { + + class DrawTextItemRecorder: public QPaintEngine + { + public: + DrawTextItemRecorder(int expectedItemCount, QStaticTextItem *items, + int expectedGlyphCount, QFixedPoint *positionPool, glyph_t *glyphPool) + : m_items(items), + m_itemCount(0), m_glyphCount(0), + m_expectedItemCount(expectedItemCount), + m_expectedGlyphCount(expectedGlyphCount), + m_glyphPool(glyphPool), + m_positionPool(positionPool), + m_dirtyPen(false) + { + } + + virtual void updateState(const QPaintEngineState &newState) + { + if (newState.state() & QPaintEngine::DirtyPen) + m_dirtyPen = true; + } + + virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) + { + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + + m_itemCount++; + m_glyphCount += ti.glyphs.numGlyphs; + if (m_items == 0) + return; + + Q_ASSERT(m_itemCount <= m_expectedItemCount); + Q_ASSERT(m_glyphCount <= m_expectedGlyphCount); + + QStaticTextItem *currentItem = (m_items + (m_itemCount - 1)); + currentItem->fontEngine = ti.fontEngine; + currentItem->font = ti.font(); + currentItem->chars = ti.chars; + currentItem->numChars = ti.num_chars; + currentItem->numGlyphs = ti.glyphs.numGlyphs; + currentItem->glyphs = m_glyphPool; + currentItem->glyphPositions = m_positionPool; + if (m_dirtyPen) + currentItem->color = state->pen().color(); + + QTransform matrix = state->transform(); + matrix.translate(position.x(), position.y()); + + QVarLengthArray<glyph_t> glyphs; + QVarLengthArray<QFixedPoint> positions; + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + + int size = glyphs.size(); + Q_ASSERT(size == ti.glyphs.numGlyphs); + Q_ASSERT(size == positions.size()); + + memmove(currentItem->glyphs, glyphs.constData(), sizeof(glyph_t) * size); + memmove(currentItem->glyphPositions, positions.constData(), sizeof(QFixedPoint) * size); + + m_glyphPool += size; + m_positionPool += size; + } + + + virtual bool begin(QPaintDevice *) { return true; } + virtual bool end() { return true; } + virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} + virtual Type type() const + { + return User; + } + + int itemCount() const + { + return m_itemCount; + } + + int glyphCount() const + { + return m_glyphCount; + } + + private: + QStaticTextItem *m_items; + int m_itemCount; + int m_glyphCount; + int m_expectedItemCount; + int m_expectedGlyphCount; + + glyph_t *m_glyphPool; + QFixedPoint *m_positionPool; + + bool m_dirtyPen; + }; + + class DrawTextItemDevice: public QPaintDevice + { + public: + DrawTextItemDevice(int expectedItemCount = -1, QStaticTextItem *items = 0, + int expectedGlyphCount = -1, QFixedPoint *positionPool = 0, + glyph_t *glyphPool = 0) + { + m_paintEngine = new DrawTextItemRecorder(expectedItemCount, items, + expectedGlyphCount, positionPool, glyphPool); + } + + ~DrawTextItemDevice() + { + delete m_paintEngine; + } + + int metric(PaintDeviceMetric m) const + { + int val; + switch (m) { + case PdmWidth: + case PdmHeight: + case PdmWidthMM: + case PdmHeightMM: + val = 0; + break; + case PdmDpiX: + case PdmPhysicalDpiX: + val = qt_defaultDpiX(); + break; + case PdmDpiY: + case PdmPhysicalDpiY: + val = qt_defaultDpiY(); + break; + case PdmNumColors: + val = 16777216; + break; + case PdmDepth: + val = 24; + break; + default: + val = 0; + qWarning("DrawTextItemDevice::metric: Invalid metric command"); + } + return val; + } + + virtual QPaintEngine *paintEngine() const + { + return m_paintEngine; + } + + int itemCount() const + { + return m_paintEngine->itemCount(); + } + + int glyphCount() const + { + return m_paintEngine->glyphCount(); + } + + private: + DrawTextItemRecorder *m_paintEngine; + }; +} + +void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p) +{ + bool preferRichText = textFormat == Qt::RichText + || (textFormat == Qt::AutoText && Qt::mightBeRichText(text)); + + if (!preferRichText) { + QTextLayout textLayout; + textLayout.setText(text); + textLayout.setFont(font); + + qreal leading = QFontMetricsF(font).leading(); + qreal height = -leading; + + textLayout.beginLayout(); + while (1) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + + if (textWidth >= 0.0) + line.setLineWidth(textWidth); + height += leading; + line.setPosition(QPointF(0.0, height)); + height += line.height(); + } + textLayout.endLayout(); + + actualSize = textLayout.boundingRect().size(); + textLayout.draw(p, topLeftPosition); + } else { + QTextDocument document; + QColor color = p->pen().color(); + document.setDefaultStyleSheet(QString::fromLatin1("body { color: #%1%2%3 }") + .arg(QString::number(color.red(), 16), 2, QLatin1Char('0')) + .arg(QString::number(color.green(), 16), 2, QLatin1Char('0')) + .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0'))); + document.setDefaultFont(font); + document.setDocumentMargin(0.0); + if (textWidth >= 0.0) + document.setTextWidth(textWidth); + document.setHtml(text); + + document.adjustSize(); + p->save(); + p->translate(topLeftPosition); + document.drawContents(p); + p->restore(); + + actualSize = document.size(); + } +} + +void QStaticTextPrivate::init() +{ + delete[] items; + delete[] glyphPool; + delete[] positionPool; + + position = QPointF(0, 0); + + // Draw once to count number of items and glyphs, so that we can use as little memory + // as possible to store the data + DrawTextItemDevice counterDevice; + { + QPainter painter(&counterDevice); + painter.setFont(font); + painter.setTransform(matrix); + + paintText(QPointF(0, 0), &painter); + + } + + itemCount = counterDevice.itemCount(); + items = new QStaticTextItem[itemCount]; + + if (useBackendOptimizations) { + for (int i=0; i<itemCount; ++i) + items[i].useBackendOptimizations = true; + } + + + int glyphCount = counterDevice.glyphCount(); + glyphPool = new glyph_t[glyphCount]; + positionPool = new QFixedPoint[glyphCount]; + + // Draw again to actually record the items and glyphs + DrawTextItemDevice recorderDevice(itemCount, items, glyphCount, positionPool, glyphPool); + { + QPainter painter(&recorderDevice); + painter.setFont(font); + painter.setTransform(matrix); + + paintText(QPointF(0, 0), &painter); + } + + needsRelayout = false; +} + +QT_END_NAMESPACE diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h new file mode 100644 index 0000000..f3bef93 --- /dev/null +++ b/src/gui/text/qstatictext.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +#ifndef QSTATICTEXT_H +#define QSTATICTEXT_H + +#include <QtCore/qsize.h> +#include <QtCore/qstring.h> +#include <QtCore/qmetatype.h> + +#include <QtGui/qtransform.h> +#include <QtGui/qfont.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QStaticTextPrivate; +class Q_GUI_EXPORT QStaticText +{ +public: + enum PerformanceHint { + ModerateCaching, + AggressiveCaching + }; + + QStaticText(); + QStaticText(const QString &text); + QStaticText(const QStaticText &other); + ~QStaticText(); + + void setText(const QString &text); + QString text() const; + + void setTextFormat(Qt::TextFormat textFormat); + Qt::TextFormat textFormat() const; + + void setTextWidth(qreal textWidth); + qreal textWidth() const; + + QSizeF size() const; + + void prepare(const QTransform &matrix = QTransform(), const QFont &font = QFont()); + + void setPerformanceHint(PerformanceHint performanceHint); + PerformanceHint performanceHint() const; + + QStaticText &operator=(const QStaticText &); + bool operator==(const QStaticText &) const; + bool operator!=(const QStaticText &) const; + +private: + void detach(); + + QExplicitlySharedDataPointer<QStaticTextPrivate> data; + friend class QStaticTextPrivate; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QStaticText) + +QT_END_HEADER + +#endif // QSTATICTEXT_H diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h new file mode 100644 index 0000000..f017ed1 --- /dev/null +++ b/src/gui/text/qstatictext_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +#ifndef QSTATICTEXT_P_H +#define QSTATICTEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtextureglyphcache_p.h> +#include <QtGui/qcolor.h> + +QT_BEGIN_NAMESPACE + +class QStaticTextUserData +{ +public: + enum Type { + NoUserData, + OpenGLUserData + }; + + QStaticTextUserData(Type t) : type(t) {} + virtual ~QStaticTextUserData() {} + + Type type; +}; + +class Q_GUI_EXPORT QStaticTextItem +{ +public: + QStaticTextItem() : chars(0), numChars(0), fontEngine(0), userData(0), + useBackendOptimizations(false), userDataNeedsUpdate(0) {} + ~QStaticTextItem() { delete userData; } + + void setUserData(QStaticTextUserData *newUserData) + { + if (userData == newUserData) + return; + + delete userData; + userData = newUserData; + } + + QFixedPoint *glyphPositions; // 8 bytes per glyph + glyph_t *glyphs; // 4 bytes per glyph + const QChar *chars; // 2 bytes per glyph + // ================= + // 14 bytes per glyph + + // 12 bytes for pointers + int numGlyphs; // 4 bytes per item + int numChars; // 4 bytes per item + QFontEngine *fontEngine; // 4 bytes per item + QFont font; // 8 bytes per item + QColor color; // 10 bytes per item + QStaticTextUserData *userData; // 8 bytes per item + char useBackendOptimizations : 1; // 1 byte per item + char userDataNeedsUpdate : 1; // + // ================ + // 51 bytes per item +}; + +class QStaticText; +class Q_AUTOTEST_EXPORT QStaticTextPrivate +{ +public: + QStaticTextPrivate(); + QStaticTextPrivate(const QStaticTextPrivate &other); + ~QStaticTextPrivate(); + + void init(); + void paintText(const QPointF &pos, QPainter *p); + + void invalidate() + { + needsRelayout = true; + } + + QAtomicInt ref; // 4 bytes per text + + QString text; // 4 bytes per text + QFont font; // 8 bytes per text + qreal textWidth; // 8 bytes per text + QSizeF actualSize; // 16 bytes per text + QPointF position; // 16 bytes per text + + QTransform matrix; // 80 bytes per text + QStaticTextItem *items; // 4 bytes per text + int itemCount; // 4 bytes per text + glyph_t *glyphPool; // 4 bytes per text + QFixedPoint *positionPool; // 4 bytes per text + + unsigned char needsRelayout : 1; + unsigned char useBackendOptimizations : 1; // 1 byte per text + unsigned char textFormat : 2; + // ================ + // 163 bytes per text + + static QStaticTextPrivate *get(const QStaticText *q); +}; + +QT_END_NAMESPACE + +#endif // QSTATICTEXT_P_H diff --git a/src/gui/text/qsyntaxhighlighter.cpp b/src/gui/text/qsyntaxhighlighter.cpp index 6750c09..e594b7e 100644 --- a/src/gui/text/qsyntaxhighlighter.cpp +++ b/src/gui/text/qsyntaxhighlighter.cpp @@ -59,23 +59,24 @@ class QSyntaxHighlighterPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QSyntaxHighlighter) public: - inline QSyntaxHighlighterPrivate() : rehighlightPending(false) {} + inline QSyntaxHighlighterPrivate() + : rehighlightPending(false), inReformatBlocks(false) + {} QPointer<QTextDocument> doc; void _q_reformatBlocks(int from, int charsRemoved, int charsAdded); - void reformatBlock(QTextBlock block); - + void reformatBlocks(int from, int charsRemoved, int charsAdded); + void reformatBlock(const QTextBlock &block); + inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) { - QObject::disconnect(doc, SIGNAL(contentsChange(int,int,int)), - q_func(), SLOT(_q_reformatBlocks(int,int,int))); + inReformatBlocks = true; cursor.beginEditBlock(); int from = cursor.position(); cursor.movePosition(operation); - _q_reformatBlocks(from, 0, cursor.position() - from); + reformatBlocks(from, 0, cursor.position() - from); cursor.endEditBlock(); - QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), - q_func(), SLOT(_q_reformatBlocks(int,int,int))); + inReformatBlocks = false; } inline void _q_delayedRehighlight() { @@ -83,17 +84,19 @@ public: return; rehighlightPending = false; q_func()->rehighlight(); - return; } void applyFormatChanges(); QVector<QTextCharFormat> formatChanges; QTextBlock currentBlock; bool rehighlightPending; + bool inReformatBlocks; }; void QSyntaxHighlighterPrivate::applyFormatChanges() { + bool formatsChanged = false; + QTextLayout *layout = currentBlock.layout(); QList<QTextLayout::FormatRange> ranges = layout->additionalFormats(); @@ -101,19 +104,26 @@ void QSyntaxHighlighterPrivate::applyFormatChanges() const int preeditAreaStart = layout->preeditAreaPosition(); const int preeditAreaLength = layout->preeditAreaText().length(); - QList<QTextLayout::FormatRange>::Iterator it = ranges.begin(); - while (it != ranges.end()) { - if (it->start >= preeditAreaStart - && it->start + it->length <= preeditAreaStart + preeditAreaLength) - ++it; - else - it = ranges.erase(it); + if (preeditAreaLength != 0) { + QList<QTextLayout::FormatRange>::Iterator it = ranges.begin(); + while (it != ranges.end()) { + if (it->start >= preeditAreaStart + && it->start + it->length <= preeditAreaStart + preeditAreaLength) { + ++it; + } else { + it = ranges.erase(it); + formatsChanged = true; + } + } + } else if (!ranges.isEmpty()) { + ranges.clear(); + formatsChanged = true; } QTextCharFormat emptyFormat; QTextLayout::FormatRange r; - r.start = r.length = -1; + r.start = -1; int i = 0; while (i < formatChanges.count()) { @@ -135,34 +145,46 @@ void QSyntaxHighlighterPrivate::applyFormatChanges() r.length = i - r.start; - if (r.start >= preeditAreaStart) { - r.start += preeditAreaLength; - } else if (r.start + r.length >= preeditAreaStart) { - r.length += preeditAreaLength; + if (preeditAreaLength != 0) { + if (r.start >= preeditAreaStart) + r.start += preeditAreaLength; + else if (r.start + r.length >= preeditAreaStart) + r.length += preeditAreaLength; } ranges << r; - r.start = r.length = -1; + formatsChanged = true; + r.start = -1; } if (r.start != -1) { r.length = formatChanges.count() - r.start; - if (r.start >= preeditAreaStart) { - r.start += preeditAreaLength; - } else if (r.start + r.length >= preeditAreaStart) { - r.length += preeditAreaLength; + if (preeditAreaLength != 0) { + if (r.start >= preeditAreaStart) + r.start += preeditAreaLength; + else if (r.start + r.length >= preeditAreaStart) + r.length += preeditAreaLength; } ranges << r; + formatsChanged = true; } - layout->setAdditionalFormats(ranges); + if (formatsChanged) { + layout->setAdditionalFormats(ranges); + doc->markContentsDirty(currentBlock.position(), currentBlock.length()); + } } void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded) { - Q_UNUSED(charsRemoved); + if (!inReformatBlocks) + reformatBlocks(from, charsRemoved, charsAdded); +} + +void QSyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded) +{ rehighlightPending = false; QTextBlock block = doc->findBlock(from); @@ -191,21 +213,18 @@ void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, in formatChanges.clear(); } -void QSyntaxHighlighterPrivate::reformatBlock(QTextBlock block) +void QSyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block) { Q_Q(QSyntaxHighlighter); Q_ASSERT_X(!currentBlock.isValid(), "QSyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively"); currentBlock = block; - QTextBlock previous = block.previous(); formatChanges.fill(QTextCharFormat(), block.length() - 1); q->highlightBlock(block.text()); applyFormatChanges(); - doc->markContentsDirty(block.position(), block.length()); - currentBlock = QTextBlock(); } @@ -349,8 +368,8 @@ void QSyntaxHighlighter::setDocument(QTextDocument *doc) if (d->doc) { connect(d->doc, SIGNAL(contentsChange(int,int,int)), this, SLOT(_q_reformatBlocks(int,int,int))); - QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight())); d->rehighlightPending = true; + QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight())); } } @@ -391,11 +410,16 @@ void QSyntaxHighlighter::rehighlight() void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block) { Q_D(QSyntaxHighlighter); - if (!d->doc) + if (!d->doc || !block.isValid() || block.document() != d->doc) return; + const bool rehighlightPending = d->rehighlightPending; + QTextCursor cursor(block); d->rehighlight(cursor, QTextCursor::EndOfBlock); + + if (rehighlightPending) + d->rehighlightPending = rehighlightPending; } /*! @@ -460,7 +484,6 @@ void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block) void QSyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format) { Q_D(QSyntaxHighlighter); - if (start < 0 || start >= d->formatChanges.count()) return; @@ -628,7 +651,7 @@ QTextBlockUserData *QSyntaxHighlighter::currentBlockUserData() const \since 4.4 Returns the current text block. - */ +*/ QTextBlock QSyntaxHighlighter::currentBlock() const { Q_D(const QSyntaxHighlighter); diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp index aaaa3ca..6864fe1 100644 --- a/src/gui/text/qtextcontrol.cpp +++ b/src/gui/text/qtextcontrol.cpp @@ -441,7 +441,6 @@ void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged())); // convenience signal forwards - QObject::connect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged())); QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool))); QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool))); QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool))); @@ -452,8 +451,11 @@ void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, if (!document) doc->setUndoRedoEnabled(false); + //Saving the index save some time. + static int contentsChangedIndex = QTextDocument::staticMetaObject.indexOfSignal("contentsChanged()"); + static int textChangedIndex = QTextControl::staticMetaObject.indexOfSignal("textChanged()"); // avoid multiple textChanged() signals being emitted - QObject::disconnect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged())); + QMetaObject::disconnect(doc, contentsChangedIndex, q, textChangedIndex); if (!text.isEmpty()) { // clear 'our' cursor for insertion to prevent @@ -488,7 +490,7 @@ void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, } cursor.setCharFormat(charFormatForInsertion); - QObject::connect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged())); + QMetaObject::connect(doc, contentsChangedIndex, q, textChangedIndex); emit q->textChanged(); if (!document) doc->setUndoRedoEnabled(previousUndoRedoState); @@ -846,9 +848,9 @@ void QTextControl::copy() QApplication::clipboard()->setMimeData(data); } -void QTextControl::paste() +void QTextControl::paste(QClipboard::Mode mode) { - const QMimeData *md = QApplication::clipboard()->mimeData(); + const QMimeData *md = QApplication::clipboard()->mimeData(mode); if (md) insertFromMimeData(md); } @@ -1228,7 +1230,12 @@ void QTextControlPrivate::keyPressEvent(QKeyEvent *e) q->cut(); } else if (e == QKeySequence::Paste) { - q->paste(); + QClipboard::Mode mode = QClipboard::Clipboard; +#ifdef Q_WS_X11 + if (e->modifiers() == (Qt::CTRL | Qt::SHIFT) && e->key() == Qt::Key_Insert) + mode = QClipboard::Selection; +#endif + q->paste(mode); } #endif else if (e == QKeySequence::Delete) { @@ -1762,8 +1769,8 @@ void QTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const QPoint QMenu *menu = q->createStandardContextMenu(docPos, contextWidget); if (!menu) return; - menu->exec(screenPos); - delete menu; + menu->setAttribute(Qt::WA_DeleteOnClose); + menu->popup(screenPos); #endif } diff --git a/src/gui/text/qtextcontrol_p.h b/src/gui/text/qtextcontrol_p.h index 40507f4..8399d50 100644 --- a/src/gui/text/qtextcontrol_p.h +++ b/src/gui/text/qtextcontrol_p.h @@ -62,6 +62,7 @@ #include <QtCore/qrect.h> #include <QtGui/qabstracttextdocumentlayout.h> #include <QtGui/qtextdocumentfragment.h> +#include <QtGui/qclipboard.h> #ifdef QT3_SUPPORT #include <QtGui/qtextobject.h> @@ -191,7 +192,7 @@ public Q_SLOTS: #ifndef QT_NO_CLIPBOARD void cut(); void copy(); - void paste(); + void paste(QClipboard::Mode mode = QClipboard::Clipboard); #endif void undo(); diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp index c81d538..c91df3c 100644 --- a/src/gui/text/qtextcursor.cpp +++ b/src/gui/text/qtextcursor.cpp @@ -1145,7 +1145,7 @@ void QTextCursor::setPosition(int pos, MoveMode m) Returns the absolute position of the cursor within the document. The cursor is positioned between characters. - \sa setPosition() movePosition() anchor() + \sa setPosition() movePosition() anchor() positionInBlock() */ int QTextCursor::position() const { @@ -1155,6 +1155,22 @@ int QTextCursor::position() const } /*! + \since 4.7 + Returns the relative position of the cursor within the block. + The cursor is positioned between characters. + + This is equivalent to \c{ position() - block().position()}. + + \sa position() +*/ +int QTextCursor::positionInBlock() const +{ + if (!d || !d->priv) + return 0; + return d->position - d->block().position(); +} + +/*! Returns the anchor position; this is the same as position() unless there is a selection in which case position() marks one end of the selection and anchor() marks the other end. Just like the cursor @@ -2414,9 +2430,17 @@ int QTextCursor::blockNumber() const return d->block().blockNumber(); } + /*! \since 4.2 Returns the position of the cursor within its containing line. + + Note that this is the column number relative to a wrapped line, + not relative to the block (i.e. the paragraph). + + You probably want to call positionInBlock() instead. + + \sa positionInBlock() */ int QTextCursor::columnNumber() const { diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h index 38bc39d..3e968a3 100644 --- a/src/gui/text/qtextcursor.h +++ b/src/gui/text/qtextcursor.h @@ -89,6 +89,7 @@ public: void setPosition(int pos, MoveMode mode = MoveAnchor); int position() const; + int positionInBlock() const; int anchor() const; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 21f3189..9a1b70c 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -61,6 +61,7 @@ #include <qapplication.h> #include "qtextcontrol_p.h" #include "private/qtextedit_p.h" +#include "private/qdataurl_p.h" #include "qtextdocument_p.h" #include <private/qprinter_p.h> @@ -434,6 +435,30 @@ void QTextDocument::redo(QTextCursor *cursor) } } +/*! \enum QTextDocument::Stacks + + \value UndoStack The undo stack. + \value RedoStack The redo stack. + \value UndoAndRedoStacks Both the undo and redo stacks. +*/ + +/*! + \since 4.7 + Clears the stacks specified by \a stacksToClear. + + This method clears any commands on the undo stack, the redo stack, + or both (the default). If commands are cleared, the appropriate + signals are emitted, QTextDocument::undoAvailable() or + QTextDocument::redoAvailable(). + + \sa QTextDocument::undoAvailable() QTextDocument::redoAvailable() +*/ +void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear) +{ + Q_D(QTextDocument); + d->clearUndoRedoStacks(stacksToClear, true); +} + /*! \overload @@ -1749,9 +1774,9 @@ void QTextDocument::print(QPrinter *printer) const int pageCopies; if (printer->collateCopies() == true){ docCopies = 1; - pageCopies = printer->numCopies(); + pageCopies = printer->supportsMultipleCopies() ? 1 : printer->copyCount(); } else { - docCopies = printer->numCopies(); + docCopies = printer->supportsMultipleCopies() ? 1 : printer->copyCount(); pageCopies = 1; } @@ -1924,6 +1949,10 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name) } #endif + // handle data: URLs + if (r.isNull() && name.scheme() == QLatin1String("data")) + r = qDecodeDataUrl(name).second; + // if resource was not loaded try to load it here if (!doc && r.isNull() && name.isRelative()) { QUrl currentURL = d->url; diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index b5bcb41..0140772 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -256,6 +256,13 @@ public: void undo(QTextCursor *cursor); void redo(QTextCursor *cursor); + enum Stacks { + UndoStack = 0x01, + RedoStack = 0x02, + UndoAndRedoStacks = UndoStack | RedoStack + }; + void clearUndoRedoStacks(Stacks historyToClear = UndoAndRedoStacks); + int maximumBlockCount() const; void setMaximumBlockCount(int maximum); diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 372b9dc..302a349 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE // The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 } #if !defined(Q_CC_DIAB) # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ - QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 } + QTextUndoCommand c = { a1, a2, 0, 0, quint8(a3), a4, a5, a6, { a7 }, a8 } #else # define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8 @@ -259,8 +259,7 @@ void QTextDocumentPrivate::clear() objects.clear(); title.clear(); - undoState = 0; - truncateUndoStack(); + clearUndoRedoStacks(QTextDocument::UndoAndRedoStacks); text = QString(); unreachableCharacterCount = 0; modifiedState = 0; @@ -292,7 +291,7 @@ QTextDocumentPrivate::~QTextDocumentPrivate() cursors.clear(); undoState = 0; undoEnabled = true; - truncateUndoStack(); + clearUndoRedoStacks(QTextDocument::RedoStack); } void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout) @@ -1027,7 +1026,7 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) if (!undoEnabled) return; if (undoState < undoStack.size()) - truncateUndoStack(); + clearUndoRedoStacks(QTextDocument::RedoStack); if (!undoStack.isEmpty() && modified) { QTextUndoCommand &last = undoStack[undoState - 1]; @@ -1050,26 +1049,46 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) emit document()->undoCommandAdded(); } -void QTextDocumentPrivate::truncateUndoStack() +void QTextDocumentPrivate::clearUndoRedoStacks(QTextDocument::Stacks stacksToClear, + bool emitSignals) { - if (undoState == undoStack.size()) - return; - - for (int i = undoState; i < undoStack.size(); ++i) { - QTextUndoCommand c = undoStack[i]; - if (c.command & QTextUndoCommand::Removed) { - // ######## -// QTextFragment *f = c.fragment_list; -// while (f) { -// QTextFragment *n = f->right; -// delete f; -// f = n; -// } - } else if (c.command & QTextUndoCommand::Custom) { - delete c.custom; + bool undoCommandsAvailable = undoState != 0; + bool redoCommandsAvailable = undoState != undoStack.size(); + if (stacksToClear == QTextDocument::UndoStack && undoCommandsAvailable) { + for (int i = 0; i < undoState; ++i) { + QTextUndoCommand c = undoStack[undoState]; + if (c.command & QTextUndoCommand::Custom) + delete c.custom; } + undoStack.remove(0, undoState); + undoStack.resize(undoStack.size() - undoState); + undoState = 0; + if (emitSignals) + emitUndoAvailable(false); + } else if (stacksToClear == QTextDocument::RedoStack + && redoCommandsAvailable) { + for (int i = undoState; i < undoStack.size(); ++i) { + QTextUndoCommand c = undoStack[i]; + if (c.command & QTextUndoCommand::Custom) + delete c.custom; + } + undoStack.resize(undoState); + if (emitSignals) + emitRedoAvailable(false); + } else if (stacksToClear == QTextDocument::UndoAndRedoStacks + && !undoStack.isEmpty()) { + for (int i = 0; i < undoStack.size(); ++i) { + QTextUndoCommand c = undoStack[i]; + if (c.command & QTextUndoCommand::Custom) + delete c.custom; + } + undoState = 0; + undoStack.resize(0); + if (emitSignals && undoCommandsAvailable) + emitUndoAvailable(false); + if (emitSignals && redoCommandsAvailable) + emitRedoAvailable(false); } - undoStack.resize(undoState); } void QTextDocumentPrivate::emitUndoAvailable(bool available) @@ -1097,7 +1116,7 @@ void QTextDocumentPrivate::enableUndoRedo(bool enable) if (!enable) { undoState = 0; - truncateUndoStack(); + clearUndoRedoStacks(QTextDocument::RedoStack); emitUndoAvailable(false); emitRedoAvailable(false); } diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index 4ecc2fa..ac5ed3c 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -252,10 +252,11 @@ public: inline QFont defaultFont() const { return formats.defaultFont(); } inline void setDefaultFont(const QFont &f) { formats.setDefaultFont(f); } + void clearUndoRedoStacks(QTextDocument::Stacks stacksToClear, bool emitSignals = false); + private: bool split(int pos); bool unite(uint f); - void truncateUndoStack(); void insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op); int insert_block(int pos, uint strPos, int format, int blockformat, QTextUndoCommand::Operation op, int command); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index b826588..7dc2c26 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1124,14 +1124,13 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const bool kerningEnabled = this->font(si).d->kerning; HB_ShaperItem entire_shaper_item; - entire_shaper_item.kerning_applied = false; + qMemSet(&entire_shaper_item, 0, sizeof(entire_shaper_item)); entire_shaper_item.string = reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData()); entire_shaper_item.stringLength = layoutData->string.length(); entire_shaper_item.item.script = (HB_Script)si.analysis.script; entire_shaper_item.item.pos = si.position; entire_shaper_item.item.length = length(item); entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel; - entire_shaper_item.glyphIndicesPresent = false; HB_UChar16 upperCased[256]; // XXX what about making this 4096, so we don't have to extend it ever. if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase @@ -1299,10 +1298,10 @@ QTextEngine::QTextEngine() } QTextEngine::QTextEngine(const QString &str, const QFont &f) - : fnt(f) + : text(str), + fnt(f) { init(this); - text = str; } QTextEngine::~QTextEngine() @@ -2467,7 +2466,7 @@ void QTextEngine::splitItem(int item, int pos) const if (pos <= 0) return; - layoutData->items.insert(item + 1, QScriptItem(layoutData->items[item])); + layoutData->items.insert(item + 1, layoutData->items[item]); QScriptItem &oldItem = layoutData->items[item]; QScriptItem &newItem = layoutData->items[item+1]; newItem.position += pos; @@ -2610,10 +2609,9 @@ void QTextEngine::resolveAdditionalFormats() const } QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f) - : _layoutData(string, _memory, MemSize) + : QTextEngine(string, f), + _layoutData(string, _memory, MemSize) { - fnt = f; - text = string; stackEngine = true; layoutData = &_layoutData; } diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index f36cbd2..5054b66 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -382,6 +382,7 @@ struct Q_AUTOTEST_EXPORT QScriptLine QFixed y; QFixed width; QFixed textWidth; + QFixed textAdvance; int from; signed int length : 29; mutable uint justified : 1; diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 3c0e85e..312d135 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -1570,6 +1570,20 @@ qreal QTextLine::naturalTextWidth() const return eng->lines[i].textWidth.toReal(); } +/*! \since 4.7 + Returns the horizontal advance of the text. The advance of the text + is the distance from its position to the next position at which + text would naturally be drawn. + + By adding the advance to the position of the text line and using this + as the position of a second text line, you will be able to position + the two lines side-by-side without gaps in-between. +*/ +qreal QTextLine::horizontalAdvance() const +{ + return eng->lines[i].textAdvance.toReal(); +} + /*! Lays out the line with the given \a width. The line is filled from its starting position with as many characters as will fit into @@ -1928,6 +1942,7 @@ void QTextLine::layout_helper(int maxGlyphs) found: if (lbh.rightBearing > 0) // If right bearing has not yet been adjusted lbh.adjustRightBearing(); + line.textAdvance = line.textWidth; line.textWidth -= qMin(QFixed(), lbh.rightBearing); if (line.length == 0) { diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h index edae7de..8c93ed6 100644 --- a/src/gui/text/qtextlayout.h +++ b/src/gui/text/qtextlayout.h @@ -202,6 +202,7 @@ public: bool leadingIncluded() const; qreal naturalTextWidth() const; + qreal horizontalAdvance() const; QRectF naturalTextRect() const; enum Edge { diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp index d30c996..6f099a9 100644 --- a/src/gui/text/qzip.cpp +++ b/src/gui/text/qzip.cpp @@ -280,6 +280,21 @@ static QFile::Permissions modeToPermissions(quint32 mode) return ret; } +static QDateTime readMSDosDate(const uchar *src) +{ + uint dosDate = readUInt(src); + quint64 uDate; + uDate = (quint64)(dosDate >> 16); + uint tm_mday = (uDate & 0x1f); + uint tm_mon = ((uDate & 0x1E0) >> 5); + uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980); + uint tm_hour = ((dosDate & 0xF800) >> 11); + uint tm_min = ((dosDate & 0x7E0) >> 5); + uint tm_sec = ((dosDate & 0x1f) << 1); + + return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec)); +} + struct LocalFileHeader { uchar signature[4]; // 0x04034b50 @@ -343,7 +358,7 @@ struct FileHeader }; QZipReader::FileInfo::FileInfo() - : isDir(false), isFile(true), isSymLink(false), crc32(0), size(0) + : isDir(false), isFile(false), isSymLink(false), crc32(0), size(0) { } @@ -365,9 +380,15 @@ QZipReader::FileInfo& QZipReader::FileInfo::operator=(const FileInfo &other) permissions = other.permissions; crc32 = other.crc32; size = other.size; + lastModified = other.lastModified; return *this; } +bool QZipReader::FileInfo::isValid() const +{ + return isDir || isFile || isSymLink; +} + class QZipPrivate { public: @@ -403,6 +424,7 @@ void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const fileInfo.permissions = modeToPermissions(mode); fileInfo.crc32 = readUInt(header.h.crc_32); fileInfo.size = readUInt(header.h.uncompressed_size); + fileInfo.lastModified = readMSDosDate(header.h.last_mod_file); } class QZipReaderPrivate : public QZipPrivate @@ -750,6 +772,14 @@ QZipReader::~QZipReader() } /*! + Returns device used for reading zip archive. +*/ +QIODevice* QZipReader::device() const +{ + return d->device; +} + +/*! Returns true if the user can read the file; otherwise returns false. */ bool QZipReader::isReadable() const @@ -796,6 +826,7 @@ int QZipReader::count() const /*! Returns a FileInfo of an entry in the zipfile. The \a index is the index into the directoy listing of the zipfile. + Returns an invalid FileInfo if \a index is out of boundaries. \sa fileInfoList() */ @@ -803,7 +834,8 @@ QZipReader::FileInfo QZipReader::entryInfoAt(int index) const { d->scanFiles(); QZipReader::FileInfo fi; - d->fillFileInfo(index, fi); + if (index >= 0 && index < d->fileHeaders.count()) + d->fillFileInfo(index, fi); return fi; } @@ -1022,6 +1054,14 @@ QZipWriter::~QZipWriter() } /*! + Returns device used for writing zip archive. +*/ +QIODevice* QZipWriter::device() const +{ + return d->device; +} + +/*! Returns true if the user can write to the archive; otherwise returns false. */ bool QZipWriter::isWritable() const diff --git a/src/gui/text/qzipreader_p.h b/src/gui/text/qzipreader_p.h index 67a2ace..6466a7b 100644 --- a/src/gui/text/qzipreader_p.h +++ b/src/gui/text/qzipreader_p.h @@ -55,6 +55,7 @@ // We mean it. // +#include <QtCore/qdatetime.h> #include <QtCore/qfile.h> #include <QtCore/qstring.h> @@ -62,7 +63,7 @@ QT_BEGIN_NAMESPACE class QZipReaderPrivate; -class Q_AUTOTEST_EXPORT QZipReader +class Q_GUI_EXPORT QZipReader { public: QZipReader(const QString &fileName, QIODevice::OpenMode mode = QIODevice::ReadOnly ); @@ -70,15 +71,18 @@ public: explicit QZipReader(QIODevice *device); ~QZipReader(); + QIODevice* device() const; + bool isReadable() const; bool exists() const; - struct Q_AUTOTEST_EXPORT FileInfo + struct Q_GUI_EXPORT FileInfo { FileInfo(); FileInfo(const FileInfo &other); ~FileInfo(); FileInfo &operator=(const FileInfo &other); + bool isValid() const; QString filePath; uint isDir : 1; uint isFile : 1; @@ -86,6 +90,7 @@ public: QFile::Permissions permissions; uint crc32; qint64 size; + QDateTime lastModified; void *d; }; diff --git a/src/gui/text/qzipwriter_p.h b/src/gui/text/qzipwriter_p.h index 9322f4a..a50c172 100644 --- a/src/gui/text/qzipwriter_p.h +++ b/src/gui/text/qzipwriter_p.h @@ -69,6 +69,8 @@ public: explicit QZipWriter(QIODevice *device); ~QZipWriter(); + QIODevice* device() const; + bool isWritable() const; bool exists() const; diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index b7615a4..9ec3142 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -37,7 +37,9 @@ HEADERS += \ text/qtexttable_p.h \ text/qzipreader_p.h \ text/qzipwriter_p.h \ - text/qtextodfwriter_p.h + text/qtextodfwriter_p.h \ + text/qstatictext_p.h \ + text/qstatictext.h SOURCES += \ text/qfont.cpp \ @@ -66,7 +68,8 @@ SOURCES += \ text/qsyntaxhighlighter.cpp \ text/qcssparser.cpp \ text/qzip.cpp \ - text/qtextodfwriter.cpp + text/qtextodfwriter.cpp \ + text/qstatictext.cpp win32 { SOURCES += \ |