From d796c9dd933ab96ec83b9a634feedd5d32e1ba3f Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Tue, 8 Nov 2011 12:31:36 -0600 Subject: Test conversion to TQt3 from Qt3 8c6fc1f8e35fd264dd01c582ca5e7549b32ab731 --- src/kernel/qtextlayout.cpp | 643 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 src/kernel/qtextlayout.cpp (limited to 'src/kernel/qtextlayout.cpp') diff --git a/src/kernel/qtextlayout.cpp b/src/kernel/qtextlayout.cpp new file mode 100644 index 000000000..842316d7b --- /dev/null +++ b/src/kernel/qtextlayout.cpp @@ -0,0 +1,643 @@ +/**************************************************************************** +** +** ??? +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the kernel module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qtextlayout_p.h" +#include "qtextengine_p.h" + +#include +#include +#include + + +TQRect TQTextItem::rect() const +{ + TQScriptItem& si = engine->items[item]; + return TQRect( si.x, si.y, si.width, si.ascent+si.descent ); +} + +int TQTextItem::x() const +{ + return engine->items[item].x; +} + +int TQTextItem::y() const +{ + return engine->items[item].y; +} + +int TQTextItem::width() const +{ + return engine->items[item].width; +} + +int TQTextItem::ascent() const +{ + return engine->items[item].ascent; +} + +int TQTextItem::descent() const +{ + return engine->items[item].descent; +} + +void TQTextItem::setWidth( int w ) +{ + engine->items[item].width = w; +} + +void TQTextItem::setAscent( int a ) +{ + engine->items[item].ascent = a; +} + +void TQTextItem::setDescent( int d ) +{ + engine->items[item].descent = d; +} + +int TQTextItem::from() const +{ + return engine->items[item].position; +} + +int TQTextItem::length() const +{ + return engine->length(item); +} + + +int TQTextItem::cursorToX( int *cPos, Edge edge ) const +{ + int pos = *cPos; + TQScriptItem *si = &engine->items[item]; + + engine->shape( item ); + advance_t *advances = engine->advances( si ); + GlyphAttributes *glyphAttributes = engine->glyphAttributes( si ); + unsigned short *logClusters = engine->logClusters( si ); + + int l = engine->length( item ); + if ( pos > l ) + pos = l; + if ( pos < 0 ) + pos = 0; + + int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos]; + if ( edge == Trailing ) { + // trailing edge is leading edge of next cluster + while ( glyph_pos < si->num_glyphs && !glyphAttributes[glyph_pos].clusterStart ) + glyph_pos++; + } + + int x = 0; + bool reverse = engine->items[item].analysis.bidiLevel % 2; + + if ( reverse ) { + for ( int i = si->num_glyphs-1; i >= glyph_pos; i-- ) + x += advances[i]; + } else { + for ( int i = 0; i < glyph_pos; i++ ) + x += advances[i]; + } +// qDebug("cursorToX: pos=%d, gpos=%d x=%d", pos, glyph_pos, x ); + *cPos = pos; + return x; +} + +int TQTextItem::xToCursor( int x, CursorPosition cpos ) const +{ + TQScriptItem *si = &engine->items[item]; + engine->shape( item ); + advance_t *advances = engine->advances( si ); + unsigned short *logClusters = engine->logClusters( si ); + + int l = engine->length( item ); + bool reverse = si->analysis.bidiLevel % 2; + if ( x < 0 ) + return reverse ? l : 0; + + + if ( reverse ) { + int width = 0; + for ( int i = 0; i < si->num_glyphs; i++ ) { + width += advances[i]; + } + x = -x + width; + } + int cp_before = 0; + int cp_after = 0; + int x_before = 0; + int x_after = 0; + + int lastCluster = 0; + for ( int i = 1; i <= l; i++ ) { + int newCluster = i < l ? logClusters[i] : si->num_glyphs; + if ( newCluster != lastCluster ) { + // calculate cluster width + cp_before = cp_after; + x_before = x_after; + cp_after = i; + for ( int j = lastCluster; j < newCluster; j++ ) + x_after += advances[j]; + // qDebug("cluster boundary: lastCluster=%d, newCluster=%d, x_before=%d, x_after=%d", + // lastCluster, newCluster, x_before, x_after ); + if ( x_after > x ) + break; + lastCluster = newCluster; + } + } + + bool before = ( cpos == OnCharacters || (x - x_before) < (x_after - x) ); + +// qDebug("got cursor position for %d: %d/%d, x_ba=%d/%d using %d", +// x, cp_before,cp_after, x_before, x_after, before ? cp_before : cp_after ); + + return before ? cp_before : cp_after; + +} + + +bool TQTextItem::isRightToLeft() const +{ + return (engine->items[item].analysis.bidiLevel % 2); +} + +bool TQTextItem::isObject() const +{ + return engine->items[item].isObject; +} + +bool TQTextItem::isSpace() const +{ + return engine->items[item].isSpace; +} + +bool TQTextItem::isTab() const +{ + return engine->items[item].isTab; +} + + +TQTextLayout::TQTextLayout() + :d(0) {} + +TQTextLayout::TQTextLayout( const TQString& string, TQPainter *p ) +{ + TQFontPrivate *f = p ? ( p->pfont ? p->pfont->d : p->cfont.d ) : TQApplication::font().d; + d = new TQTextEngine( (string.isNull() ? (const TQString&)TQString::fromLatin1("") : string), f ); +} + +TQTextLayout::TQTextLayout( const TQString& string, const TQFont& fnt ) +{ + d = new TQTextEngine( (string.isNull() ? (const TQString&)TQString::fromLatin1("") : string), fnt.d ); +} + +TQTextLayout::~TQTextLayout() +{ + delete d; +} + +void TQTextLayout::setText( const TQString& string, const TQFont& fnt ) +{ + delete d; + d = new TQTextEngine( (string.isNull() ? (const TQString&)TQString::fromLatin1("") : string), fnt.d ); +} + +/* add an additional item boundary eg. for style change */ +void TQTextLayout::setBoundary( int strPos ) +{ + if ( strPos <= 0 || strPos >= (int)d->string.length() ) + return; + + int itemToSplit = 0; + while ( itemToSplit < d->items.size() && d->items[itemToSplit].position <= strPos ) + itemToSplit++; + itemToSplit--; + if ( d->items[itemToSplit].position == strPos ) { + // already a split at the requested position + return; + } + d->splitItem( itemToSplit, strPos - d->items[itemToSplit].position ); +} + + +int TQTextLayout::numItems() const +{ + return d->items.size(); +} + +TQTextItem TQTextLayout::itemAt( int i ) const +{ + return TQTextItem( i, d ); +} + + +TQTextItem TQTextLayout::findItem( int strPos ) const +{ + if ( strPos == 0 && d->items.size() ) + return TQTextItem( 0, d ); + // ## TODO use bsearch + for ( int i = d->items.size()-1; i >= 0; --i ) { + if ( d->items[i].position < strPos ) + return TQTextItem( i, d ); + } + return TQTextItem(); +} + + +void TQTextLayout::beginLayout( TQTextLayout::LayoutMode m ) +{ + d->items.clear(); + TQTextEngine::Mode mode = TQTextEngine::Full; + if (m == NoBidi) + mode = TQTextEngine::NoBidi; + else if (m == SingleLine) + mode = TQTextEngine::SingleLine; + d->itemize( mode ); + d->currentItem = 0; + d->firstItemInLine = -1; +} + +void TQTextLayout::beginLine( int width ) +{ + d->lineWidth = width; + d->widthUsed = 0; + d->firstItemInLine = -1; +} + +bool TQTextLayout::atEnd() const +{ + return d->currentItem >= d->items.size(); +} + +TQTextItem TQTextLayout::nextItem() +{ + d->currentItem++; + + if ( d->currentItem >= d->items.size() ) + return TQTextItem(); + + d->shape( d->currentItem ); + return TQTextItem( d->currentItem, d ); +} + +TQTextItem TQTextLayout::currentItem() +{ + if ( d->currentItem >= d->items.size() ) + return TQTextItem(); + + d->shape( d->currentItem ); + return TQTextItem( d->currentItem, d ); +} + +/* ## maybe also currentItem() */ +void TQTextLayout::setLineWidth( int newWidth ) +{ + d->lineWidth = newWidth; +} + +int TQTextLayout::lineWidth() const +{ + return d->lineWidth; +} + +int TQTextLayout::widthUsed() const +{ + return d->widthUsed; +} + +int TQTextLayout::availableWidth() const +{ + return d->lineWidth - d->widthUsed; +} + + +/* returns true if completely added */ +TQTextLayout::Result TQTextLayout::addCurrentItem() +{ + if ( d->firstItemInLine == -1 ) + d->firstItemInLine = d->currentItem; + TQScriptItem ¤t = d->items[d->currentItem]; + d->shape( d->currentItem ); + d->widthUsed += current.width; +// qDebug("trying to add item %d with width %d, remaining %d", d->currentItem, current.width, d->lineWidth-d->widthUsed ); + + d->currentItem++; + + return (d->widthUsed <= d->lineWidth + || (d->currentItem < d->items.size() && d->items[d->currentItem].isSpace)) ? Ok : LineFull; +} + +TQTextLayout::Result TQTextLayout::endLine( int x, int y, int alignment, + int *ascent, int *descent, int *lineLeft, int *lineRight ) +{ + int available = d->lineWidth; + int numRuns = 0; + int numSpaceItems = 0; + Q_UINT8 _levels[128]; + int _visual[128]; + Q_UINT8 *levels = _levels; + int *visual = _visual; + int i; + TQTextLayout::Result result = LineEmpty; + +// qDebug("endLine x=%d, y=%d, first=%d, current=%d lw=%d wu=%d", x, y, d->firstItemInLine, d->currentItem, d->lineWidth, d->widthUsed ); + int width_nobreak_found = d->widthUsed; + if ( d->firstItemInLine == -1 ) + goto end; + + if ( !(alignment & (TQt::SingleLine|TQt::IncludeTrailingSpaces)) + && d->currentItem > d->firstItemInLine && d->items[d->currentItem-1].isSpace ) { + int i = d->currentItem-1; + while ( i > d->firstItemInLine && d->items[i].isSpace ) { + numSpaceItems++; + d->widthUsed -= d->items[i--].width; + } + } + + if ( (alignment & (TQt::WordBreak|TQt::BreakAnywhere)) && + d->widthUsed > d->lineWidth ) { + // find linebreak + + // even though we removed trailing spaces the line was too wide. We'll have to break at an earlier + // position. To not confuse the layouting below, reset the number of space items + numSpaceItems = 0; + + + bool breakany = alignment & TQt::BreakAnywhere; + + const TQCharAttributes *attrs = d->attributes(); + int w = 0; + int itemWidth = 0; + int breakItem = d->firstItemInLine; + int breakPosition = -1; +#if 0 + // we iterate backwards or forward depending on what we guess is closer + if ( d->widthUsed - d->lineWidth < d->lineWidth ) { + // backwards search should be faster + + } else +#endif + { + int tmpWidth = 0; + int swidth = 0; + // forward search is probably faster + for ( int i = d->firstItemInLine; i < d->currentItem; i++ ) { + const TQScriptItem *si = &d->items[i]; + int length = d->length( i ); + const TQCharAttributes *itemAttrs = attrs + si->position; + + advance_t *advances = d->advances( si ); + unsigned short *logClusters = d->logClusters( si ); + + int lastGlyph = 0; + int tmpItemWidth = 0; + +// qDebug("looking for break in item %d, isSpace=%d", i, si->isSpace ); + if(si->isSpace && !(alignment & (TQt::SingleLine|TQt::IncludeTrailingSpaces))) { + swidth += si->width; + } else { + tmpWidth += swidth; + swidth = 0; + for ( int pos = 0; pos < length; pos++ ) { +// qDebug("advance=%d, w=%d, tmpWidth=%d, softbreak=%d, whitespace=%d", +// *advances, w, tmpWidth, itemAttrs->softBreak, itemAttrs->whiteSpace ); + int glyph = logClusters[pos]; + if ( lastGlyph != glyph ) { + while ( lastGlyph < glyph ) + tmpItemWidth += advances[lastGlyph++]; + if ( breakPosition != -1 && w + tmpWidth + tmpItemWidth > d->lineWidth ) { +// qDebug("found break at w=%d, tmpWidth=%d, tmpItemWidth=%d", w, tmpWidth, tmpItemWidth); + d->widthUsed = w; + goto found; + } + } + if ( (itemAttrs->softBreak || + ( breakany && itemAttrs->charStop ) ) && + (i != d->firstItemInLine || pos != 0) ) { + if ( breakItem != i ) + itemWidth = 0; + if (itemAttrs->softBreak) + breakany = FALSE; + breakItem = i; + breakPosition = pos; +// qDebug("found possible break at item %d, position %d (absolute=%d), w=%d, tmpWidth=%d, tmpItemWidth=%d", breakItem, breakPosition, d->items[breakItem].position+breakPosition, w, tmpWidth, tmpItemWidth); + w += tmpWidth + tmpItemWidth; + itemWidth += tmpItemWidth; + tmpWidth = 0; + tmpItemWidth = 0; + } + itemAttrs++; + } + while ( lastGlyph < si->num_glyphs ) + tmpItemWidth += advances[lastGlyph++]; + tmpWidth += tmpItemWidth; + if ( w + tmpWidth > d->lineWidth ) { + d->widthUsed = w; + goto found; + } + } + } + } + + found: + // no valid break point found + if ( breakPosition == -1 ) { + d->widthUsed = width_nobreak_found; + goto nobreak; + } + +// qDebug("linebreak at item %d, position %d, wu=%d", breakItem, breakPosition, d->widthUsed ); + // split the line + if ( breakPosition > 0 ) { +// int length = d->length( breakItem ); + +// qDebug("splitting item, itemWidth=%d", itemWidth); + // not a full item, need to break + d->splitItem( breakItem, breakPosition ); + d->currentItem = breakItem+1; + } else { + d->currentItem = breakItem; + } + } + + result = Ok; + + nobreak: + // position the objects in the line + available -= d->widthUsed; + + numRuns = d->currentItem - d->firstItemInLine - numSpaceItems; + if ( numRuns > 127 ) { + levels = new Q_UINT8[numRuns]; + visual = new int[numRuns]; + } + +// qDebug("reordering %d runs, numSpaceItems=%d", numRuns, numSpaceItems ); + for ( i = 0; i < numRuns; i++ ) { + levels[i] = d->items[i+d->firstItemInLine].analysis.bidiLevel; +// qDebug(" level = %d", d->items[i+d->firstItemInLine].analysis.bidiLevel ); + } + d->bidiReorder( numRuns, levels, visual ); + + end: + // ### FIXME + if ( alignment & TQt::AlignJustify ) { + // #### justify items + alignment = TQt::AlignAuto; + } + if ( (alignment & TQt::AlignHorizontal_Mask) == TQt::AlignAuto ) + alignment = TQt::AlignLeft; + if ( alignment & TQt::AlignRight ) + x += available; + else if ( alignment & TQt::AlignHCenter ) + x += available/2; + + + int asc = ascent ? *ascent : 0; + int desc = descent ? *descent : 0; + + for ( i = 0; i < numRuns; i++ ) { + TQScriptItem &si = d->items[d->firstItemInLine+visual[i]]; + asc = TQMAX( asc, si.ascent ); + desc = TQMAX( desc, si.descent ); + } + + int left = x; + for ( i = 0; i < numRuns; i++ ) { + TQScriptItem &si = d->items[d->firstItemInLine+visual[i]]; +// qDebug("positioning item %d with width %d (from=%d/length=%d) at %d", d->firstItemInLine+visual[i], si.width, si.position, +// d->length(d->firstItemInLine+visual[i]), x ); + si.x = x; + si.y = y + asc; + x += si.width; + } + int right = x; + + if ( numSpaceItems ) { + if ( d->items[d->firstItemInLine+numRuns].analysis.bidiLevel % 2 ) { + x = left; + for ( i = 0; i < numSpaceItems; i++ ) { + TQScriptItem &si = d->items[d->firstItemInLine + numRuns + i]; + x -= si.width; + si.x = x; + si.y = y + asc; + } + } else { + for ( i = 0; i < numSpaceItems; i++ ) { + TQScriptItem &si = d->items[d->firstItemInLine + numRuns + i]; + si.x = x; + si.y = y + asc; + x += si.width; + } + } + } + + if ( lineLeft ) + *lineLeft = left; + if ( lineRight ) + *lineRight = right; + if ( ascent ) + *ascent = asc; + if ( descent ) + *descent = desc; + + if (levels != _levels) + delete []levels; + if (visual != _visual) + delete []visual; + + return result; +} + +void TQTextLayout::endLayout() +{ + // nothing to do currently +} + + +int TQTextLayout::nextCursorPosition( int oldPos, CursorMode mode ) const +{ +// qDebug("looking for next cursor pos for %d", oldPos ); + const TQCharAttributes *attributes = d->attributes(); + int len = d->string.length(); + if ( oldPos >= len ) + return oldPos; + oldPos++; + if ( mode == SkipCharacters ) { + while ( oldPos < len && !attributes[oldPos].charStop ) + oldPos++; + } else { + while ( oldPos < len && !attributes[oldPos].wordStop && !attributes[oldPos-1].whiteSpace ) + oldPos++; + } +// qDebug(" -> %d", oldPos ); + return oldPos; +} + +int TQTextLayout::previousCursorPosition( int oldPos, CursorMode mode ) const +{ +// qDebug("looking for previous cursor pos for %d", oldPos ); + const TQCharAttributes *attributes = d->attributes(); + if ( oldPos <= 0 ) + return 0; + oldPos--; + if ( mode == SkipCharacters ) { + while ( oldPos && !attributes[oldPos].charStop ) + oldPos--; + } else { + while ( oldPos && !attributes[oldPos].wordStop && !attributes[oldPos-1].whiteSpace ) + oldPos--; + } +// qDebug(" -> %d", oldPos ); + return oldPos; +} + + +bool TQTextLayout::validCursorPosition( int pos ) const +{ + const TQCharAttributes *attributes = d->attributes(); + if ( pos < 0 || pos > (int)d->string.length() ) + return FALSE; + return attributes[pos].charStop; +} + +void TQTextLayout::setDirection(TQChar::Direction dir) +{ + d->direction = dir; +} -- cgit v1.2.1