#include "tqtglobaldefines.h" #ifdef USE_QT4 // #if 0 /**************************************************************************** ** ** 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 Qt3Support module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** 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. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "tqrichtext_p.h" #ifndef QT_NO_RICHTEXT #include "Qt/qbitmap.h" #include "Qt/qapplication.h" #include "tqcleanuphandler.h" #include "Qt/qcursor.h" #include "Qt/qdatastream.h" #include "tqdragobject.h" #include "Qt/qdrawutil.h" #include "Qt/qfile.h" #include "Qt/qfileinfo.h" #include "Qt/qfont.h" #include "Qt/qimage.h" #include "Qt/qmap.h" #include "Qt/qmime.h" #include "tqpaintdevicemetrics.h" #include "tqpainter.h" #include "tqstringlist.h" #include "Qt/qstyle.h" #include "Qt/qstyleoption.h" #include "tqstylesheet.h" #include "Qt/qtextstream.h" #include "private/qt4_qtextengine_p.h" // #include #include #if defined(Q_WS_X11) // #include "qx11info_x11.h" #endif QT_BEGIN_NAMESPACE static TQTextCursor* richTextExportStart = 0; static TQTextCursor* richTextExportEnd = 0; class TQTextFormatCollection; const int border_tolerance = 2; #ifdef Q_WS_WIN QT_BEGIN_INCLUDE_NAMESPACE #include "qt_windows.h" QT_END_INCLUDE_NAMESPACE #endif static inline bool is_printer(TQPainter *p) { if (!p || !p->device()) return false; return p->device()->devType() == QInternal::Printer; } static inline int scale(int value, TQPainter *painter) { if (is_printer(painter)) { TQPaintDeviceMetrics metrics(painter->device()); #if defined(Q_WS_X11) value = value * metrics.logicalDpiY() / QX11Info::appDpiY(painter->tqdevice()->x11Screen()); #elif defined (Q_WS_WIN) HDC hdc = GetDC(0); int gdc = GetDeviceCaps(hdc, LOGPIXELSY); if (gdc) value = value * metrics.logicalDpiY() / gdc; ReleaseDC(0, hdc); #elif defined (Q_WS_MAC) value = value * metrics.logicalDpiY() / 75; // ##### FIXME #elif defined (Q_WS_QWS) value = value * metrics.logicalDpiY() / 75; #endif } return value; } static inline bool isBreakable(TQTextString *string, int pos) { if (string->at(pos).nobreak) return false; return (pos < string->length()-1 && string->at(pos+1).softBreak); } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void TQTextCommandHistory::addCommand(TQTextCommand *cmd) { if (current < history.count() - 1) { QList commands; for (int i = 0; i <= current; ++i) commands.insert(i, history.takeFirst()); commands.append(cmd); while (!history.isEmpty()) delete history.takeFirst(); history = commands; } else { history.append(cmd); } if (history.count() > steps) delete history.takeFirst(); else ++current; } TQTextCursor *TQTextCommandHistory::undo(TQTextCursor *c) { if (current > -1) { TQTextCursor *c2 = history.at(current)->unexecute(c); --current; return c2; } return 0; } TQTextCursor *TQTextCommandHistory::redo(TQTextCursor *c) { if (current > -1) { if (current < history.count() - 1) { ++current; return history.at(current)->execute(c); } } else { if (history.count() > 0) { ++current; return history.at(current)->execute(c); } } return 0; } bool TQTextCommandHistory::isUndoAvailable() { return current > -1; } bool TQTextCommandHistory::isRedoAvailable() { return (current > -1 && current < history.count() - 1) || (current == -1 && history.count() > 0); } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextDeleteCommand::TQTextDeleteCommand(TQTextDocument *dc, int i, int idx, const QVector &str, const QByteArray& oldStyleInfo) : TQTextCommand(dc), id(i), index(idx), parag(0), text(str), styleInformation(oldStyleInfo) { for (int j = 0; j < (int)text.size(); ++j) { if (text[j].format()) text[j].format()->addRef(); } } TQTextDeleteCommand::TQTextDeleteCommand(TQTextParagraph *p, int idx, const QVector &str) : TQTextCommand(0), id(-1), index(idx), parag(p), text(str) { for (int i = 0; i < (int)text.size(); ++i) { if (text[i].format()) text[i].format()->addRef(); } } TQTextDeleteCommand::~TQTextDeleteCommand() { for (int i = 0; i < (int)text.size(); ++i) { if (text[i].format()) text[i].format()->removeRef(); } text.resize(0); } TQTextCursor *TQTextDeleteCommand::execute(TQTextCursor *c) { TQTextParagraph *s = doc ? doc->paragAt(id) : parag; if (!s) { qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId()); return 0; } cursor.setParagraph(s); cursor.setIndex(index); int len = text.size(); if (c) *c = cursor; if (doc) { doc->setSelectionStart(TQTextDocument::Temp, cursor); for (int i = 0; i < len; ++i) cursor.gotoNextLetter(); doc->setSelectionEnd(TQTextDocument::Temp, cursor); doc->removeSelectedText(TQTextDocument::Temp, &cursor); if (c) *c = cursor; } else { s->remove(index, len); } return c; } TQTextCursor *TQTextDeleteCommand::unexecute(TQTextCursor *c) { TQTextParagraph *s = doc ? doc->paragAt(id) : parag; if (!s) { qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId()); return 0; } cursor.setParagraph(s); cursor.setIndex(index); TQString str = TQTextString::toString(text); cursor.insert(str, true, &text); if (c) *c = cursor; cursor.setParagraph(s); cursor.setIndex(index); #ifndef QT_NO_DATASTREAM if (!styleInformation.isEmpty()) { QDataStream styleStream(&styleInformation, IO_ReadOnly); int num; styleStream >> num; TQTextParagraph *p = s; while (num-- && p) { p->readStyleInformation(styleStream); p = p->next(); } } #endif s = cursor.paragraph(); while (s) { s->format(); s->setChanged(true); if (s == c->paragraph()) break; s = s->next(); } return &cursor; } TQTextFormatCommand::TQTextFormatCommand(TQTextDocument *dc, int sid, int sidx, int eid, int eidx, const QVector &old, TQTextFormat *f, int fl) : TQTextCommand(dc), startId(sid), startIndex(sidx), endId(eid), endIndex(eidx), format(f), oldFormats(old), flags(fl) { format = dc->formatCollection()->format(f); for (int j = 0; j < (int)oldFormats.size(); ++j) { if (oldFormats[j].format()) oldFormats[j].format()->addRef(); } } TQTextFormatCommand::~TQTextFormatCommand() { format->removeRef(); for (int j = 0; j < (int)oldFormats.size(); ++j) { if (oldFormats[j].format()) oldFormats[j].format()->removeRef(); } } TQTextCursor *TQTextFormatCommand::execute(TQTextCursor *c) { TQTextParagraph *sp = doc->paragAt(startId); TQTextParagraph *ep = doc->paragAt(endId); if (!sp || !ep) return c; TQTextCursor start(doc); start.setParagraph(sp); start.setIndex(startIndex); TQTextCursor end(doc); end.setParagraph(ep); end.setIndex(endIndex); doc->setSelectionStart(TQTextDocument::Temp, start); doc->setSelectionEnd(TQTextDocument::Temp, end); doc->setFormat(TQTextDocument::Temp, format, flags); doc->removeSelection(TQTextDocument::Temp); if (endIndex == ep->length()) end.gotoLeft(); *c = end; return c; } TQTextCursor *TQTextFormatCommand::unexecute(TQTextCursor *c) { TQTextParagraph *sp = doc->paragAt(startId); TQTextParagraph *ep = doc->paragAt(endId); if (!sp || !ep) return 0; int idx = startIndex; int fIndex = 0; while ( fIndex < int(oldFormats.size()) ) { if (oldFormats.at(fIndex).c == TQLatin1Char('\n')) { if (idx > 0) { if (idx < sp->length() && fIndex > 0) sp->setFormat(idx, 1, oldFormats.at(fIndex - 1).format()); if (sp == ep) break; sp = sp->next(); idx = 0; } fIndex++; } if (oldFormats.at(fIndex).format()) sp->setFormat(idx, 1, oldFormats.at(fIndex).format()); idx++; fIndex++; if (fIndex >= (int)oldFormats.size()) break; if (idx >= sp->length()) { if (sp == ep) break; sp = sp->next(); idx = 0; } } TQTextCursor end(doc); end.setParagraph(ep); end.setIndex(endIndex); if (endIndex == ep->length()) end.gotoLeft(); *c = end; return c; } TQTextStyleCommand::TQTextStyleCommand(TQTextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange) : TQTextCommand(dc), firstParag(fParag), lastParag(lParag), before(beforeChange) { after = readStyleInformation( dc, fParag, lParag); } QByteArray TQTextStyleCommand::readStyleInformation( TQTextDocument* doc, int fParag, int lParag) { QByteArray style; #ifndef QT_NO_DATASTREAM TQTextParagraph *p = doc->paragAt(fParag); if (!p) return style; QDataStream styleStream(&style, IO_WriteOnly); int num = lParag - fParag + 1; styleStream << num; while (num -- && p) { p->writeStyleInformation(styleStream); p = p->next(); } #endif return style; } void TQTextStyleCommand::writeStyleInformation( TQTextDocument* doc, int fParag, const QByteArray& style) { #ifndef QT_NO_DATASTREAM TQTextParagraph *p = doc->paragAt(fParag); if (!p) return; QByteArray copy = style; QDataStream styleStream(©, IO_ReadOnly); int num; styleStream >> num; while (num-- && p) { p->readStyleInformation(styleStream); p = p->next(); } #endif } TQTextCursor *TQTextStyleCommand::execute(TQTextCursor *c) { writeStyleInformation(doc, firstParag, after); return c; } TQTextCursor *TQTextStyleCommand::unexecute(TQTextCursor *c) { writeStyleInformation(doc, firstParag, before); return c; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextCursor::TQTextCursor(TQTextDocument *dc) : idx(0), tmpX(-1), ox(0), oy(0), valid(true) { para = dc ? dc->firstParagraph() : 0; } TQTextCursor::TQTextCursor(const TQTextCursor &c) { ox = c.ox; oy = c.oy; idx = c.idx; para = c.para; tmpX = c.tmpX; indices = c.indices; paras = c.paras; xOffsets = c.xOffsets; yOffsets = c.yOffsets; valid = c.valid; } TQTextCursor::~TQTextCursor() { } TQTextCursor &TQTextCursor::operator=(const TQTextCursor &c) { ox = c.ox; oy = c.oy; idx = c.idx; para = c.para; tmpX = c.tmpX; indices = c.indices; paras = c.paras; xOffsets = c.xOffsets; yOffsets = c.yOffsets; valid = c.valid; return *this; } bool TQTextCursor::operator==(const TQTextCursor &c) const { return para == c.para && idx == c.idx; } int TQTextCursor::totalOffsetX() const { int xoff = ox; for (QStack::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit) xoff += *xit; return xoff; } int TQTextCursor::totalOffsetY() const { int yoff = oy; for (QStack::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit) yoff += *yit; return yoff; } #ifndef QT_NO_TEXTCUSTOMITEM void TQTextCursor::gotoIntoNested(const QPoint &globalPos) { if (!para) return; Q_ASSERT(para->at(idx)->isCustom()); push(); ox = 0; int bl, y; para->lineHeightOfChar(idx, &bl, &y); oy = y + para->rect().y(); ox = para->at(idx)->x; TQTextDocument* doc = document(); para->at(idx)->customItem()->enterAt(this, doc, para, idx, ox, oy, globalPos-QPoint(ox,oy)); } #endif void TQTextCursor::invalidateNested() { if (nestedDepth()) { QStack::Iterator it = paras.begin(); QStack::Iterator it2 = indices.begin(); for (; it != paras.end(); ++it, ++it2) { if (*it == para) continue; (*it)->invalidate(0); #ifndef QT_NO_TEXTCUSTOMITEM if ((*it)->at(*it2)->isCustom()) (*it)->at(*it2)->customItem()->invalidate(); #endif } } } void TQTextCursor::insert(const QString &str, bool checkNewLine, QVector *formatting) { tmpX = -1; bool justInsert = true; TQString s(str); #if defined(Q_WS_WIN) if (checkNewLine) { int i = 0; while ((i = s.indexOf(TQLatin1Char('\r'), i)) != -1) s.remove(i ,1); } #endif if (checkNewLine) justInsert = s.indexOf(TQLatin1Char('\n')) == -1; if (justInsert) { // we ignore new lines and insert all in the current para at the current index para->insert(idx, s.unicode(), s.length()); if (formatting) { for (int i = 0; i < (int)s.length(); ++i) { if (formatting->at(i).format()) { formatting->at(i).format()->addRef(); para->string()->setFormat(idx + i, formatting->at(i).format(), true); } } } idx += s.length(); } else { // we split at new lines int start = -1; int end; int y = para->rect().y() + para->rect().height(); int lastIndex = 0; do { end = s.indexOf(TQLatin1Char('\n'), start + 1); // find line break if (end == -1) // didn't find one, so end of line is end of string end = s.length(); int len = (start == -1 ? end : end - start - 1); if (len > 0) // insert the line para->insert(idx, s.unicode() + start + 1, len); else para->invalidate(0); if (formatting) { // set formats to the chars of the line for (int i = 0; i < len; ++i) { if (formatting->at(i + lastIndex).format()) { formatting->at(i + lastIndex).format()->addRef(); para->string()->setFormat(i + idx, formatting->at(i + lastIndex).format(), true); } } lastIndex += len; } start = end; // next start is at the end of this line idx += len; // increase the index of the cursor to the end of the inserted text if (s[end] == TQLatin1Char('\n')) { // if at the end was a line break, break the line splitAndInsertEmptyParagraph(false, true); para->setEndState(-1); para->prev()->format(-1, false); lastIndex++; } } while (end < (int)s.length()); para->format(-1, false); int dy = para->rect().y() + para->rect().height() - y; TQTextParagraph *p = para; p->setParagId(p->prev() ? p->prev()->paragId() + 1 : 0); p = p->next(); while (p) { p->setParagId(p->prev()->paragId() + 1); p->move(dy); p->invalidate(0); p->setEndState(-1); p = p->next(); } } int h = para->rect().height(); para->format(-1, true); if (h != para->rect().height()) invalidateNested(); else if (para->document() && para->document()->parent()) para->document()->nextDoubleBuffered = true; fixCursorPosition(); } void TQTextCursor::gotoLeft() { if (para->string()->isRightToLeft()) gotoNextLetter(); else gotoPreviousLetter(); } void TQTextCursor::gotoPreviousLetter() { tmpX = -1; if (idx > 0) { idx = para->string()->previousCursorPosition(idx); #ifndef QT_NO_TEXTCUSTOMITEM const TQTextStringChar *tsc = para->at(idx); if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) processNesting(EnterEnd); #endif } else if (para->prev()) { para = para->prev(); while (!para->isVisible() && para->prev()) para = para->prev(); idx = para->length() - 1; } else if (nestedDepth()) { pop(); processNesting(Prev); if (idx == -1) { pop(); if (idx > 0) { idx = para->string()->previousCursorPosition(idx); #ifndef QT_NO_TEXTCUSTOMITEM const TQTextStringChar *tsc = para->at(idx); if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) processNesting(EnterEnd); #endif } else if (para->prev()) { para = para->prev(); idx = para->length() - 1; } } } } void TQTextCursor::push() { indices.push(idx); paras.push(para); xOffsets.push(ox); yOffsets.push(oy); } void TQTextCursor::pop() { if (indices.isEmpty()) return; idx = indices.pop(); para = paras.pop(); ox = xOffsets.pop(); oy = yOffsets.pop(); } void TQTextCursor::restoreState() { while (!indices.isEmpty()) pop(); } bool TQTextCursor::place(const QPoint &p, TQTextParagraph *s, bool link) { QPoint pos(p); TQRect r; TQTextParagraph *str = s; if (pos.y() < s->rect().y()) { pos.setY(s->rect().y()); #ifdef Q_WS_MAC pos.setX(s->rect().x()); #endif } while (s) { r = s->rect(); r.setWidth(document() ? document()->width() : QWIDGETSIZE_MAX); if (s->isVisible()) str = s; if (pos.y() >= r.y() && pos.y() <= r.y() + r.height()) break; if (!s->next()) { #ifdef Q_WS_MAC pos.setX(s->rect().x() + s->rect().width()); #endif break; } s = s->next(); } if (!s || !str) return false; s = str; setParagraph(s); int y = s->rect().y(); int lines = s->lines(); TQTextStringChar *chr = 0; int index = 0; int i = 0; int cy = 0; int ch = 0; for (; i < lines; ++i) { chr = s->lineStartOfLine(i, &index); cy = s->lineY(i); ch = s->lineHeight(i); if (!chr) return false; if (pos.y() <= y + cy + ch) break; } int nextLine; if (i < lines - 1) s->lineStartOfLine(i+1, &nextLine); else nextLine = s->length(); i = index; int x = s->rect().x(); if (pos.x() < x) pos.setX(x + 1); int cw; int curpos = s->length()-1; int dist = 10000000; bool inCustom = false; while (i < nextLine) { chr = s->at(i); int cpos = x + chr->x; cw = s->string()->width(i); #ifndef QT_NO_TEXTCUSTOMITEM if (chr->isCustom() && chr->customItem()->isNested()) { if (pos.x() >= cpos && pos.x() <= cpos + cw && pos.y() >= y + cy && pos.y() <= y + cy + chr->height()) { inCustom = true; curpos = i; break; } } else #endif { if(chr->rightToLeft) cpos += cw; int diff = cpos - pos.x(); bool dm = diff < 0 ? !chr->rightToLeft : chr->rightToLeft; if ((QABS(diff) < dist || (dist == diff && dm == true)) && para->string()->validCursorPosition(i)) { dist = QABS(diff); if (!link || pos.x() >= x + chr->x) curpos = i; } } i++; } setIndex(curpos); #ifndef QT_NO_TEXTCUSTOMITEM if (inCustom && para->document() && para->at(curpos)->isCustom() && para->at(curpos)->customItem()->isNested()) { TQTextDocument *oldDoc = para->document(); gotoIntoNested(pos); if (oldDoc == para->document()) return true; QPoint p(pos.x() - offsetX(), pos.y() - offsetY()); if (!place(p, document()->firstParagraph(), link)) pop(); } #endif return true; } bool TQTextCursor::processNesting(Operation op) { if (!para->document()) return false; TQTextDocument* doc = para->document(); push(); ox = para->at(idx)->x; int bl, y; para->lineHeightOfChar(idx, &bl, &y); oy = y + para->rect().y(); bool ok = false; #ifndef QT_NO_TEXTCUSTOMITEM switch (op) { case EnterBegin: ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy); break; case EnterEnd: ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy, true); break; case Next: ok = para->at(idx)->customItem()->next(this, doc, para, idx, ox, oy); break; case Prev: ok = para->at(idx)->customItem()->prev(this, doc, para, idx, ox, oy); break; case Down: ok = para->at(idx)->customItem()->down(this, doc, para, idx, ox, oy); break; case Up: ok = para->at(idx)->customItem()->up(this, doc, para, idx, ox, oy); break; } if (!ok) #endif pop(); return ok; } void TQTextCursor::gotoRight() { if (para->string()->isRightToLeft()) gotoPreviousLetter(); else gotoNextLetter(); } void TQTextCursor::gotoNextLetter() { tmpX = -1; #ifndef QT_NO_TEXTCUSTOMITEM const TQTextStringChar *tsc = para->at(idx); if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) { if (processNesting(EnterBegin)) return; } #endif if (idx < para->length() - 1) { idx = para->string()->nextCursorPosition(idx); } else if (para->next()) { para = para->next(); while (!para->isVisible() && para->next()) para = para->next(); idx = 0; } else if (nestedDepth()) { pop(); processNesting(Next); if (idx == -1) { pop(); if (idx < para->length() - 1) { idx = para->string()->nextCursorPosition(idx); } else if (para->next()) { para = para->next(); idx = 0; } } } } void TQTextCursor::gotoUp() { int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line); if (!c) return; if (tmpX < 0) tmpX = x(); if (indexOfLineStart == 0) { if (!para->prev()) { if (!nestedDepth()) return; pop(); processNesting(Up); if (idx == -1) { pop(); if (!para->prev()) return; idx = tmpX = 0; } else { tmpX = -1; return; } } TQTextParagraph *p = para->prev(); while (p && !p->isVisible()) p = p->prev(); if (p) para = p; int lastLine = para->lines() - 1; if (!para->lineStartOfLine(lastLine, &indexOfLineStart)) return; idx = indexOfLineStart; while (idx < para->length()-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } else { --line; int oldIndexOfLineStart = indexOfLineStart; if (!para->lineStartOfLine(line, &indexOfLineStart)) return; idx = indexOfLineStart; while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } fixCursorPosition(); } void TQTextCursor::gotoDown() { int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line); if (!c) return; if (tmpX < 0) tmpX = x(); if (line == para->lines() - 1) { if (!para->next()) { if (!nestedDepth()) return; pop(); processNesting(Down); if (idx == -1) { pop(); if (!para->next()) return; idx = tmpX = 0; } else { tmpX = -1; return; } } TQTextParagraph *s = para->next(); while (s && !s->isVisible()) s = s->next(); if (s) para = s; if (!para->lineStartOfLine(0, &indexOfLineStart)) return; int end; if (para->lines() == 1) end = para->length(); else para->lineStartOfLine(1, &end); idx = indexOfLineStart; while (idx < end-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } else { ++line; int end; if (line == para->lines() - 1) end = para->length(); else para->lineStartOfLine(line + 1, &end); if (!para->lineStartOfLine(line, &indexOfLineStart)) return; idx = indexOfLineStart; while (idx < end-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } fixCursorPosition(); } void TQTextCursor::gotoLineEnd() { tmpX = -1; int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line); if (!c) return; if (line == para->lines() - 1) { idx = para->length() - 1; } else { c = para->lineStartOfLine(++line, &indexOfLineStart); indexOfLineStart--; idx = indexOfLineStart; } } void TQTextCursor::gotoLineStart() { tmpX = -1; int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line); if (!c) return; idx = indexOfLineStart; } void TQTextCursor::gotoHome() { if (topParagraph()->document()) gotoPosition(topParagraph()->document()->firstParagraph()); else gotoLineStart(); } void TQTextCursor::gotoEnd() { if (topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid()) gotoPosition(topParagraph()->document()->lastParagraph(), topParagraph()->document()->lastParagraph()->length() - 1); else gotoLineEnd(); } void TQTextCursor::gotoPageUp(int visibleHeight) { int targetY = globalY() - visibleHeight; TQTextParagraph* old; int index; do { old = para; index = idx; gotoUp(); } while ((old != para || index != idx) && globalY() > targetY); } void TQTextCursor::gotoPageDown(int visibleHeight) { int targetY = globalY() + visibleHeight; TQTextParagraph* old; int index; do { old = para; index = idx; gotoDown(); } while ((old != para || index != idx) && globalY() < targetY); } void TQTextCursor::gotoWordRight() { if (para->string()->isRightToLeft()) gotoPreviousWord(); else gotoNextWord(); } void TQTextCursor::gotoWordLeft() { if (para->string()->isRightToLeft()) gotoNextWord(); else gotoPreviousWord(); } static bool is_seperator(const TQChar &c, bool onlySpace) { if (onlySpace) return c.isSpace(); return c.isSpace() || c == TQLatin1Char('\t') || c == TQLatin1Char('.') || c == TQLatin1Char(',') || c == TQLatin1Char(':') || c == TQLatin1Char(';') || c == TQLatin1Char('-') || c == TQLatin1Char('<') || c == TQLatin1Char('>') || c == TQLatin1Char('[') || c == TQLatin1Char(']') || c == TQLatin1Char('(') || c == TQLatin1Char(')') || c == TQLatin1Char('{') || c == TQLatin1Char('}'); } void TQTextCursor::gotoPreviousWord(bool onlySpace) { gotoPreviousLetter(); tmpX = -1; TQTextString *s = para->string(); bool allowSame = false; if (idx == ((int)s->length()-1)) return; for (int i = idx; i >= 0; --i) { if (is_seperator(s->at(i).c, onlySpace)) { if (!allowSame) continue; idx = i + 1; return; } if (!allowSame && !is_seperator(s->at(i).c, onlySpace)) allowSame = true; } idx = 0; } void TQTextCursor::gotoNextWord(bool onlySpace) { tmpX = -1; TQTextString *s = para->string(); bool allowSame = false; for (int i = idx; i < (int)s->length(); ++i) { if (!is_seperator(s->at(i).c, onlySpace)) { if (!allowSame) continue; idx = i; return; } if (!allowSame && is_seperator(s->at(i).c, onlySpace)) allowSame = true; } if (idx < ((int)s->length()-1)) { gotoLineEnd(); } else if (para->next()) { TQTextParagraph *p = para->next(); while (p && !p->isVisible()) p = p->next(); if (s) { para = p; idx = 0; } } else { gotoLineEnd(); } } bool TQTextCursor::atParagStart() { return idx == 0; } bool TQTextCursor::atParagEnd() { return idx == para->length() - 1; } void TQTextCursor::splitAndInsertEmptyParagraph(bool ind, bool updateIds) { if (!para->document()) return; tmpX = -1; TQTextFormat *f = 0; if (para->document()->useFormatCollection()) { f = para->at(idx)->format(); if (idx == para->length() - 1 && idx > 0) f = para->at(idx - 1)->format(); if (f->isMisspelled()) { f->removeRef(); f = para->document()->formatCollection()->format(f->font(), f->color()); } } if (atParagEnd()) { TQTextParagraph *n = para->next(); TQTextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds); if (f) s->setFormat(0, 1, f, true); s->copyParagData(para); if (ind) { int oi, ni; s->indent(&oi, &ni); para = s; idx = ni; } else { para = s; idx = 0; } } else if (atParagStart()) { TQTextParagraph *p = para->prev(); TQTextParagraph *s = para->document()->createParagraph(para->document(), p, para, updateIds); if (f) s->setFormat(0, 1, f, true); s->copyParagData(para); if (ind) { s->indent(); s->format(); indent(); para->format(); } } else { TQString str = para->string()->toString().mid(idx, 0xFFFFFF); TQTextParagraph *n = para->next(); TQTextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds); s->copyParagData(para); s->remove(0, 1); s->append(str, true); for (int i = 0; i < str.length(); ++i) { TQTextStringChar* tsc = para->at(idx + i); s->setFormat(i, 1, tsc->format(), true); #ifndef QT_NO_TEXTCUSTOMITEM if (tsc->isCustom()) { TQTextCustomItem * item = tsc->customItem(); s->at(i)->setCustomItem(item); tsc->loseCustomItem(); } #endif if (tsc->isAnchor()) s->at(i)->setAnchor(tsc->anchorName(), tsc->anchorHref()); } para->truncate(idx); if (ind) { int oi, ni; s->indent(&oi, &ni); para = s; idx = ni; } else { para = s; idx = 0; } } invalidateNested(); } bool TQTextCursor::remove() { tmpX = -1; if (!atParagEnd()) { int next = para->string()->nextCursorPosition(idx); para->remove(idx, next-idx); int h = para->rect().height(); para->format(-1, true); if (h != para->rect().height()) invalidateNested(); else if (para->document() && para->document()->parent()) para->document()->nextDoubleBuffered = true; return false; } else if (para->next()) { para->join(para->next()); invalidateNested(); return true; } return false; } /* needed to implement backspace the correct way */ bool TQTextCursor::removePreviousChar() { tmpX = -1; if (!atParagStart()) { para->remove(idx-1, 1); int h = para->rect().height(); idx--; // shouldn't be needed, just to make sure. fixCursorPosition(); para->format(-1, true); if (h != para->rect().height()) invalidateNested(); else if (para->document() && para->document()->parent()) para->document()->nextDoubleBuffered = true; return false; } else if (para->prev()) { para = para->prev(); para->join(para->next()); invalidateNested(); return true; } return false; } void TQTextCursor::indent() { int oi = 0, ni = 0; para->indent(&oi, &ni); if (oi == ni) return; if (idx >= oi) idx += ni - oi; else idx = ni; } void TQTextCursor::fixCursorPosition() { // searches for the closest valid cursor position if (para->string()->validCursorPosition(idx)) return; int lineIdx; TQTextStringChar *start = para->lineStartOfChar(idx, &lineIdx, 0); int x = para->string()->at(idx).x; int diff = QABS(start->x - x); int best = lineIdx; TQTextStringChar *c = start; ++c; TQTextStringChar *end = ¶->string()->at(para->length()-1); while (c <= end && !c->lineStart) { int xp = c->x; if (c->rightToLeft) xp += para->string()->width(lineIdx + (c-start)); int ndiff = QABS(xp - x); if (ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start))) { diff = ndiff; best = lineIdx + (c-start); } ++c; } idx = best; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextDocument::TQTextDocument(TQTextDocument *p) : par(p), parentPar(0) #ifndef QT_NO_TEXTCUSTOMITEM , tc(0) #endif , tArray(0), tStopWidth(0) { fCollection = par ? par->fCollection : new TQTextFormatCollection; init(); } void TQTextDocument::init() { oTextValid = true; mightHaveCustomItems = false; if (par) par->insertChild(this); pProcessor = 0; useFC = true; pFormatter = 0; indenter = 0; fParag = 0; txtFormat = TQt::AutoText; preferRichText = false; pages = false; focusIndicator.parag = 0; minw = 0; wused = 0; minwParag = curParag = 0; align = TQt::AlignAuto; nSelections = 1; setStyleSheet(TQStyleSheet::defaultSheet()); #ifndef QT_NO_MIME factory_ = TQMimeSourceFactory::defaultFactory(); #endif contxt.clear(); underlLinks = par ? par->underlLinks : true; backBrush = 0; buf_pixmap = 0; nextDoubleBuffered = false; if (par) withoutDoubleBuffer = par->withoutDoubleBuffer; else withoutDoubleBuffer = false; lParag = fParag = createParagraph(this, 0, 0); cx = 0; cy = 2; if (par) cx = cy = 0; cw = 600; vw = 0; flow_ = new TQTextFlow; flow_->setWidth(cw); leftmargin = rightmargin = 4; scaleFontsFactor = 1; commandHistory = new TQTextCommandHistory(100); tStopWidth = formatCollection()->defaultFormat()->width(TQLatin1Char('x')) * 8; } TQTextDocument::~TQTextDocument() { delete commandHistory; if (par) par->removeChild(this); clear(); delete flow_; if (!par) { delete pFormatter; delete fCollection; } delete pProcessor; delete buf_pixmap; delete indenter; delete backBrush; delete [] tArray; } void TQTextDocument::clear(bool createEmptyParag) { while (fParag) { TQTextParagraph *p = fParag->next(); delete fParag; fParag = p; } if (flow_) flow_->clear(); fParag = lParag = 0; if (createEmptyParag) fParag = lParag = createParagraph(this); selections.clear(); oText.clear(); oTextValid = false; } int TQTextDocument::widthUsed() const { return wused + 2*border_tolerance; } int TQTextDocument::height() const { int h = 0; if (lParag) h = lParag->rect().top() + lParag->rect().height() + 1; int fh = flow_->boundingRect().bottom(); return qMax(h, fh); } TQTextParagraph *TQTextDocument::createParagraph(TQTextDocument *dc, TQTextParagraph *pr, TQTextParagraph *nx, bool updateIds) { return new TQTextParagraph(dc, pr, nx, updateIds); } bool TQTextDocument::setMinimumWidth(int needed, int used, TQTextParagraph *p) { if (needed == -1) { minw = 0; wused = 0; p = 0; } if (p == minwParag) { if (minw > needed) { TQTextParagraph *tp = fParag; while (tp) { if (tp != p && tp->minwidth > needed) { needed = tp->minwidth; minwParag = tp; } tp = tp->n; } } minw = needed; emit minimumWidthChanged(minw); } else if (needed > minw) { minw = needed; minwParag = p; emit minimumWidthChanged(minw); } wused = qMax(wused, used); wused = qMax(wused, minw); cw = qMax(minw, cw); return true; } void TQTextDocument::setPlainText(const TQString &text) { preferRichText = false; clear(); oTextValid = true; oText = text; int lastNl = 0; int nl = text.indexOf(TQLatin1Char('\n')); if (nl == -1) { lParag = createParagraph(this, lParag, 0); if (!fParag) fParag = lParag; TQString s = text; if (!s.isEmpty()) { if (s[(int)s.length() - 1] == TQLatin1Char('\r')) s.remove(s.length() - 1, 1); lParag->append(s); } } else { for (;;) { lParag = createParagraph(this, lParag, 0); if (!fParag) fParag = lParag; int l = nl - lastNl; if (l > 0) { if (text.unicode()[nl-1] == TQLatin1Char('\r')) l--; TQString cs = TQString::fromRawData(text.unicode()+lastNl, l); lParag->append(cs); } if (nl == (int)text.length()) break; lastNl = nl + 1; nl = text.indexOf(TQLatin1Char('\n'), nl + 1); if (nl == -1) nl = text.length(); } } if (!lParag) lParag = fParag = createParagraph(this, 0, 0); } struct TQTextDocumentTag { TQTextDocumentTag(){} TQTextDocumentTag(const TQString&n, const TQStyleSheetItem* s, const TQTextFormat& f) :name(n),style(s), format(f), alignment(TQt::AlignAuto), direction(TQChar::DirON),liststyle(TQStyleSheetItem::ListDisc) { wsm = TQStyleSheetItem::WhiteSpaceNormal; } TQString name; const TQStyleSheetItem* style; TQString anchorHref; TQStyleSheetItem::WhiteSpaceMode wsm; TQTextFormat format; signed int alignment : 16; signed int direction : 5; TQStyleSheetItem::ListStyle liststyle; TQTextDocumentTag( const TQTextDocumentTag& t) { name = t.name; style = t.style; anchorHref = t.anchorHref; wsm = t.wsm; format = t.format; alignment = t.alignment; direction = t.direction; liststyle = t.liststyle; } TQTextDocumentTag& operator=(const TQTextDocumentTag& t) { name = t.name; style = t.style; anchorHref = t.anchorHref; wsm = t.wsm; format = t.format; alignment = t.alignment; direction = t.direction; liststyle = t.liststyle; return *this; } Q_DUMMY_COMPARISON_OPERATOR(TQTextDocumentTag) }; #define NEWPAR \ do{ \ if (!hasNewPar) { \ if (!textEditMode && curpar && curpar->length()>1 \ && curpar->at(curpar->length()-2)->c == TQChar::LineSeparator) \ curpar->remove(curpar->length()-2, 1); \ curpar = createParagraph(this, curpar, curpar->next()); \ styles.append(vec); \ vec = 0; \ } \ hasNewPar = true; \ curpar->rtext = true; \ curpar->align = curtag.alignment; \ curpar->lstyle = curtag.liststyle; \ curpar->litem = (curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem); \ curpar->str->setDirection((TQChar::Direction)curtag.direction); \ space = true; \ tabExpansionColumn = 0; \ delete vec; \ vec = new QVector(); \ for (QStack::Iterator it = tags.begin(); it != tags.end(); ++it) \ vec->append(const_cast((*it).style)); \ vec->append(const_cast(curtag.style)); \ } while(false); void TQTextDocument::setRichText(const TQString &text, const TQString &context, const TQTextFormat *initialFormat) { preferRichText = true; if (!context.isEmpty()) setContext(context); clear(); fParag = lParag = createParagraph(this); oTextValid = true; oText = text; setRichTextInternal(text, 0, initialFormat); fParag->rtext = true; } void TQTextDocument::setRichTextInternal(const TQString &text, TQTextCursor* cursor, const TQTextFormat *initialFormat) { TQTextParagraph* curpar = lParag; int pos = 0; QStack tags; if (!initialFormat) initialFormat = formatCollection()->defaultFormat(); TQTextDocumentTag initag(TQLatin1String(""), sheet_->item(TQLatin1String("")), *initialFormat); if (bodyText.isValid()) initag.format.setColor(bodyText); TQTextDocumentTag curtag = initag; bool space = true; bool canMergeLi = false; bool textEditMode = false; int tabExpansionColumn = 0; const TQChar* doc = static_cast(text.unicode()); int length = text.length(); bool hasNewPar = curpar->length() <= 1; TQString anchorName; // style sheet handling for margin and line spacing calculation below TQTextParagraph* stylesPar = curpar; QVector* vec = 0; QList< QVector *> styles; if (cursor) { cursor->splitAndInsertEmptyParagraph(); TQTextCursor tmp = *cursor; tmp.gotoPreviousLetter(); stylesPar = curpar = tmp.paragraph(); hasNewPar = true; textEditMode = true; } else { NEWPAR; } // set rtext spacing to false for the initial paragraph. curpar->rtext = false; TQString wellKnownTags = TQLatin1String("br hr wsp table qt body meta title"); while (pos < length) { if (hasPrefix(doc, length, pos, TQLatin1Char('<'))){ if (!hasPrefix(doc, length, pos+1, TQLatin1Char('/'))) { // open tag QMap attr; QMap::Iterator it, end = attr.end(); bool emptyTag = false; TQString tagname = parseOpenTag(doc, length, pos, attr, emptyTag); if (tagname.isEmpty()) continue; // nothing we could do with this, probably parse error const TQStyleSheetItem* nstyle = sheet_->item(tagname); if (nstyle) { // we might have to close some 'forgotten' tags while (!nstyle->allowedInContext(curtag.style)) { TQString msg; msg.sprintf("QText Warning: Document not valid ('%s' not allowed in '%s' #%d)", tagname.ascii(), curtag.style->name().ascii(), pos); sheet_->error(msg); if (tags.isEmpty()) break; curtag = tags.pop(); } /* special handling for p and li for HTML compatibility. We do not want to embed blocks in p, and we do not want new blocks inside non-empty lis. Plus we want to merge empty lis sometimes. */ if(nstyle->displayMode() == TQStyleSheetItem::DisplayListItem) { canMergeLi = true; } else if (nstyle->displayMode() == TQStyleSheetItem::DisplayBlock) { while (curtag.style->name() == TQLatin1String("p")) { if (tags.isEmpty()) break; curtag = tags.pop(); } if (curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem) { // we are in a li and a new block comes along if (nstyle->name() == TQLatin1String("ul") || nstyle->name() == TQLatin1String("ol")) hasNewPar = false; // we want an empty li (like most browsers) if (!hasNewPar) { /* do not add new blocks inside non-empty lis */ while (curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem) { if (tags.isEmpty()) break; curtag = tags.pop(); } } else if (canMergeLi) { /* we have an empty li and a block comes along, merge them */ nstyle = curtag.style; } canMergeLi = false; } } } #ifndef QT_NO_TEXTCUSTOMITEM TQTextCustomItem* custom = 0; #else bool custom = false; #endif // some well-known tags, some have a nstyle, some not if (wellKnownTags.contains(tagname)) { if (tagname == TQLatin1String("br")) { emptyTag = space = true; int index = qMax(curpar->length(),1) - 1; TQTextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor); curpar->append(TQString(TQChar(TQChar::LineSeparator))); curpar->setFormat(index, 1, &format); hasNewPar = false; } else if (tagname == TQLatin1String("hr")) { emptyTag = space = true; #ifndef QT_NO_TEXTCUSTOMITEM custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this); #endif } else if (tagname == TQLatin1String("table")) { emptyTag = space = true; #ifndef QT_NO_TEXTCUSTOMITEM TQTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor); curpar->setAlignment(curtag.alignment); custom = parseTable(attr, format, doc, length, pos, curpar); #endif } else if (tagname == TQLatin1String("qt") || tagname == TQLatin1String("body")) { it = attr.find(TQLatin1String("bgcolor")); if (it != end) { TQBrush *b = new TQBrush(QColor(*it)); setPaper(b); } it = attr.find(TQLatin1String("background")); if (it != end) { #ifndef QT_NO_MIME TQImage img; TQString bg = *it; const QMimeSource* m = factory_->data(bg, contxt); if (!m) { qCritical("QRichText: no mimesource for %s", QFile::encodeName(bg).data()); } else { if (!TQImageDrag::decode(TQT_TQMIMESOURCE_CONST(m), img)) { qCritical("TQTextImage: cannot decode %s", QFile::encodeName(bg).data()); } } if (!img.isNull()) { TQBrush *b = new TQBrush(QColor(), TQPixmap(img)); setPaper(b); } #endif } it = attr.find(TQLatin1String("text")); if (it != end) { QColor c(*it); initag.format.setColor(c); curtag.format.setColor(c); bodyText = c; } it = attr.find(TQLatin1String("link")); if (it != end) linkColor = QColor(*it); it = attr.find(TQLatin1String("title")); if (it != end) attribs.insert(TQLatin1String("title"), *it); if (textEditMode) { it = attr.find(TQLatin1String("style")); if (it != end) { TQString a = *it; int count = a.count(TQLatin1Char(';')) + 1; for (int s = 0; s < count; s++) { TQString style = a.section(TQLatin1Char(';'), s, s); if (style.startsWith(TQLatin1String("font-size:")) && style.endsWith(TQLatin1String("pt"))) { scaleFontsFactor = double(formatCollection()->defaultFormat()->fn.pointSize()) / style.mid(10, style.length() - 12).toInt(); } } } nstyle = 0; // ignore body in textEditMode } // end qt- and body-tag handling } else if (tagname == TQLatin1String("meta")) { if (attr[TQLatin1String("name")] == TQLatin1String("qrichtext") && attr[TQLatin1String("content")] == TQLatin1String("1")) textEditMode = true; } else if (tagname == TQLatin1String("title")) { TQString title; while (pos < length) { if (hasPrefix(doc, length, pos, TQLatin1Char('<')) && hasPrefix(doc, length, pos+1, TQLatin1Char('/')) && parseCloseTag(doc, length, pos) == TQLatin1String("title")) break; title += doc[pos]; ++pos; } attribs.insert(TQLatin1String("title"), title); } } // end of well-known tag handling #ifndef QT_NO_TEXTCUSTOMITEM if (!custom) // try generic custom item custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this); #endif if (!nstyle && !custom) // we have no clue what this tag could be, ignore it continue; if (custom) { #ifndef QT_NO_TEXTCUSTOMITEM int index = qMax(curpar->length(),1) - 1; TQTextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor); curpar->append(TQString(TQLatin1Char('*'))); TQTextFormat* f = formatCollection()->format(&format); curpar->setFormat(index, 1, f); curpar->at(index)->setCustomItem(custom); if (!curtag.anchorHref.isEmpty()) curpar->at(index)->setAnchor(TQString(), curtag.anchorHref); if (!anchorName.isEmpty() ) { curpar->at(index)->setAnchor(anchorName, curpar->at(index)->anchorHref()); anchorName.clear(); } registerCustomItem(custom, curpar); hasNewPar = false; #endif } else if (!emptyTag) { /* if we do nesting, push curtag on the stack, otherwise reinint curag. */ if (curtag.style->name() != tagname || nstyle->selfNesting()) { tags.push(curtag); } else { if (!tags.isEmpty()) curtag = tags.top(); else curtag = initag; } curtag.name = tagname; curtag.style = nstyle; curtag.name = tagname; curtag.style = nstyle; if (nstyle->whiteSpaceMode() != TQStyleSheetItem::WhiteSpaceModeUndefined) curtag.wsm = nstyle->whiteSpaceMode(); /* netscape compatibility: eat a newline and only a newline if a pre block starts */ if (curtag.wsm == TQStyleSheetItem::WhiteSpacePre && nstyle->displayMode() == TQStyleSheetItem::DisplayBlock) eat(doc, length, pos, TQLatin1Char('\n')); /* ignore whitespace for inline elements if there was already one*/ if (!textEditMode && (curtag.wsm == TQStyleSheetItem::WhiteSpaceNormal || curtag.wsm == TQStyleSheetItem::WhiteSpaceNoWrap) && (space || nstyle->displayMode() != TQStyleSheetItem::DisplayInline)) eatSpace(doc, length, pos); curtag.format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor); if (nstyle->isAnchor()) { if (!anchorName.isEmpty()) anchorName += TQLatin1Char('#') + attr[TQLatin1String("name")]; else anchorName = attr[TQLatin1String("name")]; curtag.anchorHref = attr[TQLatin1String("href")]; } if (nstyle->alignment() != TQStyleSheetItem::Undefined) curtag.alignment = nstyle->alignment(); if (nstyle->listStyle() != TQStyleSheetItem::ListStyleUndefined) curtag.liststyle = nstyle->listStyle(); if (nstyle->displayMode() == TQStyleSheetItem::DisplayBlock || nstyle->displayMode() == TQStyleSheetItem::DisplayListItem) { if (nstyle->name() == TQLatin1String("ol") || nstyle->name() == TQLatin1String("ul") || nstyle->name() == TQLatin1String("li")) { TQString type = attr[TQLatin1String("type")]; if (!type.isEmpty()) { if (type == TQLatin1String("1")) { curtag.liststyle = TQStyleSheetItem::ListDecimal; } else if (type == TQLatin1String("a")) { curtag.liststyle = TQStyleSheetItem::ListLowerAlpha; } else if (type == TQLatin1String("A")) { curtag.liststyle = TQStyleSheetItem::ListUpperAlpha; } else { type = type.toLower(); if (type == TQLatin1String("square")) curtag.liststyle = TQStyleSheetItem::ListSquare; else if (type == TQLatin1String("disc")) curtag.liststyle = TQStyleSheetItem::ListDisc; else if (type == TQLatin1String("circle")) curtag.liststyle = TQStyleSheetItem::ListCircle; } } } /* Internally we treat ordered and bullet lists the same for margin calculations. In order to have fast pointer compares in the xMargin() functions we restrict ourselves to
    . Once we calculate the margins in the parser rathern than later, the unelegance of this approach goes awy */ if (nstyle->name() == TQLatin1String("ul")) curtag.style = sheet_->item(TQLatin1String("ol")); it = attr.find(TQLatin1String("align")); if (it != end) { TQString align = (*it).toLower(); if (align == TQLatin1String("center")) curtag.alignment = Qt::AlignCenter; else if (align == TQLatin1String("right")) curtag.alignment = Qt::AlignRight; else if (align == TQLatin1String("justify")) curtag.alignment = Qt::AlignJustify; } it = attr.find(TQLatin1String("dir")); if (it != end) { TQString dir = (*it).toLower(); if (dir == TQLatin1String("rtl")) curtag.direction = TQChar::DirR; else if (dir == TQLatin1String("ltr")) curtag.direction = TQChar::DirL; } NEWPAR; if (curtag.style && curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem) { it = attr.find(TQLatin1String("value")); if (it != end) curpar->setListValue((*it).toInt()); } it = attr.find(TQLatin1String("style")); if (it != end) { TQString a = *it; bool ok = true; int count = a.count(TQLatin1Char(';'))+1; for (int s = 0; ok && s < count; s++) { TQString style = a.section(TQLatin1Char(';'), s, s); if (style.startsWith(TQLatin1String("margin-top:")) && style.endsWith(TQLatin1String("px"))) curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok); else if (style.startsWith(TQLatin1String("margin-bottom:")) && style.endsWith(TQLatin1String("px"))) curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok); else if (style.startsWith(TQLatin1String("margin-left:")) && style.endsWith(TQLatin1String("px"))) curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok); else if (style.startsWith(TQLatin1String("margin-right:")) && style.endsWith(TQLatin1String("px"))) curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok); else if (style.startsWith(TQLatin1String("text-indent:")) && style.endsWith(TQLatin1String("px"))) curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok); } if (!ok) // be pressmistic curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0; } } else if (nstyle->name() == TQLatin1String("html")) { it = attr.find(TQLatin1String("dir")); if (it != end) { TQString dir = (*it).toLower(); if (dir == TQLatin1String("rtl")) curtag.direction = TQChar::DirR; else if (dir == TQLatin1String("ltr")) curtag.direction = TQChar::DirL; } } } } else { TQString tagname = parseCloseTag(doc, length, pos); if (tagname.isEmpty()) continue; // nothing we could do with this, probably parse error if (!sheet_->item(tagname)) // ignore unknown tags continue; if (tagname == TQLatin1String("li")) continue; // we close a block item. Since the text may continue, we need to have a new paragraph bool needNewPar = curtag.style->displayMode() == TQStyleSheetItem::DisplayBlock || curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem; // html slopiness: handle unbalanched tag closing while (curtag.name != tagname) { TQString msg; msg.sprintf("QText Warning: Document not valid ('%s' not closed before '%s' #%d)", curtag.name.ascii(), tagname.ascii(), pos); sheet_->error(msg); if (tags.isEmpty()) break; curtag = tags.pop(); } // close the tag if (!tags.isEmpty()) curtag = tags.pop(); else curtag = initag; if (needNewPar) { if (textEditMode && (tagname == TQLatin1String("p") || tagname == TQLatin1String("div"))) // preserve empty paragraphs hasNewPar = false; NEWPAR; } } } else { // normal contents TQString s; TQChar c; while (pos < length && !hasPrefix(doc, length, pos, TQLatin1Char('<'))){ if (textEditMode) { // text edit mode: we handle all white space but ignore newlines c = parseChar(doc, length, pos, TQStyleSheetItem::WhiteSpacePre); if (c == TQChar::LineSeparator) break; } else { int l = pos; c = parseChar(doc, length, pos, curtag.wsm); // in white space pre mode: treat any space as non breakable // and expand tabs to eight character wide columns. if (curtag.wsm == TQStyleSheetItem::WhiteSpacePre) { if (c == TQLatin1Char('\t')) { c = TQLatin1Char(' '); while((++tabExpansionColumn)%8) s += c; } if (c == TQChar::LineSeparator) tabExpansionColumn = 0; else tabExpansionColumn++; } if (c == TQLatin1Char(' ') || c == TQChar::LineSeparator) { /* avoid overlong paragraphs by forcing a new paragraph after 4096 characters. This case can occur when loading undiscovered plain text documents in rich text mode. Instead of hanging forever, we do the trick. */ if (curtag.wsm == TQStyleSheetItem::WhiteSpaceNormal && s.length() > 4096) do { if (doc[l] == TQLatin1Char('\n')) { hasNewPar = false; // for a new paragraph ... NEWPAR; hasNewPar = false; // ... and make it non-reusable c = TQLatin1Char('\n'); // make sure we break below break; } } while (++l < pos); } } if (c == TQLatin1Char('\n')) break; // break on newlines, pre delievers a TQChar::LineSeparator bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode; if (curtag.wsm == TQStyleSheetItem::WhiteSpaceNormal && c_isSpace && space) continue; if (c == TQLatin1Char('\r')) continue; space = c_isSpace; s += c; } if (!s.isEmpty() && curtag.style->displayMode() != TQStyleSheetItem::DisplayNone) { hasNewPar = false; int index = qMax(curpar->length(),1) - 1; curpar->append(s); if (curtag.wsm != TQStyleSheetItem::WhiteSpaceNormal) { TQTextString *str = curpar->string(); for (int i = index; i < index + s.length(); ++i) str->at(i).nobreak = true; } TQTextFormat* f = formatCollection()->format(&curtag.format); curpar->setFormat(index, s.length(), f, false); // do not use collection because we have done that already f->ref += s.length() -1; // that what friends are for... if (!curtag.anchorHref.isEmpty()) { for (int i = 0; i < int(s.length()); i++) curpar->at(index + i)->setAnchor(TQString(), curtag.anchorHref); } if (!anchorName.isEmpty() ) { for (int i = 0; i < int(s.length()); i++) curpar->at(index + i)->setAnchor(anchorName, curpar->at(index + i)->anchorHref()); anchorName.clear(); } } } } if (hasNewPar && curpar != fParag && !cursor && stylesPar != curpar) { // cleanup unused last paragraphs curpar = curpar->p; delete curpar->n; } if (!anchorName.isEmpty() ) { curpar->at(curpar->length() - 1)->setAnchor(anchorName, curpar->at(curpar->length() - 1)->anchorHref()); anchorName.clear(); } setRichTextMarginsInternal(styles, stylesPar); if (cursor) { cursor->gotoPreviousLetter(); cursor->remove(); } while (!styles.isEmpty()) delete styles.takeFirst(); delete vec; } void TQTextDocument::setRichTextMarginsInternal(QList< QVector *>& styles, TQTextParagraph* stylesPar) { // margin and line spacing calculation // qDebug("setRichTextMarginsInternal: styles.size() = %d", styles.size()); QVector* prevStyle = 0; int stylesIndex = 0; QVector* curStyle = styles.size() ? styles.first() : 0; QVector* nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0; while (stylesPar) { if (!curStyle) { stylesPar = stylesPar->next(); prevStyle = curStyle; curStyle = nextStyle; nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0; continue; } int i, mar; TQStyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0; if (mainStyle && mainStyle->displayMode() == TQStyleSheetItem::DisplayListItem) stylesPar->setListItem(true); int numLists = 0; for (i = 0; i < (int)curStyle->size(); ++i) { if ((*curStyle)[i]->displayMode() == TQStyleSheetItem::DisplayBlock && (*curStyle)[i]->listStyle() != TQStyleSheetItem::ListStyleUndefined) numLists++; } stylesPar->ldepth = numLists; if (stylesPar->next() && nextStyle) { // also set the depth of the next paragraph, required for the margin calculation numLists = 0; for (i = 0; i < (int)nextStyle->size(); ++i) { if ((*nextStyle)[i]->displayMode() == TQStyleSheetItem::DisplayBlock && (*nextStyle)[i]->listStyle() != TQStyleSheetItem::ListStyleUndefined) numLists++; } stylesPar->next()->ldepth = numLists; } // do the top margin TQStyleSheetItem* item = mainStyle; int m; if (stylesPar->utm > 0) { m = stylesPar->utm-1; stylesPar->utm = 0; } else { m = qMax(0, item->margin(TQStyleSheetItem::MarginTop)); if (stylesPar->ldepth) { if (item->displayMode() == TQStyleSheetItem::DisplayListItem) m /= stylesPar->ldepth * stylesPar->ldepth; else m = 0; } } for (i = (int)curStyle->size() - 2 ; i >= 0; --i) { item = (*curStyle)[i]; if (prevStyle && i < (int) prevStyle->size() && ( item->displayMode() == TQStyleSheetItem::DisplayBlock && (*prevStyle)[i] == item)) break; // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags if (item->listStyle() != TQStyleSheetItem::ListStyleUndefined && (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item)) continue; mar = qMax(0, item->margin(TQStyleSheetItem::MarginTop)); m = qMax(m, mar); } stylesPar->utm = m - stylesPar->topMargin(); // do the bottom margin item = mainStyle; if (stylesPar->ubm > 0) { m = stylesPar->ubm-1; stylesPar->ubm = 0; } else { m = qMax(0, item->margin(TQStyleSheetItem::MarginBottom)); if (stylesPar->ldepth) { if (item->displayMode() == TQStyleSheetItem::DisplayListItem) m /= stylesPar->ldepth * stylesPar->ldepth; else m = 0; } } for (i = (int)curStyle->size() - 2 ; i >= 0; --i) { item = (*curStyle)[i]; if (nextStyle && i < (int) nextStyle->size() && ( item->displayMode() == TQStyleSheetItem::DisplayBlock && (*nextStyle)[i] == item)) break; // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags if (item->listStyle() != TQStyleSheetItem::ListStyleUndefined && (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item)) continue; mar = qMax(0, item->margin(TQStyleSheetItem::MarginBottom)); m = qMax(m, mar); } stylesPar->ubm = m - stylesPar->bottomMargin(); // do the left margin, simplyfied item = mainStyle; if (stylesPar->ulm > 0) { m = stylesPar->ulm-1; stylesPar->ulm = 0; } else { m = qMax(0, item->margin(TQStyleSheetItem::MarginLeft)); } for (i = (int)curStyle->size() - 2 ; i >= 0; --i) { item = (*curStyle)[i]; m += qMax(0, item->margin(TQStyleSheetItem::MarginLeft)); } stylesPar->ulm = m - stylesPar->leftMargin(); // do the right margin, simplyfied item = mainStyle; if (stylesPar->urm > 0) { m = stylesPar->urm-1; stylesPar->urm = 0; } else { m = qMax(0, item->margin(TQStyleSheetItem::MarginRight)); } for (i = (int)curStyle->size() - 2 ; i >= 0; --i) { item = (*curStyle)[i]; m += qMax(0, item->margin(TQStyleSheetItem::MarginRight)); } stylesPar->urm = m - stylesPar->rightMargin(); // do the first line margin, which really should be called text-indent item = mainStyle; if (stylesPar->uflm > 0) { m = stylesPar->uflm-1; stylesPar->uflm = 0; } else { m = qMax(0, item->margin(TQStyleSheetItem::MarginFirstLine)); } for (i = (int)curStyle->size() - 2 ; i >= 0; --i) { item = (*curStyle)[i]; mar = qMax(0, item->margin(TQStyleSheetItem::MarginFirstLine)); m = qMax(m, mar); } stylesPar->uflm =m - stylesPar->firstLineMargin(); // do the bogus line "spacing", which really is just an extra margin item = mainStyle; for (i = (int)curStyle->size() - 1 ; i >= 0; --i) { item = (*curStyle)[i]; if (item->lineSpacing() != TQStyleSheetItem::Undefined) { stylesPar->ulinespacing = item->lineSpacing(); if (formatCollection() && stylesPar->ulinespacing < formatCollection()->defaultFormat()->height()) stylesPar->ulinespacing += formatCollection()->defaultFormat()->height(); break; } } stylesPar = stylesPar->next(); prevStyle = curStyle; curStyle = nextStyle; nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0; } } void TQTextDocument::setText(const TQString &text, const TQString &context) { focusIndicator.parag = 0; selections.clear(); if ((txtFormat == TQt::AutoText && TQStyleSheet::mightBeRichText(text)) || txtFormat == TQt::RichText) setRichText(text, context); else setPlainText(text); } TQString TQTextDocument::plainText() const { TQString buffer; TQString s; TQTextParagraph *p = fParag; while (p) { if (!p->mightHaveCustomItems) { const TQTextString *ts = p->string(); // workaround VC++ and Borland s = ts->toString(); // with false we don't fix spaces (nbsp) } else { for (int i = 0; i < p->length() - 1; ++i) { #ifndef QT_NO_TEXTCUSTOMITEM if (p->at(i)->isCustom()) { if (p->at(i)->customItem()->isNested()) { s += TQLatin1String("\n"); TQTextTable *t = (TQTextTable*)p->at(i)->customItem(); QList cells = t->tableCells(); for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *c = cells.at(idx); s += c->richText()->plainText() + TQLatin1String("\n"); } s += TQLatin1String("\n"); } } else #endif { s += p->at(i)->c; } } } s.remove(s.length() - 1, 1); if (p->next()) s += TQLatin1String("\n"); buffer += s; p = p->next(); } return buffer; } static TQString align_to_string(int a) { if (a & Qt::AlignRight) return TQLatin1String(" align=\"right\""); if (a & Qt::AlignHCenter) return TQLatin1String(" align=\"center\""); if (a & Qt::AlignJustify) return TQLatin1String(" align=\"justify\""); return TQString(); } static TQString direction_to_string(int dir) { if (dir != TQChar::DirON) return (dir == TQChar::DirL? TQLatin1String(" dir=\"ltr\"") : TQLatin1String(" dir=\"rtl\"")); return TQString(); } static TQString list_value_to_string(int v) { if (v != -1) return TQLatin1String(" listvalue=\"") + TQString::number(v) + TQLatin1Char('"'); return TQString(); } static TQString list_style_to_string(int v) { switch(v) { case TQStyleSheetItem::ListDecimal: return TQLatin1String("\"1\""); case TQStyleSheetItem::ListLowerAlpha: return TQLatin1String("\"a\""); case TQStyleSheetItem::ListUpperAlpha: return TQLatin1String("\"A\""); case TQStyleSheetItem::ListDisc: return TQLatin1String("\"disc\""); case TQStyleSheetItem::ListSquare: return TQLatin1String("\"square\""); case TQStyleSheetItem::ListCircle: return TQLatin1String("\"circle\""); default: return TQString(); } } static inline bool list_is_ordered(int v) { return v == TQStyleSheetItem::ListDecimal || v == TQStyleSheetItem::ListLowerAlpha || v == TQStyleSheetItem::ListUpperAlpha; } static TQString margin_to_string(TQStyleSheetItem* style, int t, int b, int l, int r, int fl) { TQString s; if (l > 0) s += TQString(s.size() ? TQLatin1String(";") : TQLatin1String("")) + TQLatin1String("margin-left:") + TQString::number(l+qMax(0,style->margin(TQStyleSheetItem::MarginLeft))) + TQLatin1String("px"); if (r > 0) s += TQString(s.size() ? TQLatin1String(";") : TQLatin1String("")) + TQLatin1String("margin-right:") + TQString::number(r+qMax(0,style->margin(TQStyleSheetItem::MarginRight))) + TQLatin1String("px"); if (t > 0) s += TQString(s.size() ? TQLatin1String(";") : TQLatin1String("")) + TQLatin1String("margin-top:") + TQString::number(t+qMax(0,style->margin(TQStyleSheetItem::MarginTop))) + TQLatin1String("px"); if (b > 0) s += TQString(s.size() ? TQLatin1String(";") : TQLatin1String("")) + TQLatin1String("margin-bottom:") + TQString::number(b+qMax(0,style->margin(TQStyleSheetItem::MarginBottom))) + TQLatin1String("px"); if (fl > 0) s += TQString(s.size() ? TQLatin1String(";") : TQLatin1String("")) + TQLatin1String("text-indent:") + TQString::number(fl+qMax(0,style->margin(TQStyleSheetItem::MarginFirstLine))) + TQLatin1String("px"); if (s.size()) return TQLatin1String(" style=\"") + s + TQLatin1String("\""); return TQString(); } TQString TQTextDocument::richText() const { TQString s = TQLatin1String(""); if (!par) { s += TQLatin1String("defaultFormat()->font().pointSize()); s += TQLatin1String("pt;font-family:"); s += formatCollection()->defaultFormat()->font().family(); s += TQLatin1String("\">"); } TQTextParagraph* p = fParag; TQStyleSheetItem* item_p = styleSheet()->item(TQLatin1String("p")); TQStyleSheetItem* item_div = styleSheet()->item(TQLatin1String("div")); TQStyleSheetItem* item_ul = styleSheet()->item(TQLatin1String("ul")); TQStyleSheetItem* item_ol = styleSheet()->item(TQLatin1String("ol")); TQStyleSheetItem* item_li = styleSheet()->item(TQLatin1String("li")); if (!item_p || !item_div || !item_ul || !item_ol || !item_li) { qWarning("QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)"); return TQString(); } int pastListDepth = 0; int listDepth = 0; #if 0 int futureListDepth = 0; #endif QVector listStyles(10); while (p) { listDepth = p->listDepth(); if (listDepth < pastListDepth) { for (int i = pastListDepth; i > listDepth; i--) s += list_is_ordered(listStyles[i]) ? TQLatin1String("
") : TQLatin1String(""); s += TQLatin1Char('\n'); } else if (listDepth > pastListDepth) { s += TQLatin1Char('\n'); listStyles.resize(qMax((int)listStyles.size(), listDepth+1)); TQString list_type; listStyles[listDepth] = p->listStyle(); if (!list_is_ordered(p->listStyle()) || item_ol->listStyle() != p->listStyle()) list_type = TQLatin1String(" type=") + list_style_to_string(p->listStyle()); for (int i = pastListDepth; i < listDepth; i++) { s += list_is_ordered(p->listStyle()) ? TQLatin1String("'); } } else { s += TQLatin1Char('\n'); } TQString ps = p->richText(); #if 0 // for the bottom margin we need to know whether we are at the end of a list futureListDepth = 0; if (listDepth > 0 && p->next()) futureListDepth = p->next()->listDepth(); #endif if (richTextExportStart && richTextExportStart->paragraph() ==p && richTextExportStart->index() == 0) s += TQLatin1String(""); if (p->isListItem()) { s += TQLatin1String("listStyle() != listStyles[listDepth]) s += TQLatin1String(" type=") + list_style_to_string(p->listStyle()); s += align_to_string(p->alignment()); s += margin_to_string(item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm); s += list_value_to_string(p->listValue()); s += direction_to_string(p->direction()); s += TQLatin1Char('>'); s += ps; s += TQLatin1String(""); } else if (p->listDepth()) { s += TQLatin1String("alignment()); s += margin_to_string(item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm); s += direction_to_string(p->direction()); s += TQLatin1Char('>'); s += ps; s += TQLatin1String(""); } else { // normal paragraph item s += TQLatin1String("alignment()); s += margin_to_string(item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm); s += direction_to_string(p->direction()); s += TQLatin1Char('>'); s += ps; s += TQLatin1String("

"); } pastListDepth = listDepth; p = p->next(); } while (listDepth > 0) { s += list_is_ordered(listStyles[listDepth]) ? TQLatin1String("") : TQLatin1String(""); listDepth--; } if (!par) s += TQLatin1String("\n\n"); return s; } TQString TQTextDocument::text() const { if ((txtFormat == TQt::AutoText && preferRichText) || txtFormat == TQt::RichText) return richText(); return plainText(); } TQString TQTextDocument::text(int parag) const { TQTextParagraph *p = paragAt(parag); if (!p) return TQString(); if ((txtFormat == TQt::AutoText && preferRichText) || txtFormat == TQt::RichText) return p->richText(); else return p->string()->toString(); } void TQTextDocument::invalidate() { TQTextParagraph *s = fParag; while (s) { s->invalidate(0); s = s->next(); } } void TQTextDocument::selectionStart(int id, int ¶gId, int &index) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return; TQTextDocumentSelection &sel = *it; paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId(); index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index(); } TQTextCursor TQTextDocument::selectionStartCursor(int id) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return TQTextCursor(this); TQTextDocumentSelection &sel = *it; if (sel.swapped) return sel.endCursor; return sel.startCursor; } TQTextCursor TQTextDocument::selectionEndCursor(int id) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return TQTextCursor(this); TQTextDocumentSelection &sel = *it; if (!sel.swapped) return sel.endCursor; return sel.startCursor; } void TQTextDocument::selectionEnd(int id, int ¶gId, int &index) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return; TQTextDocumentSelection &sel = *it; paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId(); index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index(); } void TQTextDocument::addSelection(int id) { nSelections = qMax(nSelections, id + 1); } static void setSelectionEndHelper(int id, TQTextDocumentSelection &sel, TQTextCursor &start, TQTextCursor &end) { TQTextCursor c1 = start; TQTextCursor c2 = end; if (sel.swapped) { c1 = end; c2 = start; } c1.paragraph()->removeSelection(id); c2.paragraph()->removeSelection(id); if (c1.paragraph() != c2.paragraph()) { c1.paragraph()->setSelection(id, c1.index(), c1.paragraph()->length() - 1); c2.paragraph()->setSelection(id, 0, c2.index()); } else { c1.paragraph()->setSelection(id, qMin(c1.index(), c2.index()), qMax(c1.index(), c2.index())); } sel.startCursor = start; sel.endCursor = end; if (sel.startCursor.paragraph() == sel.endCursor.paragraph()) sel.swapped = sel.startCursor.index() > sel.endCursor.index(); } bool TQTextDocument::setSelectionEnd(int id, const TQTextCursor &cursor) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return false; TQTextDocumentSelection &sel = *it; TQTextCursor start = sel.startCursor; TQTextCursor end = cursor; if (start == end) { removeSelection(id); setSelectionStart(id, cursor); return true; } if (sel.endCursor.paragraph() == end.paragraph()) { setSelectionEndHelper(id, sel, start, end); return true; } bool inSelection = false; TQTextCursor c(this); TQTextCursor tmp = sel.startCursor; if (sel.swapped) tmp = sel.endCursor; tmp.restoreState(); TQTextCursor tmp2 = cursor; tmp2.restoreState(); c.setParagraph(tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph()); bool hadStart = false; bool hadEnd = false; bool hadStartParag = false; bool hadEndParag = false; bool hadOldStart = false; bool hadOldEnd = false; bool leftSelection = false; sel.swapped = false; for (;;) { if (c == start) hadStart = true; if (c == end) hadEnd = true; if (c.paragraph() == start.paragraph()) hadStartParag = true; if (c.paragraph() == end.paragraph()) hadEndParag = true; if (c == sel.startCursor) hadOldStart = true; if (c == sel.endCursor) hadOldEnd = true; if (!sel.swapped && ((hadEnd && !hadStart) || (hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index()))) sel.swapped = true; if ((c == end && hadStartParag) || (c == start && hadEndParag)) { TQTextCursor tmp = c; tmp.restoreState(); if (tmp.paragraph() != c.paragraph()) { int sstart = tmp.paragraph()->selectionStart(id); tmp.paragraph()->removeSelection(id); tmp.paragraph()->setSelection(id, sstart, tmp.index()); } } if (inSelection && ((c == end && hadStart) || (c == start && hadEnd))) leftSelection = true; else if (!leftSelection && !inSelection && (hadStart || hadEnd)) inSelection = true; bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection(id) && c.atParagEnd(); c.paragraph()->removeSelection(id); if (inSelection) { if (c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph()) { c.paragraph()->setSelection(id, qMin(start.index(), end.index()), qMax(start.index(), end.index())); } else if (c.paragraph() == start.paragraph() && !hadEndParag) { c.paragraph()->setSelection(id, start.index(), c.paragraph()->length() - 1); } else if (c.paragraph() == end.paragraph() && !hadStartParag) { c.paragraph()->setSelection(id, end.index(), c.paragraph()->length() - 1); } else if (c.paragraph() == end.paragraph() && hadEndParag) { c.paragraph()->setSelection(id, 0, end.index()); } else if (c.paragraph() == start.paragraph() && hadStartParag) { c.paragraph()->setSelection(id, 0, start.index()); } else { c.paragraph()->setSelection(id, 0, c.paragraph()->length() - 1); } } if (leftSelection) inSelection = false; if (noSelectionAnymore) break; // *ugle*hack optimization TQTextParagraph *p = c.paragraph(); if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph()) { c.gotoNextLetter(); if (p == lastParagraph() && c.atParagEnd()) break; } else { if (p->document()->parent()) do { c.gotoNextLetter(); } while (c.paragraph() == p); else c.setParagraph(p->next()); } } if (!sel.swapped) sel.startCursor.paragraph()->setSelection(id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1); sel.startCursor = start; sel.endCursor = end; if (sel.startCursor.paragraph() == sel.endCursor.paragraph()) sel.swapped = sel.startCursor.index() > sel.endCursor.index(); setSelectionEndHelper(id, sel, start, end); return true; } void TQTextDocument::selectAll(int id) { removeSelection(id); TQTextDocumentSelection sel; sel.swapped = false; TQTextCursor c(this); c.setParagraph(fParag); c.setIndex(0); sel.startCursor = c; c.setParagraph(lParag); c.setIndex(lParag->length() - 1); sel.endCursor = c; selections.insert(id, sel); TQTextParagraph *p = fParag; while (p) { p->setSelection(id, 0, p->length() - 1); p = p->next(); } for (int idx = 0; idx < childList.size(); ++idx) { TQTextDocument *dc = childList.at(idx); dc->selectAll(id); } } bool TQTextDocument::removeSelection(int id) { if (!selections.contains(id)) return false; TQTextDocumentSelection &sel = selections[id]; TQTextCursor start = sel.swapped ? sel.endCursor : sel.startCursor; TQTextCursor end = sel.swapped ? sel.startCursor : sel.endCursor; TQTextParagraph* p = 0; while (start != end) { if (p != start.paragraph()) { p = start.paragraph(); p->removeSelection(id); //### avoid endless loop by all means necessary, did somebody mention refactoring? if (!parent() && p == lParag) break; } start.gotoNextLetter(); } p = start.paragraph(); p->removeSelection(id); selections.remove(id); return true; } TQString TQTextDocument::selectedText(int id, bool asRichText) const { QMap::ConstIterator it = selections.find(id); if (it == selections.end()) return TQString(); TQTextDocumentSelection sel = *it; TQTextCursor c1 = sel.startCursor; TQTextCursor c2 = sel.endCursor; if (sel.swapped) { c2 = sel.startCursor; c1 = sel.endCursor; } /* 3.0.3 improvement: Make it possible to get a reasonable selection inside a table. This approach is very conservative: make sure that both cursors have the same depth level and point to paragraphs within the same text document. Meaning if you select text in two table cells, you will get the entire table. This is still far better than the 3.0.2, where you always got the entire table. ### Fix this properly when refactoring */ while (c2.nestedDepth() > c1.nestedDepth()) c2.oneUp(); while (c1.nestedDepth() > c2.nestedDepth()) c1.oneUp(); while (c1.nestedDepth() && c2.nestedDepth() && c1.paragraph()->document() != c2.paragraph()->document()) { c1.oneUp(); c2.oneUp(); } // do not trust sel_swapped with tables. Fix this properly when refactoring as well if (c1.paragraph()->paragId() > c2.paragraph()->paragId() || (c1.paragraph() == c2.paragraph() && c1.index() > c2.index())) { TQTextCursor tmp = c1; c2 = c1; c1 = tmp; } // end selection 3.0.3 improvement if (asRichText && !parent()) { richTextExportStart = &c1; richTextExportEnd = &c2; TQString sel = richText(); int from = sel.indexOf(TQLatin1String("")); if (from >= 0) { from += 20; // find the previous span and move it into the start fragment before we clip it TQString prevspan; int pspan = sel.lastIndexOf(TQLatin1String(" sel.lastIndexOf(TQLatin1String("'), pspan); prevspan = sel.mid(pspan, spanend - pspan + 1); } int to = sel.lastIndexOf(TQLatin1String("")); if (from <= to) sel = TQLatin1String("") + prevspan + sel.mid(from, to - from); } richTextExportStart = richTextExportEnd = 0; return sel; } TQString s; if (c1.paragraph() == c2.paragraph()) { TQTextParagraph *p = c1.paragraph(); int end = c2.index(); if (p->at(qMax(0, end - 1))->isCustom()) ++end; if (!p->mightHaveCustomItems) { s += p->string()->toString().mid(c1.index(), end - c1.index()); } else { for (int i = c1.index(); i < end; ++i) { #ifndef QT_NO_TEXTCUSTOMITEM if (p->at(i)->isCustom()) { if (p->at(i)->customItem()->isNested()) { s += TQLatin1String("\n"); TQTextTable *t = (TQTextTable*)p->at(i)->customItem(); QList cells = t->tableCells(); for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *c = cells.at(idx); s += c->richText()->plainText() + TQLatin1String("\n"); } s += TQLatin1String("\n"); } } else #endif { s += p->at(i)->c; } } } } else { TQTextParagraph *p = c1.paragraph(); int start = c1.index(); while (p) { int end = p == c2.paragraph() ? c2.index() : p->length() - 1; if (p == c2.paragraph() && p->at(qMax(0, end - 1))->isCustom()) ++end; if (!p->mightHaveCustomItems) { s += p->string()->toString().mid(start, end - start); if (p != c2.paragraph()) s += TQLatin1String("\n"); } else { for (int i = start; i < end; ++i) { #ifndef QT_NO_TEXTCUSTOMITEM if (p->at(i)->isCustom()) { if (p->at(i)->customItem()->isNested()) { s += TQLatin1String(TQLatin1String("\n")); TQTextTable *t = (TQTextTable*)p->at(i)->customItem(); QList cells = t->tableCells(); for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *c = cells.at(idx); s += c->richText()->plainText() + TQLatin1String("\n"); } s += TQLatin1String("\n"); } } else #endif { s += p->at(i)->c; } } } start = 0; if (p == c2.paragraph()) break; p = p->next(); } } // ### workaround for plain text export until we get proper // mime types: turn unicode line seperators into the more // widely understood \n. Makes copy and pasting code snipplets // from within Assistent possible TQChar* uc = (TQChar*) s.unicode(); for (int ii = 0; ii < s.length(); ii++) { if (uc[(int)ii] == TQChar::LineSeparator) uc[(int)ii] = TQLatin1Char('\n'); else if ( uc[(int)ii] == TQChar::Nbsp ) uc[(int)ii] = TQLatin1Char(' '); } return s; } void TQTextDocument::setFormat(int id, TQTextFormat *f, int flags) { QMap::ConstIterator it = selections.constFind(id); if (it == selections.constEnd()) return; TQTextDocumentSelection sel = *it; TQTextCursor c1 = sel.startCursor; TQTextCursor c2 = sel.endCursor; if (sel.swapped) { c2 = sel.startCursor; c1 = sel.endCursor; } c2.restoreState(); c1.restoreState(); if (c1.paragraph() == c2.paragraph()) { c1.paragraph()->setFormat(c1.index(), c2.index() - c1.index(), f, true, flags); return; } c1.paragraph()->setFormat(c1.index(), c1.paragraph()->length() - c1.index(), f, true, flags); TQTextParagraph *p = c1.paragraph()->next(); while (p && p != c2.paragraph()) { p->setFormat(0, p->length(), f, true, flags); p = p->next(); } c2.paragraph()->setFormat(0, c2.index(), f, true, flags); } void TQTextDocument::removeSelectedText(int id, TQTextCursor *cursor) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return; TQTextDocumentSelection sel = *it; TQTextCursor c1 = sel.startCursor; TQTextCursor c2 = sel.endCursor; if (sel.swapped) { c2 = sel.startCursor; c1 = sel.endCursor; } // ### no support for editing tables yet if (c1.nestedDepth() || c2.nestedDepth()) return; c2.restoreState(); c1.restoreState(); *cursor = c1; removeSelection(id); if (c1.paragraph() == c2.paragraph()) { c1.paragraph()->remove(c1.index(), c2.index() - c1.index()); return; } if (c1.paragraph() == fParag && c1.index() == 0 && c2.paragraph() == lParag && c2.index() == lParag->length() - 1) cursor->setValid(false); bool didGoLeft = false; if ( c1.index() == 0 && c1.paragraph() != fParag) { cursor->gotoPreviousLetter(); didGoLeft = cursor->isValid(); } c1.paragraph()->remove(c1.index(), c1.paragraph()->length() - 1 - c1.index()); TQTextParagraph *p = c1.paragraph()->next(); int dy = 0; TQTextParagraph *tmp; while (p && p != c2.paragraph()) { tmp = p->next(); dy -= p->rect().height(); delete p; p = tmp; } c2.paragraph()->remove(0, c2.index()); while (p) { p->move(dy); p->invalidate(0); p->setEndState(-1); p = p->next(); } c1.paragraph()->join(c2.paragraph()); if (didGoLeft) cursor->gotoNextLetter(); } void TQTextDocument::indentSelection(int id) { QMap::Iterator it = selections.find(id); if (it == selections.end()) return; TQTextDocumentSelection sel = *it; TQTextParagraph *startParag = sel.startCursor.paragraph(); TQTextParagraph *endParag = sel.endCursor.paragraph(); if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) { endParag = sel.startCursor.paragraph(); startParag = sel.endCursor.paragraph(); } TQTextParagraph *p = startParag; while (p && p != endParag) { p->indent(); p = p->next(); } } void TQTextCommandHistory::clear() { while (!history.isEmpty()) delete history.takeFirst(); current = -1; } void TQTextDocument::addCommand(TQTextCommand *cmd) { commandHistory->addCommand(cmd); } TQTextCursor *TQTextDocument::undo(TQTextCursor *c) { return commandHistory->undo(c); } TQTextCursor *TQTextDocument::redo(TQTextCursor *c) { return commandHistory->redo(c); } bool TQTextDocument::find(TQTextCursor& cursor, const TQString &expr, bool cs, bool wo, bool forward) { Qt::CaseSensitivity caseSensitive = cs ? Qt::CaseSensitive : Qt::CaseInsensitive; removeSelection(Standard); if (expr.isEmpty()) return false; for (;;) { TQString s = cursor.paragraph()->string()->toString(); int start = cursor.index(); for (;;) { int res = forward ? s.indexOf(expr, start, caseSensitive) : s.lastIndexOf(expr, start, caseSensitive); int end = res + expr.length(); if (res == -1 || (!forward && start <= res)) break; if (!wo || ((res == 0 || !s[res-1].isLetterOrNumber()) && (end == (int)s.length() || !s[end].isLetterOrNumber()))) { removeSelection(Standard); cursor.setIndex(forward ? end : res); setSelectionStart(Standard, cursor); cursor.setIndex(forward ? res : end); setSelectionEnd(Standard, cursor); if (!forward) cursor.setIndex(res); return true; } start = res + (forward ? 1 : -1); } if (forward) { if (cursor.paragraph() == lastParagraph() && cursor.atParagEnd()) break; cursor.gotoNextLetter(); } else { if (cursor.paragraph() == firstParagraph() && cursor.atParagStart()) break; cursor.gotoPreviousLetter(); } } return false; } void TQTextDocument::setTextFormat(TQt::TextFormat f) { txtFormat = f; if (fParag == lParag && fParag->length() <= 1) fParag->rtext = (f == TQt::RichText); } TQt::TextFormat TQTextDocument::textFormat() const { return txtFormat; } bool TQTextDocument::inSelection(int selId, const QPoint &pos) const { QMap::ConstIterator it = selections.find(selId); if (it == selections.end()) return false; TQTextDocumentSelection sel = *it; TQTextParagraph *startParag = sel.startCursor.paragraph(); TQTextParagraph *endParag = sel.endCursor.paragraph(); if (sel.startCursor.paragraph() == sel.endCursor.paragraph() && sel.startCursor.paragraph()->selectionStart(selId) == sel.endCursor.paragraph()->selectionEnd(selId)) return false; if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) { endParag = sel.startCursor.paragraph(); startParag = sel.endCursor.paragraph(); } TQTextParagraph *p = startParag; while (p) { if (p->rect().contains(pos)) { bool inSel = false; int selStart = p->selectionStart(selId); int selEnd = p->selectionEnd(selId); int y = 0; int h = 0; for (int i = 0; i < p->length(); ++i) { if (i == selStart) inSel = true; if (i == selEnd) break; if (p->at(i)->lineStart) { y = (*p->lineStarts.find(i))->y; h = (*p->lineStarts.find(i))->h; } if (pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h) { if (inSel && pos.x() >= p->at(i)->x && pos.x() <= p->at(i)->x + p->at(i)->format()->width(p->at(i)->c)) return true; } } } if (pos.y() < p->rect().y()) break; if (p == endParag) break; p = p->next(); } return false; } void TQTextDocument::doLayout(TQPainter *p, int w) { minw = wused = 0; if (!is_printer(p)) p = 0; withoutDoubleBuffer = (p != 0); TQPainter * oldPainter = TQTextFormat::painter(); TQTextFormat::setPainter(p); tStopWidth = formatCollection()->defaultFormat()->width( TQLatin1Char('x') ) * 8; flow_->setWidth(w); cw = w; vw = w; TQTextParagraph *parag = fParag; while (parag) { parag->invalidate(0); if (p) parag->adjustToPainter(p); parag->format(); parag = parag->next(); } TQTextFormat::setPainter(oldPainter); } TQPixmap *TQTextDocument::bufferPixmap(const QSize &s) { if (!buf_pixmap) buf_pixmap = new TQPixmap(s.expandedTo(QSize(1,1))); else if (buf_pixmap->size() != s) buf_pixmap->resize(s.expandedTo(buf_pixmap->size())); return buf_pixmap; } void TQTextDocument::draw(TQPainter *p, const TQRect &rect, const QPalette &pal, const TQBrush *paper) { if (!firstParagraph()) return; if (paper) { p->setBrushOrigin(-qIntCast(p->translationX()), -qIntCast(p->translationY())); p->fillRect(rect, *paper); } TQPainter * oldPainter = TQTextFormat::painter(); TQTextFormat::setPainter(p); if (formatCollection()->defaultFormat()->color() != pal.text().color()) setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color()); TQTextParagraph *parag = firstParagraph(); while (parag) { if (!parag->isValid()) parag->format(); int y = parag->rect().y(); TQRect pr(parag->rect()); pr.setX(0); pr.setWidth(QWIDGETSIZE_MAX); if (!rect.isNull() && !rect.intersects(pr)) { parag = parag->next(); continue; } p->translate(0, y); if (rect.isValid()) parag->paint(*p, pal, 0, false, rect.x(), rect.y(), rect.width(), rect.height()); else parag->paint(*p, pal, 0, false); p->translate(0, -y); parag = parag->next(); if (!flow()->isEmpty()) flow()->drawFloatingItems(p, rect.x(), rect.y(), rect.width(), rect.height(), pal, false); } TQTextFormat::setPainter(oldPainter); } void TQTextDocument::drawParagraph(TQPainter *painter, TQTextParagraph *parag, int cx, int cy, int cw, int ch, TQPixmap *&/*doubleBuffer*/, const QPalette &pal, bool drawCursor, TQTextCursor *cursor, bool resetChanged) { if (resetChanged) parag->setChanged(false); TQRect ir(parag->rect()); #ifndef QT_NO_TEXTCUSTOMITEM if (!parag->tableCell()) #endif ir.setWidth(width()); painter->translate(ir.x(), ir.y()); if (!parag->document()->parent()) { const QPoint oldOrigin = painter->brushOrigin(); painter->setBrushOrigin(-ir.topLeft()); painter->fillRect(TQRect(0, 0, ir.width(), ir.height()), parag->backgroundBrush(pal)); painter->setBrushOrigin(oldOrigin); } painter->translate(-(ir.x() - parag->rect().x()), -(ir.y() - parag->rect().y())); parag->paint(*painter, pal, drawCursor ? cursor : 0, true, cx, cy, cw, ch); painter->translate(-ir.x(), -ir.y()); parag->document()->nextDoubleBuffered = false; } TQTextParagraph *TQTextDocument::draw(TQPainter *p, int cx, int cy, int cw, int ch, const QPalette &pal, bool onlyChanged, bool drawCursor, TQTextCursor *cursor, bool resetChanged) { if (withoutDoubleBuffer || (par && par->withoutDoubleBuffer)) { withoutDoubleBuffer = true; TQRect r; draw(p, r, pal); return 0; } withoutDoubleBuffer = false; if (!firstParagraph()) return 0; TQPainter * oldPainter = TQTextFormat::painter(); TQTextFormat::setPainter(p); if (formatCollection()->defaultFormat()->color() != pal.text().color()) setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color()); if (cx < 0 && cy < 0) { cx = 0; cy = 0; cw = width(); ch = height(); } TQTextParagraph *lastFormatted = 0; TQTextParagraph *parag = firstParagraph(); TQPixmap *doubleBuffer = 0; while (parag) { lastFormatted = parag; if (!parag->isValid()) parag->format(); TQRect pr = parag->rect(); pr.setWidth(parag->document()->width()); if (pr.y() > cy + ch) goto floating; TQRect clipr(cx, cy, cw, ch); if (!pr.intersects(clipr) || (onlyChanged && !parag->hasChanged())) { pr.setWidth(parag->document()->width()); parag = parag->next(); continue; } drawParagraph(p, parag, cx, cy, cw, ch, doubleBuffer, pal, drawCursor, cursor, resetChanged); parag = parag->next(); } parag = lastParagraph(); floating: if (parag->rect().y() + parag->rect().height() < parag->document()->height()) { if (!parag->document()->parent()) { TQRect fillRect = TQRect(0, parag->rect().y() + parag->rect().height(), parag->document()->width(), parag->document()->height() - (parag->rect().y() + parag->rect().height())); if (TQRect(cx, cy, cw, ch).intersects(fillRect)) p->fillRect(fillRect, pal.brush(QPalette::Base)); } if (!flow()->isEmpty()) { TQRect cr(cx, cy, cw, ch); flow()->drawFloatingItems(p, cr.x(), cr.y(), cr.width(), cr.height(), pal, false); } } if (buf_pixmap && buf_pixmap->height() > 300) { delete buf_pixmap; buf_pixmap = 0; } TQTextFormat::setPainter(oldPainter); return lastFormatted; } /* #### this function only sets the default font size in the format collection */ void TQTextDocument::setDefaultFormat(const QFont &font, const QColor &color) { bool reformat = font != fCollection->defaultFormat()->font(); for (int idx = 0; idx < childList.size(); ++idx) { TQTextDocument *dc = childList.at(idx); dc->setDefaultFormat(font, color); } fCollection->updateDefaultFormat(font, color, sheet_); if (!reformat) return; tStopWidth = formatCollection()->defaultFormat()->width(TQLatin1Char('x')) * 8; // invalidate paragraphs and custom items TQTextParagraph *p = fParag; while (p) { p->invalidate(0); #ifndef QT_NO_TEXTCUSTOMITEM for (int i = 0; i < p->length() - 1; ++i) if (p->at(i)->isCustom()) p->at(i)->customItem()->invalidate(); #endif p = p->next(); } } /*! \preliminary Generates an internal object for the tag called \a name, given the attributes \a attr, and using additional information provided by the mime source factory \a factory. \a context is the optional context of the document, i.e. the path to look for relative links. This becomes important if the text contains relative references, for example within image tags. QSimpleRichText always uses the default mime source factory (see \l{TQMimeSourceFactory::defaultFactory()}) to resolve these references. The context will then be used to calculate the absolute path. See TQMimeSourceFactory::makeAbsolute() for details. \a emptyTag and \a doc are for internal use only. This function should not be used in application code. */ #ifndef QT_NO_TEXTCUSTOMITEM TQTextCustomItem* TQTextDocument::tag(TQStyleSheet *sheet, const TQString& name, const QMap &attr, const TQString& context, const TQMimeSourceFactory& factory, bool /*emptyTag */, TQTextDocument *doc) { const TQStyleSheetItem* style = sheet->item(name); // first some known tags if (!style) return 0; if (style->name() == TQLatin1String("img")) return new TQTextImage(doc, attr, context, (TQMimeSourceFactory&)factory); if (style->name() == TQLatin1String("hr")) return new TQTextHorizontalLine(doc, attr, context, (TQMimeSourceFactory&)factory ); return 0; } #endif #ifndef QT_NO_TEXTCUSTOMITEM void TQTextDocument::registerCustomItem(TQTextCustomItem *i, TQTextParagraph *p) { if (i && i->placement() != TQTextCustomItem::PlaceInline) { flow_->registerFloatingItem(i); p->registerFloatingItem(i); i->setParagraph(p); } p->mightHaveCustomItems = mightHaveCustomItems = true; } void TQTextDocument::unregisterCustomItem(TQTextCustomItem *i, TQTextParagraph *p) { p->unregisterFloatingItem(i); i->setParagraph(0); flow_->unregisterFloatingItem(i); } #endif bool TQTextDocument::hasFocusParagraph() const { return !!focusIndicator.parag; } TQString TQTextDocument::focusHref() const { return focusIndicator.href; } TQString TQTextDocument::focusName() const { return focusIndicator.name; } bool TQTextDocument::focusNextPrevChild(bool next) { if (!focusIndicator.parag) { if (next) { focusIndicator.parag = fParag; focusIndicator.start = 0; focusIndicator.len = 0; } else { focusIndicator.parag = lParag; focusIndicator.start = lParag->length(); focusIndicator.len = 0; } } else { focusIndicator.parag->setChanged(true); } focusIndicator.href.clear(); focusIndicator.name.clear(); if (next) { TQTextParagraph *p = focusIndicator.parag; int index = focusIndicator.start + focusIndicator.len; while (p) { for (int i = index; i < p->length(); ++i) { if (p->at(i)->isAnchor()) { p->setChanged(true); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = p->at(i)->anchorHref(); focusIndicator.name = p->at(i)->anchorName(); while (i < p->length()) { if (!p->at(i)->isAnchor()) return true; focusIndicator.len++; i++; } #ifndef QT_NO_TEXTCUSTOMITEM } else if (p->at(i)->isCustom()) { if (p->at(i)->customItem()->isNested()) { TQTextTable *t = (TQTextTable*)p->at(i)->customItem(); QList cells = t->tableCells(); // first try to continue int idx; bool resetCells = true; for (idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *c = cells.at(idx); if (c->richText()->hasFocusParagraph()) { if (c->richText()->focusNextPrevChild(next)) { p->setChanged(true); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return true; } else { resetCells = false; ++idx; break; } } } // now really try if (resetCells) idx = 0; for (; idx < cells.size(); ++idx) { TQTextTableCell *c = cells.at(idx); if (c->richText()->focusNextPrevChild(next)) { p->setChanged(true); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return true; } } } #endif } } index = 0; p = p->next(); } } else { TQTextParagraph *p = focusIndicator.parag; int index = focusIndicator.start - 1; if (focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1) index++; while (p) { for (int i = index; i >= 0; --i) { if (p->at(i)->isAnchor()) { p->setChanged(true); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = p->at(i)->anchorHref(); focusIndicator.name = p->at(i)->anchorName(); while (i >= -1) { if (i < 0 || !p->at(i)->isAnchor()) { focusIndicator.start++; return true; } if (i < 0) break; focusIndicator.len++; focusIndicator.start--; i--; } #ifndef QT_NO_TEXTCUSTOMITEM } else if (p->at(i)->isCustom()) { if (p->at(i)->customItem()->isNested()) { TQTextTable *t = (TQTextTable*)p->at(i)->customItem(); QList cells = t->tableCells(); // first try to continue int idx; bool resetCells = true; for (idx = cells.size()-1; idx >= 0; --idx) { TQTextTableCell *c = cells.at(idx); if (c->richText()->hasFocusParagraph()) { if (c->richText()->focusNextPrevChild(next)) { p->setChanged(true); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return true; } else { resetCells = false; --idx; break; } } } // now really try if (resetCells) idx = cells.size()-1; for (; idx >= 0; --idx) { TQTextTableCell *c = cells.at(idx); if (c->richText()->focusNextPrevChild(next)) { p->setChanged(true); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return true; } } } #endif } } p = p->prev(); if (p) index = p->length() - 1; } } focusIndicator.parag = 0; return false; } int TQTextDocument::length() const { int l = -1; TQTextParagraph *p = fParag; while (p) { l += p->length(); p = p->next(); } return qMax(0,l); } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ int TQTextFormat::width(const TQChar &c) const { if (c.unicode() == 0xad) // soft hyphen return 0; if (!pntr || !pntr->isActive()) { if (c == TQLatin1Char('\t')) return fm.width(TQLatin1Char(' ')); if (ha == AlignNormal) { int w; if (c.row()) w = fm.width(c); else w = widths[c.unicode()]; if (w == 0 && !c.row()) { w = fm.width(c); ((TQTextFormat*)this)->widths[c.unicode()] = w; } return w; } else { QFont f(fn); if (usePixelSizes) f.setPixelSize((f.pixelSize() * 2) / 3); else f.setPointSize((f.pointSize() * 2) / 3); QFontMetrics fm_(f); return fm_.width(c); } } QFont f(fn); if (ha != AlignNormal) { if (usePixelSizes) f.setPixelSize((f.pixelSize() * 2) / 3); else f.setPointSize((f.pointSize() * 2) / 3); } applyFont(f); return pntr_fm->width(c); } int TQTextFormat::width(const TQString &str, int pos) const { int w = 0; if (str.unicode()[pos].unicode() == 0xad) return w; if (!pntr || !pntr->isActive()) { if (ha == AlignNormal) { w = fm.charWidth(str, pos); } else { QFont f(fn); if (usePixelSizes) f.setPixelSize((f.pixelSize() * 2) / 3); else f.setPointSize((f.pointSize() * 2) / 3); QFontMetrics fm_(f); w = fm_.charWidth(str, pos); } } else { QFont f(fn); if (ha != AlignNormal) { if (usePixelSizes) f.setPixelSize((f.pixelSize() * 2) / 3); else f.setPointSize((f.pointSize() * 2) / 3); } applyFont(f); w = pntr_fm->charWidth(str, pos); } return w; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextString::TQTextString() { bidiDirty = true; bidi = false; rightToLeft = false; dir = TQChar::DirON; } TQTextString::TQTextString(const TQTextString &s) { bidiDirty = true; bidi = s.bidi; rightToLeft = s.rightToLeft; dir = s.dir; data = s.data; data.detach(); for (int i = 0; i < (int)data.size(); ++i) { TQTextFormat *f = data[i].format(); if (f) f->addRef(); } } void TQTextString::insert(int index, const QString &s, TQTextFormat *f) { insert(index, s.unicode(), s.length(), f); } void TQTextString::insert(int index, const QChar *unicode, int len, TQTextFormat *f) { int os = data.size(); data.resize(data.size() + len); if (index < os) { memmove(data.data() + index + len, data.data() + index, sizeof(TQTextStringChar) * (os - index)); } TQTextStringChar *ch = data.data() + index; for (int i = 0; i < len; ++i) { ch->x = 0; ch->lineStart = 0; ch->nobreak = false; ch->type = TQTextStringChar::Regular; ch->p.format = f; ch->rightToLeft = 0; ch->c = unicode[i]; ++ch; } bidiDirty = true; } TQTextString::~TQTextString() { clear(); } void TQTextString::insert(int index, TQTextStringChar *c, bool doAddRefFormat ) { int os = data.size(); data.resize(data.size() + 1); if (index < os) { memmove(data.data() + index + 1, data.data() + index, sizeof(TQTextStringChar) * (os - index)); } TQTextStringChar &ch = data[(int)index]; ch.c = c->c; ch.x = 0; ch.lineStart = 0; ch.rightToLeft = 0; ch.p.format = 0; ch.type = TQTextStringChar::Regular; ch.nobreak = false; if (doAddRefFormat && c->format()) c->format()->addRef(); ch.setFormat(c->format()); bidiDirty = true; } int TQTextString::appendParagraphs( TQTextParagraph *start, TQTextParagraph *end ) { int paragCount = 0; int newLength = data.size(); for (TQTextParagraph *p = start; p != end; p = p->next()) { newLength += p->length(); ++paragCount; } const int oldLength = data.size(); data.resize(newLength); TQTextStringChar *d = &data[oldLength]; for (TQTextParagraph *p = start; p != end; p = p->next()) { const TQTextStringChar * const src = p->at(0); int i = 0; for (; i < p->length() - 1; ++i) { d[i].c = src[i].c; d[i].x = 0; d[i].lineStart = 0; d[i].rightToLeft = 0; d[i].type = TQTextStringChar::Regular; d[i].nobreak = false; d[i].p.format = src[i].format(); if (d[i].p.format) d[i].p.format->addRef(); } d[i].x = 0; d[i].lineStart = 0; d[i].nobreak = false; d[i].type = TQTextStringChar::Regular; d[i].p.format = 0; d[i].rightToLeft = 0; d[i].c = TQLatin1Char('\n'); d += p->length(); } bidiDirty = true; return paragCount; } void TQTextString::truncate(int index) { index = qMax(index, 0); index = qMin(index, (int)data.size() - 1); if (index < (int)data.size()) { for (int i = index + 1; i < (int)data.size(); ++i) { TQTextStringChar &ch = data[i]; #ifndef QT_NO_TEXTCUSTOMITEM if (!(ch.type == TQTextStringChar::Regular)) { delete ch.customItem(); if (ch.p.custom->format) ch.p.custom->format->removeRef(); delete ch.p.custom; ch.p.custom = 0; } else #endif if (ch.format()) { ch.format()->removeRef(); } } } data.resize(index); bidiDirty = true; } void TQTextString::remove(int index, int len) { for (int i = index; i < (int)data.size() && i - index < len; ++i) { TQTextStringChar &ch = data[i]; #ifndef QT_NO_TEXTCUSTOMITEM if (!(ch.type == TQTextStringChar::Regular)) { delete ch.customItem(); if (ch.p.custom->format) ch.p.custom->format->removeRef(); delete ch.p.custom; ch.p.custom = 0; } else #endif if (ch.format()) { ch.format()->removeRef(); } } memmove(data.data() + index, data.data() + index + len, sizeof(TQTextStringChar) * (data.size() - index - len)); data.resize(data.size() - len); bidiDirty = true; } void TQTextString::clear() { for (int i = 0; i < (int)data.count(); ++i) { TQTextStringChar &ch = data[i]; #ifndef QT_NO_TEXTCUSTOMITEM if (!(ch.type == TQTextStringChar::Regular)) { if (ch.customItem() && ch.customItem()->placement() == TQTextCustomItem::PlaceInline) delete ch.customItem(); if (ch.p.custom->format) ch.p.custom->format->removeRef(); delete ch.p.custom; ch.p.custom = 0; } else #endif if (ch.format()) { ch.format()->removeRef(); } } data.resize(0); bidiDirty = true; } void TQTextString::setFormat(int index, TQTextFormat *f, bool useCollection) { TQTextStringChar &ch = data[index]; if (useCollection && ch.format()) ch.format()->removeRef(); ch.setFormat(f); } void TQTextString::checkBidi() const { // ############ fix BIDI handling TQTextString *that = (TQTextString *)this; that->bidiDirty = false; int length = data.size(); if (!length) { that->bidi = rightToLeft; that->rightToLeft = (dir == TQChar::DirR); return; } if (dir == TQChar::DirR) { that->rightToLeft = true; } else if (dir == TQChar::DirL) { that->rightToLeft = false; } else { that->rightToLeft = (QApplication::layoutDirection() == Qt::RightToLeft); } const TQTextStringChar *start = data.data(); const TQTextStringChar *end = start + length; ((TQTextString *)this)->stringCache = toString(data); // determines the properties we need for layouting QTextEngine textEngine; textEngine.text = toString(); textEngine.option.setTextDirection(rightToLeft ? Qt::RightToLeft : Qt::LeftToRight); textEngine.itemize(); const HB_CharAttributes *ca = textEngine.attributes() + length-1; TQTextStringChar *ch = (TQTextStringChar *)end - 1; QScriptItem *item = &textEngine.layoutData->items[textEngine.layoutData->items.size()-1]; unsigned char bidiLevel = item->analysis.bidiLevel; that->bidi = (bidiLevel || rightToLeft); int pos = length-1; while (ch >= start) { if (item->position > pos) { --item; Q_ASSERT(item >= &textEngine.layoutData->items[0]); bidiLevel = item->analysis.bidiLevel; if (bidiLevel) that->bidi = true; } ch->softBreak = ca->lineBreakType >= HB_Break; ch->whiteSpace = ca->whiteSpace; ch->charStop = ca->charStop; ch->bidiLevel = bidiLevel; ch->rightToLeft = (bidiLevel%2); --ch; --ca; --pos; } } void TQTextDocument::setStyleSheet(TQStyleSheet *s) { if (!s) return; sheet_ = s; list_tm = list_bm = par_tm = par_bm = 12; list_lm = 40; li_tm = li_bm = 0; TQStyleSheetItem* item = s->item(TQLatin1String("ol")); if (item) { list_tm = qMax(0,item->margin(TQStyleSheetItem::MarginTop)); list_bm = qMax(0,item->margin(TQStyleSheetItem::MarginBottom)); list_lm = qMax(0,item->margin(TQStyleSheetItem::MarginLeft)); } if ((item = s->item(TQLatin1String("li")))) { li_tm = qMax(0,item->margin(TQStyleSheetItem::MarginTop)); li_bm = qMax(0,item->margin(TQStyleSheetItem::MarginBottom)); } if ((item = s->item(TQLatin1String("p")))) { par_tm = qMax(0,item->margin(TQStyleSheetItem::MarginTop)); par_bm = qMax(0,item->margin(TQStyleSheetItem::MarginBottom)); } } void TQTextDocument::setUnderlineLinks(bool b) { underlLinks = b; for (int idx = 0; idx < childList.size(); ++idx) { TQTextDocument *dc = childList.at(idx); dc->setUnderlineLinks(b); } } void TQTextStringChar::setFormat(TQTextFormat *f) { if (type == Regular) { p.format = f; } else { #ifndef QT_NO_TEXTCUSTOMITEM if (!p.custom) { p.custom = new CustomData; p.custom->custom = 0; } p.custom->format = f; #endif } } #ifndef QT_NO_TEXTCUSTOMITEM void TQTextStringChar::setCustomItem(TQTextCustomItem *i) { if (type == Regular) { TQTextFormat *f = format(); p.custom = new CustomData; p.custom->format = f; } else { delete p.custom->custom; } p.custom->custom = i; type = (type == Anchor ? CustomAnchor : Custom); } void TQTextStringChar::loseCustomItem() { if (type == Custom) { TQTextFormat *f = p.custom->format; p.custom->custom = 0; delete p.custom; type = Regular; p.format = f; } else if (type == CustomAnchor) { p.custom->custom = 0; type = Anchor; } } #endif TQString TQTextStringChar::anchorName() const { if (type == Regular) return TQString(); else return p.custom->anchorName; } TQString TQTextStringChar::anchorHref() const { if (type == Regular) return TQString(); else return p.custom->anchorHref; } void TQTextStringChar::setAnchor(const TQString& name, const TQString& href) { if (type == Regular) { TQTextFormat *f = format(); p.custom = new CustomData; #ifndef QT_NO_TEXTCUSTOMITEM p.custom->custom = 0; #endif p.custom->format = f; type = Anchor; } else if (type == Custom) { type = CustomAnchor; } p.custom->anchorName = name; p.custom->anchorHref = href; } int TQTextString::width(int idx) const { int w = 0; TQTextStringChar *c = &at(idx); if (!c->charStop || c->c.unicode() == 0xad || c->c.unicode() == 0x2028) return 0; #ifndef QT_NO_TEXTCUSTOMITEM if(c->isCustom()) { if(c->customItem()->placement() == TQTextCustomItem::PlaceInline) w = c->customItem()->width; } else #endif { int r = c->c.row(); if(r < 0x06 #ifndef Q_WS_WIN // Uniscribe's handling of Asian makes the condition below fail. || (r > 0x1f && !(r > 0xd7 && r < 0xe0)) #endif ) { w = c->format()->width(c->c); } else { // complex text. We need some hacks to get the right metric here w = c->format()->width(toString(), idx); } } return w; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextParagraph::TQTextParagraph(TQTextDocument *dc, TQTextParagraph *pr, TQTextParagraph *nx, bool updateIds) : p(pr), n(nx), docOrPseudo(dc), changed(false), firstFormat(true), firstPProcess(true), needPreProcess(false), fullWidth(true), lastInFrame(false), visible(true), breakable(true), movedDown(false), mightHaveCustomItems(false), hasdoc(dc != 0), litem(false), rtext(false), align(0), lstyle(TQStyleSheetItem::ListDisc), invalid(0), mSelections(0), #ifndef QT_NO_TEXTCUSTOMITEM mFloatingItems(0), #endif utm(0), ubm(0), ulm(0), urm(0), uflm(0), ulinespacing(0), tabStopWidth(0), minwidth(0), tArray(0), eData(0), ldepth(0) { lstyle = TQStyleSheetItem::ListDisc; if (!hasdoc) docOrPseudo = new TQTextParagraphPseudoDocument; bgcol = 0; list_val = -1; paintdevice = 0; TQTextFormat* defFormat = formatCollection()->defaultFormat(); if (!hasdoc) { tabStopWidth = defFormat->width(TQLatin1Char('x')) * 8; pseudoDocument()->commandHistory = new TQTextCommandHistory(100); } if (p) p->n = this; if (n) n->p = this; if (!p && hasdoc) document()->setFirstParagraph(this); if (!n && hasdoc) document()->setLastParagraph(this); state = -1; if (p) id = p->id + 1; else id = 0; if (n && updateIds) { TQTextParagraph *s = n; while (s) { s->id = s->p->id + 1; s->invalidateStyleCache(); s = s->n; } } str = new TQTextString(); const TQChar ch(TQLatin1Char(' ')); str->insert(0, &ch, 1, formatCollection()->defaultFormat()); } TQTextParagraph::~TQTextParagraph() { delete str; if (hasdoc) { register TQTextDocument *doc = document(); if (this == doc->minwParag) { doc->minwParag = 0; doc->minw = 0; } if (this == doc->curParag) doc->curParag = 0; } else { delete pseudoDocument(); } delete [] tArray; delete eData; QMap::Iterator it = lineStarts.begin(); for (; it != lineStarts.end(); ++it) delete *it; if (mSelections) delete mSelections; #ifndef QT_NO_TEXTCUSTOMITEM if (mFloatingItems) delete mFloatingItems; #endif if (p) p->setNext(n); if (n) n->setPrev(p); delete bgcol; } void TQTextParagraph::setNext(TQTextParagraph *s) { n = s; if (!n && hasdoc) document()->setLastParagraph(this); } void TQTextParagraph::setPrev(TQTextParagraph *s) { p = s; if (!p && hasdoc) document()->setFirstParagraph(this); } void TQTextParagraph::invalidate(int chr) { if (invalid < 0) invalid = chr; else invalid = qMin(invalid, chr); #ifndef QT_NO_TEXTCUSTOMITEM if (mFloatingItems) { for (int idx = 0; idx < mFloatingItems->size(); ++idx) { TQTextCustomItem *i = mFloatingItems->at(idx); i->ypos = -1; } } #endif invalidateStyleCache(); } void TQTextParagraph::invalidateStyleCache() { if (list_val < 0) list_val = -1; } void TQTextParagraph::insert(int index, const QString &s) { insert(index, s.unicode(), s.length()); } void TQTextParagraph::insert(int index, const QChar *unicode, int len) { if (hasdoc && !document()->useFormatCollection() && document()->preProcessor()) str->insert(index, unicode, len, document()->preProcessor()->format(TQTextPreProcessor::Standard)); else str->insert(index, unicode, len, formatCollection()->defaultFormat()); invalidate(index); needPreProcess = true; } void TQTextParagraph::truncate(int index) { str->truncate(index); insert(length(), TQLatin1String(" ")); needPreProcess = true; } void TQTextParagraph::remove(int index, int len) { if (index + len - str->length() > 0) return; #ifndef QT_NO_TEXTCUSTOMITEM for (int i = index; i < index + len; ++i) { TQTextStringChar *c = at(i); if (hasdoc && c->isCustom()) { document()->unregisterCustomItem(c->customItem(), this); } } #endif str->remove(index, len); invalidate(0); needPreProcess = true; } void TQTextParagraph::join(TQTextParagraph *s) { int oh = r.height() + s->r.height(); n = s->n; if (n) n->p = this; else if (hasdoc) document()->setLastParagraph(this); int start = str->length(); if (length() > 0 && at(length() - 1)->c == TQLatin1Char(' ')) { remove(length() - 1, 1); --start; } append(s->str->toString(), true); for (int i = 0; i < s->length(); ++i) { if (!hasdoc || document()->useFormatCollection()) { s->str->at(i).format()->addRef(); str->setFormat(i + start, s->str->at(i).format(), true); } #ifndef QT_NO_TEXTCUSTOMITEM if (s->str->at(i).isCustom()) { TQTextCustomItem * item = s->str->at(i).customItem(); str->at(i + start).setCustomItem(item); s->str->at(i).loseCustomItem(); if (hasdoc) { document()->unregisterCustomItem(item, s); document()->registerCustomItem(item, this); } } if (s->str->at(i).isAnchor()) { str->at(i + start).setAnchor(s->str->at(i).anchorName(), s->str->at(i).anchorHref()); } #endif } if (!extraData() && s->extraData()) { setExtraData(s->extraData()); s->setExtraData(0); } else if (extraData() && s->extraData()) { extraData()->join(s->extraData()); } delete s; invalidate(0); r.setHeight(oh); needPreProcess = true; if (n) { TQTextParagraph *s = n; s->invalidate(0); while (s) { s->id = s->p->id + 1; s->state = -1; s->needPreProcess = true; s->changed = true; s->invalidateStyleCache(); s = s->n; } } format(); state = -1; } void TQTextParagraph::move(int &dy) { if (dy == 0) return; changed = true; r.moveBy(0, dy); #ifndef QT_NO_TEXTCUSTOMITEM if (mFloatingItems) { for (int idx = 0; idx < mFloatingItems->size(); ++idx) { TQTextCustomItem *i = mFloatingItems->at(idx); i->ypos += dy; } } #endif if (p) p->lastInFrame = true; // do page breaks if required if (hasdoc && document()->isPageBreakEnabled()) { int shift; if ((shift = document()->formatter()->formatVertically( document(), this))) { if (p) p->setChanged(true); dy += shift; } } } void TQTextParagraph::format(int start, bool doMove) { if (!str || str->length() == 0 || !formatter()) return; if (hasdoc && document()->preProcessor() && (needPreProcess || state == -1)) document()->preProcessor()->process(document(), this, invalid <= 0 ? 0 : invalid); needPreProcess = false; if (invalid == -1) return; r.moveTopLeft(QPoint(documentX(), p ? p->r.y() + p->r.height() : documentY())); if (p) p->lastInFrame = false; movedDown = false; bool formattedAgain = false; formatAgain: r.setWidth(documentWidth()); #ifndef QT_NO_TEXTCUSTOMITEM if (hasdoc && mFloatingItems) { for (int idx = 0; idx < mFloatingItems->size(); ++idx) { TQTextCustomItem *i = mFloatingItems->at(idx); i->ypos = r.y(); if (i->placement() == TQTextCustomItem::PlaceRight) { i->xpos = r.x() + r.width() - i->width; } } } #endif QMap oldLineStarts = lineStarts; lineStarts.clear(); int y = formatter()->format(document(), this, start, oldLineStarts); r.setWidth(qMax(r.width(), formatter()->minimumWidth())); QMap::Iterator it = oldLineStarts.begin(); for (; it != oldLineStarts.end(); ++it) delete *it; if (!hasdoc) { // qt_format_text bounding rect handling it = lineStarts.begin(); int usedw = 0; for (; it != lineStarts.end(); ++it) usedw = qMax(usedw, (*it)->w); if (r.width() <= 0) { // if the user specifies an invalid rect, this means that the // bounding box should grow to the width that the text actually // needs r.setWidth(usedw); } else { r.setWidth(qMin(usedw, r.width())); } } if (y != r.height()) r.setHeight(y); if (!visible) { r.setHeight(0); } else { int minw = minwidth = formatter()->minimumWidth(); int wused = formatter()->widthUsed(); wused = qMax(minw, wused); if (hasdoc) { document()->setMinimumWidth(minw, wused, this); } else { pseudoDocument()->minw = qMax(pseudoDocument()->minw, minw); pseudoDocument()->wused = qMax(pseudoDocument()->wused, wused); } } // do page breaks if required if (hasdoc && document()->isPageBreakEnabled()) { int shift = document()->formatter()->formatVertically(document(), this); if (shift && !formattedAgain) { formattedAgain = true; goto formatAgain; } } if (n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y()) { int dy = (r.y() + r.height()) - n->r.y(); TQTextParagraph *s = n; bool makeInvalid = p && p->lastInFrame; while (s && dy) { if (!s->isFullWidth()) makeInvalid = true; if (makeInvalid) s->invalidate(0); s->move(dy); if (s->lastInFrame) makeInvalid = true; s = s->n; } } firstFormat = false; changed = true; invalid = -1; //##### string()->setTextChanged(false); } int TQTextParagraph::lineHeightOfChar(int i, int *bl, int *y) const { if (!isValid()) ((TQTextParagraph*)this)->format(); QMap::ConstIterator it = lineStarts.end(); --it; for (;;) { if (i >= it.key()) { if (bl) *bl = (*it)->baseLine; if (y) *y = (*it)->y; return (*it)->h; } if (it == lineStarts.begin()) break; --it; } qWarning("TQTextParagraph::lineHeightOfChar: couldn't find lh for %d", i); return 15; } TQTextStringChar *TQTextParagraph::lineStartOfChar(int i, int *index, int *line) const { if (!isValid()) ((TQTextParagraph*)this)->format(); int l = (int)lineStarts.count() - 1; QMap::ConstIterator it = lineStarts.end(); --it; for (;;) { if (i >= it.key()) { if (index) *index = it.key(); if (line) *line = l; return &str->at(it.key()); } if (it == lineStarts.begin()) break; --it; --l; } qWarning("TQTextParagraph::lineStartOfChar: couldn't find %d", i); return 0; } int TQTextParagraph::lines() const { if (!isValid()) ((TQTextParagraph*)this)->format(); return (int)lineStarts.count(); } TQTextStringChar *TQTextParagraph::lineStartOfLine(int line, int *index) const { if (!isValid()) ((TQTextParagraph*)this)->format(); if (line >= 0 && line < (int)lineStarts.count()) { QMap::ConstIterator it = lineStarts.begin(); while (line-- > 0) ++it; int i = it.key(); if (index) *index = i; return &str->at(i); } qWarning("TQTextParagraph::lineStartOfLine: couldn't find %d", line); return 0; } int TQTextParagraph::leftGap() const { if (!isValid()) ((TQTextParagraph*)this)->format(); if (str->length() == 0) return 0; int line = 0; int x = str->length() ? str->at(0).x : 0; /* set x to x of first char */ if (str->isBidi()) { for (int i = 1; i < str->length()-1; ++i) x = qMin(x, str->at(i).x); return x; } QMap::ConstIterator it = lineStarts.begin(); while (line < (int)lineStarts.count()) { int i = it.key(); /* char index */ x = qMin(x, str->at(i).x); ++it; ++line; } return x; } void TQTextParagraph::setFormat(int index, int len, TQTextFormat *f, bool useCollection, int flags) { if (!f) return; if (index < 0) index = 0; if (index > str->length() - 1) index = str->length() - 1; if (index + len >= str->length()) len = str->length() - index; TQTextFormatCollection *fc = 0; if (useCollection) fc = formatCollection(); TQTextFormat *of; for (int i = 0; i < len; ++i) { of = str->at(i + index).format(); if (!changed && (!of || f->key() != of->key())) changed = true; if (invalid == -1 && (f->font().family() != of->font().family() || f->font().pointSize() != of->font().pointSize() || f->font().weight() != of->font().weight() || f->font().italic() != of->font().italic() || f->vAlign() != of->vAlign())) { invalidate(0); } if (flags == -1 || flags == TQTextFormat::Format || !fc) { if (fc) f = fc->format(f); str->setFormat(i + index, f, useCollection); } else { TQTextFormat *fm = fc->format(of, f, flags); str->setFormat(i + index, fm, useCollection); } } } void TQTextParagraph::indent(int *oldIndent, int *newIndent) { if (!hasdoc || !document()->indent() || isListItem()) { if (oldIndent) *oldIndent = 0; if (newIndent) *newIndent = 0; if (oldIndent && newIndent) *newIndent = *oldIndent; return; } document()->indent()->indent(document(), this, oldIndent, newIndent); } void TQTextParagraph::paint(TQPainter &painter, const QPalette &pal, TQTextCursor *cursor, bool drawSelections, int clipx, int clipy, int clipw, int cliph) { if (!visible) return; int i, y, h, baseLine, xstart, xend = 0; i = y =h = baseLine = 0; TQRect cursorRect; drawSelections &= (mSelections != 0); // macintosh full-width selection style bool fullWidthStyle = QApplication::style()->styleHint(QStyle::SH_RichText_FullWidthSelection); int fullSelectionWidth = 0; if (drawSelections && fullWidthStyle) fullSelectionWidth = (hasdoc ? document()->width() : r.width()); TQString qstr = str->toString(); qstr.detach(); // ### workaround so that \n are not drawn, actually this should // be fixed in QFont somewhere (under Windows you get ugly boxes // otherwise) TQChar* uc = (TQChar*) qstr.unicode(); for (int ii = 0; ii < qstr.length(); ii++) if (uc[(int)ii]== TQLatin1Char(TQLatin1Char('\n')) || uc[(int)ii] == TQLatin1Char('\t')) uc[(int)ii] = 0x20; int line = -1; int paintStart = 0; TQTextStringChar *chr = 0; TQTextStringChar *nextchr = at(0); for (i = 0; i < length(); i++) { chr = nextchr; if (i < length()-1) nextchr = at(i+1); // we flush at end of document bool flush = (i == length()-1); bool ignoreSoftHyphen = false; if (!flush) { // we flush at end of line flush |= nextchr->lineStart; // we flush on format changes flush |= (nextchr->format() != chr->format()); // we flush on link changes flush |= (nextchr->isLink() != chr->isLink()); // we flush on start of run flush |= (nextchr->bidiLevel != chr->bidiLevel); // we flush on bidi changes flush |= (nextchr->rightToLeft != chr->rightToLeft); // we flush before and after tabs flush |= (chr->c == TQLatin1Char('\t') || nextchr->c == TQLatin1Char('\t')); // we flush on soft hyphens if (chr->c.unicode() == 0xad) { flush = true; if (!nextchr->lineStart) ignoreSoftHyphen = true; } // we flush on custom items flush |= chr->isCustom(); // we flush before custom items flush |= nextchr->isCustom(); // when painting justified, we flush on spaces if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify) flush |= chr->whiteSpace; } // init a new line if (chr->lineStart) { ++line; paintStart = i; lineInfo(line, y, h, baseLine); if (clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph) { // outside clip area, leave break; } // if this is the first line and we are a list item, draw the the bullet label if (line == 0 && isListItem()) { int x = chr->x; if (str->isBidi()) { if (str->isRightToLeft()) { x = chr->x + str->width(0); for (int k = 1; k < length(); ++k) { if (str->at(k).lineStart) break; x = qMax(x, str->at(k).x + str->width(k)); } } else { x = chr->x; for (int k = 1; k < length(); ++k) { if (str->at(k).lineStart) break; x = qMin(x, str->at(k).x); } } } drawLabel(&painter, x, y, 0, 0, baseLine, pal); } } // check for cursor mark if (cursor && this == cursor->paragraph() && i == cursor->index()) { TQTextStringChar *c = i == 0 ? chr : chr - 1; cursorRect.setRect(cursor->x() , y + baseLine - c->format()->ascent(), 1, c->format()->height()); } if (flush) { // something changed, draw what we have so far if (chr->rightToLeft) { xstart = chr->x; xend = at(paintStart)->x + str->width(paintStart); } else { xstart = at(paintStart)->x; xend = chr->x; if (i < length() - 1) { if (!str->at(i + 1).lineStart && str->at(i + 1).rightToLeft == chr->rightToLeft) xend = str->at(i + 1).x; else xend += str->width(i); } } if ((clipx == -1 || clipw <= 0 || (xend >= clipx && xstart <= clipx + clipw)) && (clipy == -1 || clipy < y+r.y()+h)) { if (!chr->isCustom()) drawString(painter, qstr, paintStart, i - paintStart + (ignoreSoftHyphen ? 0 : 1), xstart, y, baseLine, xend-xstart, h, drawSelections, fullSelectionWidth, chr, pal, chr->rightToLeft); #ifndef QT_NO_TEXTCUSTOMITEM else if (chr->customItem()->placement() == TQTextCustomItem::PlaceInline) { bool inSelection = false; if (drawSelections) { QMap::ConstIterator it = mSelections->constFind(TQTextDocument::Standard); inSelection = (it != mSelections->constEnd() && (*it).start <= i && (*it).end > i); } chr->customItem()->draw(&painter, chr->x, y, clipx == -1 ? clipx : (clipx - r.x()), clipy == -1 ? clipy : (clipy - r.y()), clipw, cliph, pal, inSelection); } #endif } paintStart = i+1; } } // time to draw the cursor const int cursor_extent = 4; if (!cursorRect.isNull() && cursor && ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw))) { painter.fillRect(cursorRect, pal.color(QPalette::Text)); painter.save(); if (string()->isBidi()) { if (at(cursor->index())->rightToLeft) { painter.setPen(Qt::black); painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2); painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2); } else { painter.setPen(Qt::black); painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2); painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2); } } painter.restore(); } } //#define BIDI_DEBUG void TQTextParagraph::setColorForSelection(QColor &color, TQPainter &painter, const QPalette &pal, int selection) { if (selection < 0) return; color = (hasdoc && selection != TQTextDocument::Standard) ? document()->selectionColor(selection) : pal.color(QPalette::Highlight); QColor text = (hasdoc && document()->hasSelectionTextColor(selection)) ? document()->selectionTextColor(selection) : pal.color(QPalette::HighlightedText); if (text.isValid()) painter.setPen(text); } void TQTextParagraph::drawString(TQPainter &painter, const TQString &str, int start, int len, int xstart, int y, int baseLine, int w, int h, bool drawSelections, int fullSelectionWidth, TQTextStringChar *formatChar, const QPalette& pal, bool rightToLeft) { bool plainText = hasdoc ? document()->textFormat() == TQt::PlainText : false; TQTextFormat* format = formatChar->format(); int textFlags = int(rightToLeft ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight); if (!plainText || (hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color())) painter.setPen(QPen(format->color())); else painter.setPen(pal.text().color()); painter.setFont(format->font()); if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty()) { if (format->useLinkColor()) painter.setPen(document()->linkColor.isValid() ? document()->linkColor : pal.link().color()); if (document()->underlineLinks()) { QFont fn = format->font(); fn.setUnderline(true); painter.setFont(fn); } } int real_length = len; if (len && !rightToLeft && start + len == length()) // don't draw the last character (trailing space) len--; if (len && str.unicode()[start+len-1] == TQChar::LineSeparator) len--; TQTextFormat::VerticalAlignment vAlign = format->vAlign(); if (vAlign != TQTextFormat::AlignNormal) { // sub or superscript QFont f(painter.font()); if (format->fontSizesInPixels()) f.setPixelSize((f.pixelSize() * 2) / 3); else f.setPointSize((f.pointSize() * 2) / 3); painter.setFont(f); int h = painter.fontMetrics().height(); baseLine += (vAlign == TQTextFormat::AlignSubScript) ? h/6 : -h/2; } bool allSelected = false; if (drawSelections) { QMap::ConstIterator it = mSelections->constFind(TQTextDocument::Standard); allSelected = (it != mSelections->constEnd() && (*it).start <= start && (*it).end >= start+len); } if (!allSelected) painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0); #ifdef BIDI_DEBUG painter.save(); painter.setPen (Qt::red); painter.drawLine(xstart, y, xstart, y + baseLine); painter.drawLine(xstart, y + baseLine/2, xstart + 10, y + baseLine/2); int w = 0; int i = 0; while(i < len) w += painter.fontMetrics().charWidth(str, start + i++); painter.setPen (Qt::blue); painter.drawLine(xstart + w - 1, y, xstart + w - 1, y + baseLine); painter.drawLine(xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2); painter.restore(); #endif // check if we are in a selection and draw it if (drawSelections) { QMap::ConstIterator it = mSelections->constEnd(); while (it != mSelections->constBegin()) { --it; int selStart = (*it).start; int selEnd = (*it).end; int tmpw = w; selStart = qMax(selStart, start); int real_selEnd = qMin(selEnd, start+real_length); selEnd = qMin(selEnd, start+len); bool extendRight = false; bool extendLeft = false; bool selWrap = (real_selEnd == length()-1 && n && n->hasSelection(it.key())); if (selWrap || ((real_selEnd < this->str->length()) && this->str->at(real_selEnd).lineStart)) { extendRight = (fullSelectionWidth != 0); if (!extendRight && !rightToLeft) tmpw += painter.fontMetrics().width(TQLatin1Char(' ')); } if (fullSelectionWidth && (selStart == 0 || this->str->at(selStart).lineStart)) { extendLeft = true; } if (this->str->isRightToLeft() != rightToLeft) extendLeft = extendRight = false; if (this->str->isRightToLeft()) { bool tmp = extendLeft; extendLeft = extendRight; extendRight = tmp; } if (selStart < real_selEnd || (selWrap && fullSelectionWidth && extendRight && // don't draw the standard selection on a printer= (it.key() != TQTextDocument::Standard || !is_printer(&painter)))) { int selection = it.key(); QColor color; setColorForSelection(color, painter, pal, selection); if (selStart != start || selEnd != start + len || selWrap) { // have to clip painter.save(); int cs, ce; if (rightToLeft) { cs = (selEnd != start + len) ? this->str->at(this->str->previousCursorPosition(selEnd)).x : xstart; ce = (selStart != start) ? this->str->at(this->str->previousCursorPosition(selStart)).x : xstart+tmpw; } else { cs = (selStart != start) ? this->str->at(selStart).x : xstart; ce = (selEnd != start + len) ? this->str->at(selEnd).x : xstart+tmpw; } TQRect r(cs, y, ce-cs, h); if (extendLeft) r.setLeft(0); if (extendRight) r.setRight(fullSelectionWidth); QRegion reg(r); if (painter.hasClipping()) reg &= painter.clipRegion(); painter.setClipRegion(reg); } int xleft = xstart; if (extendLeft) { tmpw += xstart; xleft = 0; } if (extendRight) tmpw = fullSelectionWidth - xleft; if(color.isValid()) painter.fillRect(xleft, y, tmpw, h, color); painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0); if (selStart != start || selEnd != start + len || selWrap) painter.restore(); } } } if (format->isMisspelled()) { painter.save(); painter.setPen(QPen(Qt::red, 1, Qt::DotLine)); painter.drawLine(xstart, y + baseLine + 1, xstart + w, y + baseLine + 1); painter.restore(); } if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() && document()->focusIndicator.parag == this && ((document()->focusIndicator.start >= start && document()->focusIndicator.start + document()->focusIndicator.len <= start + len) || (document()->focusIndicator.start <= start && document()->focusIndicator.start + document()->focusIndicator.len >= start + len))) { QStyleOptionFocusRect opt; opt.rect.setRect(xstart, y, w, h); #ifndef Q_WS_WIN opt.state = QStyle::State_None; #else // force drawing a focus rect but only on windows because it's // configurable by the user in windows settings (see // SH_UnderlineShortcut style hint) and we want to override // this settings. opt.state = QStyle::State_KeyboardFocusChange; #endif opt.palette = pal; QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &painter); } } void TQTextParagraph::drawLabel(TQPainter* p, int x, int y, int w, int h, int base, const QPalette& pal) { TQRect r (x, y, w, h); TQStyleSheetItem::ListStyle s = listStyle(); p->save(); TQTextFormat *format = at(0)->format(); if (format) { p->setPen(format->color()); p->setFont(format->font()); } QFontMetrics fm(p->fontMetrics()); int size = fm.lineSpacing() / 3; bool rtl = str->isRightToLeft(); switch (s) { case TQStyleSheetItem::ListDecimal: case TQStyleSheetItem::ListLowerAlpha: case TQStyleSheetItem::ListUpperAlpha: { if (list_val == -1) { // uninitialised list value, calcluate the right one int depth = listDepth(); list_val--; // ### evil, square and expensive. This needs to be done when formatting, not when painting TQTextParagraph* s = prev(); int depth_s; while (s && (depth_s = s->listDepth()) >= depth) { if (depth_s == depth && s->isListItem()) list_val--; s = s->prev(); } } int n = list_val; if (n < -1) n = -n - 1; TQString l; switch (s) { case TQStyleSheetItem::ListLowerAlpha: if (n < 27) { l = TQLatin1Char(('a' + (char) (n-1))); break; } case TQStyleSheetItem::ListUpperAlpha: if (n < 27) { l = TQLatin1Char(('A' + (char) (n-1))); break; } break; default: //TQStyleSheetItem::ListDecimal: l.setNum(n); break; } if (rtl) l.prepend(TQLatin1String(" .")); else l += TQString::fromLatin1(". "); int x = (rtl ? r.left() : r.right() - fm.width(l)); p->drawText(x, r.top() + base, l); } break; case TQStyleSheetItem::ListSquare: { int x = rtl ? r.left() + size : r.right() - size*2; TQRect er(x, r.top() + fm.height() / 2 - size / 2, size, size); p->fillRect(er , pal.brush(QPalette::Text)); } break; case TQStyleSheetItem::ListCircle: { int x = rtl ? r.left() + size : r.right() - size*2; TQRect er(x, r.top() + fm.height() / 2 - size / 2, size, size); p->drawEllipse(er); } break; case TQStyleSheetItem::ListDisc: default: { p->setBrush(pal.brush(QPalette::Text)); int x = rtl ? r.left() + size : r.right() - size*2; TQRect er(x, r.top() + fm.height() / 2 - size / 2, size, size); p->drawEllipse(er); p->setBrush(Qt::NoBrush); } break; } p->restore(); } #ifndef QT_NO_DATASTREAM void TQTextParagraph::readStyleInformation(QDataStream &stream) { int int_align, int_lstyle; uchar uchar_litem, uchar_rtext, uchar_dir; stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir; align = int_align; lstyle = (TQStyleSheetItem::ListStyle) int_lstyle; litem = uchar_litem; rtext = uchar_rtext; str->setDirection((TQChar::Direction)uchar_dir); TQTextParagraph* s = prev() ? prev() : this; while (s) { s->invalidate(0); s = s->next(); } } void TQTextParagraph::writeStyleInformation(QDataStream& stream) const { stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction(); } #endif void TQTextParagraph::setListItem(bool li) { if ((bool)litem == li) return; litem = li; changed = true; TQTextParagraph* s = prev() ? prev() : this; while (s) { s->invalidate(0); s = s->next(); } } void TQTextParagraph::setListDepth(int depth) { if (!hasdoc || depth == ldepth) return; ldepth = depth; TQTextParagraph* s = prev() ? prev() : this; while (s) { s->invalidate(0); s = s->next(); } } int *TQTextParagraph::tabArray() const { int *ta = tArray; if (!ta && hasdoc) ta = document()->tabArray(); return ta; } int TQTextParagraph::nextTab(int, int x) { int *ta = tArray; if (hasdoc) { if (!ta) ta = document()->tabArray(); tabStopWidth = document()->tabStopWidth(); } if (ta) { int i = 0; while (ta[i]) { if (ta[i] >= x) return tArray[i]; ++i; } return tArray[0]; } else { int n; if (tabStopWidth != 0) n = x / tabStopWidth; else return x; return tabStopWidth * (n + 1); } } void TQTextParagraph::adjustToPainter(TQPainter *p) { #ifndef QT_NO_TEXTCUSTOMITEM for (int i = 0; i < length(); ++i) { if (at(i)->isCustom()) at(i)->customItem()->adjustToPainter(p); } #endif } TQTextFormatCollection *TQTextParagraph::formatCollection() const { if (hasdoc) return document()->formatCollection(); TQTextFormatCollection* fc = &pseudoDocument()->collection; if (paintdevice != fc->paintDevice()) fc->setPaintDevice(paintdevice); return fc; } TQString TQTextParagraph::richText() const { TQString s; TQTextStringChar *formatChar = 0; TQString spaces; bool doStart = richTextExportStart && richTextExportStart->paragraph() == this; bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this; int i; TQString lastAnchorName; for (i = 0; i < length()-1; ++i) { if (doStart && i && richTextExportStart->index() == i) s += TQLatin1String(""); if (doEnd && richTextExportEnd->index() == i) s += TQLatin1String(""); TQTextStringChar *c = &str->at(i); if (c->isAnchor() && !c->anchorName().isEmpty() && c->anchorName() != lastAnchorName) { lastAnchorName = c->anchorName(); if (c->anchorName().contains(TQLatin1Char('#'))) { // TQStringList l = c->anchorName().split(TQLatin1Char('#')); TQStringList l = TQStringList::split(TQLatin1Char('#'), c->anchorName()); for (TQStringList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it) s += TQLatin1String(""); } else { s += TQLatin1String("anchorName() + TQLatin1String("\">"); } } if (!formatChar) { s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(), 0, TQString(), c->anchorHref()); formatChar = c; } else if ((formatChar->format()->key() != c->format()->key()) || (c->anchorHref() != formatChar->anchorHref())) { s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(), formatChar->format() , formatChar->anchorHref(), c->anchorHref()); formatChar = c; } if (c->c == TQLatin1Char('<')) s += TQLatin1String("<"); else if (c->c == TQLatin1Char('>')) s += TQLatin1String(">"); else if (c->c == TQLatin1Char('&')) s += TQLatin1String("&"); else if (c->c == TQLatin1Char('\"')) s += TQLatin1String("""); #ifndef QT_NO_TEXTCUSTOMITEM else if (c->isCustom()) s += c->customItem()->richText(); #endif else if (c->c == TQLatin1Char('\n') || c->c == TQChar::LineSeparator) s += TQLatin1String("
"); // space on purpose for compatibility with Netscape, Lynx & Co. else s += c->c; } if (doEnd && richTextExportEnd->index() == i) s += TQLatin1String(""); if (formatChar) s += formatChar->format()->makeFormatEndTags(formatCollection()->defaultFormat(), formatChar->anchorHref()); return s; } void TQTextParagraph::addCommand(TQTextCommand *cmd) { if (!hasdoc) pseudoDocument()->commandHistory->addCommand(cmd); else document()->commands()->addCommand(cmd); } TQTextCursor *TQTextParagraph::undo(TQTextCursor *c) { if (!hasdoc) return pseudoDocument()->commandHistory->undo(c); return document()->commands()->undo(c); } TQTextCursor *TQTextParagraph::redo(TQTextCursor *c) { if (!hasdoc) return pseudoDocument()->commandHistory->redo(c); return document()->commands()->redo(c); } int TQTextParagraph::topMargin() const { int m = 0; if (rtext) { m = isListItem() ? (document()->li_tm/qMax(1,listDepth()*listDepth())) : (listDepth() ? 0 : document()->par_tm); if (listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth())) m = qMax(m, document()->list_tm); } m += utm; return scale(m, TQTextFormat::painter()); } int TQTextParagraph::bottomMargin() const { int m = 0; if (rtext) { m = isListItem() ? (document()->li_bm/qMax(1,listDepth()*listDepth())) : (listDepth() ? 0 : document()->par_bm); if (listDepth() == 1 &&( !next() || next()->listDepth() < listDepth())) m = qMax(m, document()->list_bm); } m += ubm; return scale(m, TQTextFormat::painter()); } int TQTextParagraph::leftMargin() const { int m = ulm; if (listDepth() && !string()->isRightToLeft()) m += listDepth() * document()->list_lm; return scale(m, TQTextFormat::painter()); } int TQTextParagraph::firstLineMargin() const { int m = uflm; return scale(m, TQTextFormat::painter()); } int TQTextParagraph::rightMargin() const { int m = urm; if (listDepth() && string()->isRightToLeft()) m += listDepth() * document()->list_lm; return scale(m, TQTextFormat::painter()); } int TQTextParagraph::lineSpacing() const { int l = ulinespacing; l = scale(l, TQTextFormat::painter()); return l; } void TQTextParagraph::copyParagData(TQTextParagraph *parag) { rtext = parag->rtext; lstyle = parag->lstyle; ldepth = parag->ldepth; litem = parag->litem; align = parag->align; utm = parag->utm; ubm = parag->ubm; urm = parag->urm; ulm = parag->ulm; uflm = parag->uflm; ulinespacing = parag->ulinespacing; QColor *c = parag->backgroundColor(); if (c) setBackgroundColor(*c); str->setDirection(parag->str->direction()); } void TQTextParagraph::show() { if (visible || !hasdoc) return; visible = true; } void TQTextParagraph::hide() { if (!visible || !hasdoc) return; visible = false; } void TQTextParagraph::setDirection(TQChar::Direction dir) { if (str && str->direction() != dir) { str->setDirection(dir); invalidate(0); } } TQChar::Direction TQTextParagraph::direction() const { return (str ? str->direction() : TQChar::DirON); } void TQTextParagraph::setChanged(bool b, bool recursive) { changed = b; if (recursive) { if (document() && document()->parentParagraph()) document()->parentParagraph()->setChanged(b, recursive); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextPreProcessor::TQTextPreProcessor() { } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatter::TQTextFormatter() : thisminw(0), thiswused(0), wrapEnabled(true), wrapColumn(-1), biw(false) { } QTextLineStart *TQTextFormatter::formatLine(TQTextParagraph *parag, TQTextString *string, QTextLineStart *line, TQTextStringChar *startChar, TQTextStringChar *lastChar, int align, int space) { if (lastChar < startChar) return new QTextLineStart; #ifndef QT_NO_COMPLEXTEXT if(string->isBidi()) return bidiReorderLine(parag, string, line, startChar, lastChar, align, space); #endif int start = (startChar - &string->at(0)); int last = (lastChar - &string->at(0)); // ignore white space at the end of the line. TQTextStringChar *ch = lastChar; while (ch > startChar && ch->whiteSpace) { space += ch->format()->width(TQLatin1Char(' ')); --ch; } if (space < 0) space = 0; // do alignment Auto == Left in this case if (align & Qt::AlignHCenter || align & Qt::AlignRight) { if (align & Qt::AlignHCenter) space /= 2; for (int j = start; j <= last; ++j) string->at(j).x += space; } else if (align & Qt::AlignJustify) { int numSpaces = 0; // End at "last-1", the last space ends up with a width of 0 for (int j = last-1; j >= start; --j) { // Start at last tab, if any. TQTextStringChar &ch = string->at(j); if (ch.c == TQLatin1Char('\t')) { start = j+1; break; } if(ch.whiteSpace) numSpaces++; } int toAdd = 0; for (int k = start + 1; k <= last; ++k) { TQTextStringChar &ch = string->at(k); if(numSpaces && ch.whiteSpace) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } string->at(k).x += toAdd; } } if (last >= 0 && last < string->length()) line->w = string->at(last).x + string->width(last); else line->w = 0; return new QTextLineStart; } #ifndef QT_NO_COMPLEXTEXT #ifdef BIDI_DEBUG QT_BEGIN_INCLUDE_NAMESPACE #include QT_END_INCLUDE_NAMESPACE #endif // collects one line of the paragraph and transforms it to visual order QTextLineStart *TQTextFormatter::bidiReorderLine(TQTextParagraph * /*parag*/, TQTextString *text, QTextLineStart *line, TQTextStringChar *startChar, TQTextStringChar *lastChar, int align, int space) { // ignore white space at the end of the line. int endSpaces = 0; while (lastChar > startChar && lastChar->whiteSpace) { space += lastChar->format()->width(TQLatin1Char(' ')); --lastChar; ++endSpaces; } int start = (startChar - &text->at(0)); int last = (lastChar - &text->at(0)); int length = lastChar - startChar + 1; int x = startChar->x; unsigned char _levels[256]; int _visual[256]; unsigned char *levels = _levels; int *visual = _visual; if (length > 255) { levels = (unsigned char *)malloc(length*sizeof(unsigned char)); visual = (int *)malloc(length*sizeof(int)); } //qDebug("bidiReorderLine: length=%d (%d-%d)", length, start, last); TQTextStringChar *ch = startChar; unsigned char *l = levels; while (ch <= lastChar) { //qDebug(" level: %d", ch->bidiLevel); *(l++) = (ch++)->bidiLevel; } QTextEngine::bidiReorder(length, levels, visual); // now construct the reordered string out of the runs... int numSpaces = 0; align = QStyle::visualAlignment(text->isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight, QFlag(align)); // This is not really correct, but as we can't make the scroll bar move to the left of the origin, // this ensures all text can be scrolled to and read. if (space < 0) space = 0; if (align & Qt::AlignHCenter) x += space/2; else if (align & Qt::AlignRight) x += space; else if (align & Qt::AlignJustify) { // End at "last-1", the last space ends up with a width of 0 for (int j = last-1; j >= start; --j) { // Start at last tab, if any. TQTextStringChar &ch = text->at(j); if (ch.c == TQLatin1Char('\t')) { start = j+1; break; } if(ch.whiteSpace) numSpaces++; } } int toAdd = 0; int xorig = x; TQTextStringChar *lc = startChar + visual[0]; for (int i = 0; i < length; i++) { TQTextStringChar *ch = startChar + visual[i]; if (numSpaces && ch->whiteSpace) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } if (lc->format() != ch->format() && !ch->c.isSpace() && lc->format()->font().italic() && !ch->format()->font().italic()) { int rb = lc->format()->fontMetrics().rightBearing(lc->c); if (rb < 0) x -= rb; } ch->x = x + toAdd; ch->rightToLeft = ch->bidiLevel % 2; //qDebug("visual: %d (%p) placed at %d rightToLeft=%d", visual[i], ch, x +toAdd, ch->rightToLeft ); int ww = 0; if (ch->c.unicode() >= 32 || ch->c == TQLatin1Char(TQLatin1Char('\t')) || ch->c == TQLatin1Char('\n') || ch->isCustom()) { ww = text->width(start+visual[i]); } else { ww = ch->format()->width(TQLatin1Char(' ')); } x += ww; lc = ch; } x += toAdd; while (endSpaces--) { ++lastChar; int sw = lastChar->format()->width(TQLatin1Char(' ')); if (text->isRightToLeft()) { xorig -= sw; lastChar->x = xorig; ch->rightToLeft = true; } else { lastChar->x = x; x += sw; ch->rightToLeft = false; } } line->w = x; if (length > 255) { free(levels); free(visual); } return new QTextLineStart; } #endif void TQTextFormatter::insertLineStart(TQTextParagraph *parag, int index, QTextLineStart *ls) { QMap::Iterator it; if ((it = parag->lineStartList().find(index)) == parag->lineStartList().end()) { parag->lineStartList().insert(index, ls); } else { delete *it; parag->lineStartList().erase(it); parag->lineStartList().insert(index, ls); } } /* Standard pagebreak algorithm using TQTextFlow::adjustFlow. Returns the shift of the paragraphs bottom line. */ int TQTextFormatter::formatVertically(TQTextDocument* doc, TQTextParagraph* parag) { int oldHeight = parag->rect().height(); QMap& lineStarts = parag->lineStartList(); QMap::Iterator it = lineStarts.begin(); int h = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; for (; it != lineStarts.end() ; ++it ) { QTextLineStart * ls = it.value(); ls->y = h; TQTextStringChar *c = ¶g->string()->at(it.key()); #ifndef QT_NO_TEXTCUSTOMITEM if (c && c->customItem() && c->customItem()->ownLine()) { int h = c->customItem()->height; c->customItem()->pageBreak(parag->rect().y() + ls->y + ls->baseLine - h, doc->flow()); int delta = c->customItem()->height - h; ls->h += delta; if (delta) parag->setMovedDown(true); } else #endif { int shift = doc->flow()->adjustFlow(parag->rect().y() + ls->y, ls->w, ls->h); ls->y += shift; if (shift) parag->setMovedDown(true); } h = ls->y + ls->h; } int m = parag->bottomMargin(); if (!parag->next()) m = 0; else m = qMax(m, parag->next()->topMargin()) / 2; h += m; parag->setHeight(h); return h - oldHeight; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatterBreakInWords::TQTextFormatterBreakInWords() { } #define SPACE(s) s int TQTextFormatterBreakInWords::format(TQTextDocument *doc,TQTextParagraph *parag, int start, const QMap &) { // make sure bidi information is correct. (void)parag->string()->isBidi(); TQTextStringChar *c = 0; TQTextStringChar *firstChar = 0; int left = doc ? parag->leftMargin() + doc->leftMargin() : 0; int x = left + (doc ? parag->firstLineMargin() : 0); int dw = parag->documentVisibleWidth() - (doc ? doc->rightMargin() : 0); int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; int h = y; int len = parag->length(); if (doc) x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 4); int rm = parag->rightMargin(); int w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0); bool fullWidth = true; int minw = 0; int wused = 0; bool wrapEnabled = isWrapEnabled(parag); start = 0; //######### what is the point with start?! (Matthias) if (start == 0) c = ¶g->string()->at(0); int i = start; QTextLineStart *lineStart = new QTextLineStart(y, y, 0); insertLineStart(parag, 0, lineStart); TQPainter *painter = TQTextFormat::painter(); int col = 0; int ww = 0; TQChar lastChr; int tabBase = left < x ? left : x; for (; i < len; ++i, ++col) { if (c) lastChr = c->c; c = ¶g->string()->at(i); // ### the lines below should not be needed if (painter) c->format()->setPainter(painter); if (i > 0) { c->lineStart = 0; } else { c->lineStart = 1; firstChar = c; } if (c->c.unicode() >= 32 || c->isCustom()) { ww = parag->string()->width(i); } else if (c->c == TQLatin1Char('\t')) { int nx = parag->nextTab(i, x - tabBase) + tabBase; if (nx < x) ww = w - x; else ww = nx - x; } else { ww = c->format()->width(TQLatin1Char(' ')); } #ifndef QT_NO_TEXTCUSTOMITEM if (c->isCustom() && c->customItem()->ownLine()) { x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left; w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0); c->customItem()->resize(w - x); w = dw; y += h; h = c->height(); lineStart = new QTextLineStart(y, h, h); insertLineStart(parag, i, lineStart); c->lineStart = 1; firstChar = c; x = 0xffffff; continue; } #endif if (wrapEnabled && ((wrapAtColumn() == -1 && x + ww > w) || (wrapAtColumn() != -1 && col >= wrapAtColumn()))) { x = doc ? parag->document()->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left; w = dw; y += h; h = c->height(); lineStart = formatLine(parag, parag->string(), lineStart, firstChar, c-1); lineStart->y = y; insertLineStart(parag, i, lineStart); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; col = 0; if (wrapAtColumn() != -1) minw = qMax(minw, w); } else if (lineStart) { lineStart->baseLine = qMax(lineStart->baseLine, c->ascent()); h = qMax(h, c->height()); lineStart->h = h; } c->x = x; x += ww; wused = qMax(wused, x); } int m = parag->bottomMargin(); if (!parag->next()) m = 0; else m = qMax(m, parag->next()->topMargin()) / 2; parag->setFullWidth(fullWidth); y += h + m; if (doc) minw += doc->rightMargin(); if (!wrapEnabled) minw = qMax(minw, wused); thisminw = minw; thiswused = wused; return y; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatterBreakWords::TQTextFormatterBreakWords() { } #define DO_FLOW(lineStart) do{ if (doc && doc->isPageBreakEnabled()) { \ int yflow = lineStart->y + parag->rect().y();\ int shift = doc->flow()->adjustFlow(yflow, dw, lineStart->h); \ lineStart->y += shift;\ y += shift;\ }}while(false) int TQTextFormatterBreakWords::format(TQTextDocument *doc, TQTextParagraph *parag, int start, const QMap &) { // make sure bidi information is correct. (void)parag->string()->isBidi(); TQTextStringChar *c = 0; TQTextStringChar *firstChar = 0; TQTextString *string = parag->string(); int left = doc ? parag->leftMargin() + doc->leftMargin() : 0; int x = left + (doc ? parag->firstLineMargin() : 0); int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; int h = y; int len = parag->length(); if (doc) x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 0); int dw = parag->documentVisibleWidth() - (doc ? (left != x ? 0 : doc->rightMargin()) : 0); int curLeft = x; int rm = parag->rightMargin(); int rdiff = doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 0) : 0; int w = dw - rdiff; bool fullWidth = true; int marg = left + rdiff; int minw = 0; int wused = 0; int tminw = marg; int linespacing = doc ? parag->lineSpacing() : 0; bool wrapEnabled = isWrapEnabled(parag); start = 0; int i = start; QTextLineStart *lineStart = new QTextLineStart(y, y, 0); insertLineStart(parag, 0, lineStart); int lastBreak = -1; int tmpBaseLine = 0, tmph = 0; bool lastWasNonInlineCustom = false; int align = parag->alignment(); if (align == TQt::AlignAuto && doc && doc->alignment() != TQt::AlignAuto) align = doc->alignment(); align &= Qt::AlignHorizontal_Mask; // ### hack. The last char in the paragraph is always invisible, // ### and somehow sometimes has a wrong format. It changes // ### between // layouting and printing. This corrects some // ### layouting errors in BiDi mode due to this. if (len > 1) { c = ¶g->string()->at(len - 1); if (!c->isAnchor()) { if (c->format()) c->format()->removeRef(); c->setFormat(string->at(len - 2).format()); if (c->format()) c->format()->addRef(); } } c = ¶g->string()->at(0); TQPainter *painter = TQTextFormat::painter(); int col = 0; int ww = 0; TQChar lastChr = c->c; TQTextFormat *lastFormat = c->format(); int tabBase = left < x ? left : x; for (; i < len; ++i, ++col) { if (i) { c = ¶g->string()->at(i-1); lastChr = c->c; lastFormat = c->format(); } bool lastWasOwnLineCustomItem = lastBreak == -2; bool hadBreakableChar = lastBreak != -1; bool lastWasHardBreak = lastChr == TQChar::LineSeparator; // ### next line should not be needed if (painter) c->format()->setPainter(painter); c = &string->at(i); if (lastFormat != c->format() && !c->c.isSpace() && lastFormat->font().italic() && !c->format()->font().italic()) { int rb = lastFormat->fontMetrics().rightBearing(lastChr); if (rb < 0) x -= rb; } if ((i > 0 && (x > curLeft || ww == 0)) || lastWasNonInlineCustom) { c->lineStart = 0; } else { c->lineStart = 1; firstChar = c; } // ignore non spacing marks for column count. if (col != 0 && TQChar::category(c->c.unicode()) == TQChar::Mark_NonSpacing) --col; #ifndef QT_NO_TEXTCUSTOMITEM lastWasNonInlineCustom = (c->isCustom() && c->customItem()->placement() != TQTextCustomItem::PlaceInline); #endif if (c->c.unicode() >= 32 || c->isCustom()) { ww = string->width(i); } else if (c->c == TQLatin1Char('\t')) { if (align == Qt::AlignRight || align == Qt::AlignCenter) { // we can not (yet) do tabs ww = c->format()->width(TQLatin1Char(' ')); } else { int tabx = lastWasHardBreak ? (left + (doc ? parag->firstLineMargin() : 0)) : x; int nx = parag->nextTab(i, tabx - tabBase) + tabBase; if (nx < tabx) // strrrange... ww = 0; else ww = nx - tabx; } } else { ww = c->format()->width(TQLatin1Char(' ')); } #ifndef QT_NO_TEXTCUSTOMITEM TQTextCustomItem* ci = c->customItem(); if (c->isCustom() && ci->ownLine()) { QTextLineStart *lineStart2 = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x - ww)); x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left; w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0); ci->resize(w - x); if (ci->width < w - x) { if (align & Qt::AlignHCenter) x = (w - ci->width) / 2; else if (align & Qt::AlignRight) { x = w - ci->width; } } c->x = x; curLeft = x; if (i == 0 || !isBreakable(string, i-1) || string->at(i - 1).lineStart == 0) { y += qMax(h, qMax(tmph, linespacing)); tmph = c->height(); h = tmph; lineStart = lineStart2; lineStart->y = y; insertLineStart(parag, i, lineStart); c->lineStart = 1; firstChar = c; } else { tmph = c->height(); h = tmph; delete lineStart2; } lineStart->h = h; lineStart->baseLine = h; tmpBaseLine = lineStart->baseLine; lastBreak = -2; x = w; minw = qMax(minw, tminw); int tw = ci->minimumWidth() + (doc ? doc->leftMargin() : 0); if (tw < QWIDGETSIZE_MAX) tminw = tw; else tminw = marg; wused = qMax(wused, ci->width); continue; } else if (c->isCustom() && ci->placement() != TQTextCustomItem::PlaceInline) { int tw = ci->minimumWidth(); if (tw < QWIDGETSIZE_MAX) minw = qMax(minw, tw); } #endif // we break if // 1. the last character was a hard break (TQChar::LineSeparator) or // 2. the last character was a own-line custom item (eg. table or ruler) or // 3. wrapping was enabled, it was not a space and following // condition is true: We either had a breakable character // previously or we ar allowed to break in words and - either // we break at w pixels and the current char would exceed that // or - we break at a column and the current character would // exceed that. if (lastWasHardBreak || lastWasOwnLineCustomItem || (wrapEnabled && ((!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) && ((wrapAtColumn() == -1 && x + ww > w) || (wrapAtColumn() != -1 && col >= wrapAtColumn())))) ) ) { if (wrapAtColumn() != -1) minw = qMax(minw, x + ww); // if a break was forced (no breakable char, hard break or own line custom item), break immediately.... if (!hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem) { if (lineStart) { lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine); h = qMax(h, tmph); lineStart->h = h; DO_FLOW(lineStart); } lineStart = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x)); x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left; w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0); if (!doc && c->c == TQLatin1Char('\t')) { // qt_format_text tab handling int nx = parag->nextTab(i, x - tabBase) + tabBase; if (nx < x) ww = w - x; else ww = nx - x; } curLeft = x; y += qMax(h, linespacing); tmph = c->height(); h = 0; lineStart->y = y; insertLineStart(parag, i, lineStart); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; tmpBaseLine = lineStart->baseLine; lastBreak = -1; col = 0; if (allowBreakInWords() || lastWasHardBreak) { minw = qMax(minw, tminw); tminw = marg + ww; } } else { // ... otherwise if we had a breakable char, break there DO_FLOW(lineStart); c->x = x; i = lastBreak; lineStart = formatLine(parag, string, lineStart, firstChar, parag->at(lastBreak),align, SPACE(w - string->at(i+1).x)); x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left; w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0); if (!doc && c->c == TQLatin1Char('\t')) { // qt_format_text tab handling int nx = parag->nextTab(i, x - tabBase) + tabBase; if (nx < x) ww = w - x; else ww = nx - x; } curLeft = x; y += qMax(h, linespacing); tmph = c->height(); h = tmph; lineStart->y = y; insertLineStart(parag, i + 1, lineStart); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; tmpBaseLine = lineStart->baseLine; lastBreak = -1; col = 0; minw = qMax(minw, tminw); tminw = marg; continue; } } else if (lineStart && isBreakable(string, i)) { if (len <= 2 || i < len - 1) { tmpBaseLine = qMax(tmpBaseLine, c->ascent()); tmph = qMax(tmph, c->height()); } minw = qMax(minw, tminw); tminw = marg + ww; lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine); h = qMax(h, tmph); lineStart->h = h; if (i < len - 2 || c->c != TQLatin1Char(' ')) lastBreak = i; } else { tminw += ww; int cascent = c->ascent(); int cheight = c->height(); int belowBaseLine = qMax(tmph - tmpBaseLine, cheight-cascent); tmpBaseLine = qMax(tmpBaseLine, cascent); tmph = tmpBaseLine + belowBaseLine; } c->x = x; x += ww; wused = qMax(wused, x); } if (lineStart) { lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine); h = qMax(h, tmph); lineStart->h = h; // last line in a paragraph is not justified if (align & Qt::AlignJustify) { align |= Qt::AlignLeft; align &= ~(Qt::AlignJustify|Qt::AlignAbsolute); } DO_FLOW(lineStart); lineStart = formatLine(parag, string, lineStart, firstChar, c, align, SPACE(w - x)); delete lineStart; } minw = qMax(minw, tminw); if (doc) minw += doc->rightMargin(); int m = parag->bottomMargin(); if (!parag->next()) m = 0; else m = qMax(m, parag->next()->topMargin()) / 2; parag->setFullWidth(fullWidth); y += qMax(h, linespacing) + m; wused += rm; if (!wrapEnabled || wrapAtColumn() != -1) minw = qMax(minw, wused); // This is the case where we are breaking wherever we darn well please // in cases like that, the minw should not be the length of the entire // word, because we necessarily want to show the word on the whole line. // example: word wrap in iconview if (allowBreakInWords() && minw > wused) minw = wused; thisminw = minw; thiswused = wused; return y; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextIndent::TQTextIndent() { } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatCollection::TQTextFormatCollection() : paintdevice(0) { defFormat = new TQTextFormat(QApplication::font(), QApplication::palette().color(QPalette::Active, QPalette::Text)); lastFormat = cres = 0; cflags = -1; cachedFormat = 0; } TQTextFormatCollection::~TQTextFormatCollection() { QHash::ConstIterator it = cKey.constBegin(); while (it != cKey.constEnd()) { delete it.value(); ++it; } delete defFormat; } void TQTextFormatCollection::setPaintDevice(QPaintDevice *pd) { paintdevice = TQT_TQPAINTDEVICE(pd); #if defined(Q_WS_X11) int scr = (paintdevice) ? paintdevice->x11Screen() : QX11Info::appScreen(); defFormat->fn.tqt_x11SetScreen(scr); defFormat->update(); QHash::Iterator it = cKey.begin(); for (; it != cKey.end(); ++it) { TQTextFormat *format = *it; format->fn.tqt_x11SetScreen(scr); format->update(); } #endif // Q_WS_X11 } TQTextFormat *TQTextFormatCollection::format(TQTextFormat *f) { if (f->parent() == this || f == defFormat) { lastFormat = f; lastFormat->addRef(); return lastFormat; } if (f == lastFormat || (lastFormat && f->key() == lastFormat->key())) { lastFormat->addRef(); return lastFormat; } TQTextFormat *fm = cKey.value(f->key()); if (fm) { lastFormat = fm; lastFormat->addRef(); return lastFormat; } if (f->key() == defFormat->key()) return defFormat; lastFormat = createFormat(*f); lastFormat->collection = this; cKey.insert(lastFormat->key(), lastFormat); return lastFormat; } TQTextFormat *TQTextFormatCollection::format(TQTextFormat *of, TQTextFormat *nf, int flags) { if (cres && kof == of->key() && knf == nf->key() && cflags == flags) { cres->addRef(); return cres; } cres = createFormat(*of); kof = of->key(); knf = nf->key(); cflags = flags; if (flags & TQTextFormat::Bold) cres->fn.setBold(nf->fn.bold()); if (flags & TQTextFormat::Italic) cres->fn.setItalic(nf->fn.italic()); if (flags & TQTextFormat::Underline) cres->fn.setUnderline(nf->fn.underline()); if (flags & TQTextFormat::StrikeOut) cres->fn.setStrikeOut(nf->fn.strikeOut()); if (flags & TQTextFormat::Family) cres->fn.setFamily(nf->fn.family()); if (flags & TQTextFormat::Size) { if (of->usePixelSizes) cres->fn.setPixelSize(nf->fn.pixelSize()); else cres->fn.setPointSize(nf->fn.pointSize()); } if (flags & TQTextFormat::Color) cres->col = nf->col; if (flags & TQTextFormat::Misspelled) cres->missp = nf->missp; if (flags & TQTextFormat::VAlign) cres->ha = nf->ha; cres->update(); TQTextFormat *fm = cKey.value(cres->key()); if (!fm) { cres->collection = this; cKey.insert(cres->key(), cres); } else { delete cres; cres = fm; cres->addRef(); } return cres; } TQTextFormat *TQTextFormatCollection::format(const QFont &f, const QColor &c) { if (cachedFormat && cfont == f && ccol == c) { cachedFormat->addRef(); return cachedFormat; } TQString key = TQTextFormat::getKey(f, c, false, TQTextFormat::AlignNormal); cachedFormat = cKey.value(key); cfont = f; ccol = c; if (cachedFormat) { cachedFormat->addRef(); return cachedFormat; } if (key == defFormat->key()) return defFormat; cachedFormat = createFormat(f, c); cachedFormat->collection = this; cKey.insert(cachedFormat->key(), cachedFormat); if (cachedFormat->key() != key) qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1()); return cachedFormat; } void TQTextFormatCollection::remove(TQTextFormat *f) { if (lastFormat == f) lastFormat = 0; if (cres == f) cres = 0; if (cachedFormat == f) cachedFormat = 0; if (cKey.value(f->key()) == f) delete cKey.take(f->key()); } #define UPDATE(up, lo, rest) \ if (font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest()) \ fm->fn.set##up##rest(font.lo##rest()) void TQTextFormatCollection::updateDefaultFormat(const QFont &font, const QColor &color, TQStyleSheet *sheet) { bool usePixels = font.pointSize() == -1; bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() : font.pointSize() != defFormat->fn.pointSize(); int base = usePixels ? font.pixelSize() : font.pointSize(); QHash::Iterator it = cKey.begin(); for (; it != cKey.end(); ++it) { TQTextFormat *fm = *it; UPDATE(F, f, amily); UPDATE(W, w, eight); UPDATE(B, b, old); UPDATE(I, i, talic); UPDATE(U, u, nderline); if (changeSize) { fm->stdSize = base; fm->usePixelSizes = usePixels; if (usePixels) fm->fn.setPixelSize(fm->stdSize); else fm->fn.setPointSize(fm->stdSize); sheet->scaleFont(fm->fn, fm->logicalFontSize); } if (color.isValid() && color != defFormat->col && fm->col == defFormat->col) fm->col = color; fm->update(); } defFormat->fn = font; defFormat->col = color; defFormat->update(); defFormat->stdSize = base; defFormat->usePixelSizes = usePixels; updateKeys(); } // the keys in cKey have changed, rebuild the hashtable void TQTextFormatCollection::updateKeys() { if (cKey.isEmpty()) return; TQTextFormat** formats = new TQTextFormat *[cKey.count() + 1]; TQTextFormat **f = formats; for (QHash::Iterator it = cKey.begin(); it != cKey.end(); ++it, ++f) *f = *it; *f = 0; cKey.clear(); for (f = formats; *f; f++) cKey.insert((*f)->key(), *f); delete [] formats; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void TQTextFormat::setBold(bool b) { if (b == fn.bold()) return; fn.setBold(b); update(); } void TQTextFormat::setMisspelled(bool b) { if (b == (bool)missp) return; missp = b; update(); } void TQTextFormat::setVAlign(VerticalAlignment a) { if (a == ha) return; ha = a; update(); } void TQTextFormat::setItalic(bool b) { if (b == fn.italic()) return; fn.setItalic(b); update(); } void TQTextFormat::setUnderline(bool b) { if (b == fn.underline()) return; fn.setUnderline(b); update(); } void TQTextFormat::setStrikeOut(bool b) { if (b == fn.strikeOut()) return; fn.setStrikeOut(b); update(); } void TQTextFormat::setFamily(const TQString &f) { if (f == fn.family()) return; fn.setFamily(f); update(); } void TQTextFormat::setPointSize(int s) { if (s == fn.pointSize()) return; fn.setPointSize(s); usePixelSizes = false; update(); } void TQTextFormat::setFont(const QFont &f) { if (f == fn && !k.isEmpty()) return; fn = f; update(); } void TQTextFormat::setColor(const QColor &c) { if (c == col) return; col = c; update(); } TQString TQTextFormat::makeFormatChangeTags(TQTextFormat* defaultFormat, TQTextFormat *f, const TQString& oldAnchorHref, const TQString& anchorHref ) const { TQString tag; if (f) tag += f->makeFormatEndTags(defaultFormat, oldAnchorHref); if (!anchorHref.isEmpty()) tag += TQLatin1String(""); if (font() != defaultFormat->font() || vAlign() != defaultFormat->vAlign() || color().rgb() != defaultFormat->color().rgb()) { TQString s; if (font().family() != defaultFormat->font().family()) s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("font-family:") + fn.family(); if (font().italic() && font().italic() != defaultFormat->font().italic()) s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("font-style:") + (font().italic() ? TQLatin1String("italic") : TQLatin1String("normal")); if (font().pointSize() != defaultFormat->font().pointSize()) s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("font-size:") + TQString::number(fn.pointSize()) + TQLatin1String("pt"); if (font().weight() != defaultFormat->font().weight()) s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("font-weight:") + TQString::number(fn.weight() * 8); TQString textDecoration; bool none = false; if ( font().underline() != defaultFormat->font().underline() ) { if (font().underline()) textDecoration = TQLatin1String("underline"); else none = true; } if ( font().overline() != defaultFormat->font().overline() ) { if (font().overline()) textDecoration += TQLatin1String(" overline"); else none = true; } if ( font().strikeOut() != defaultFormat->font().strikeOut() ) { if (font().strikeOut()) textDecoration += TQLatin1String(" line-through"); else none = true; } if (none && textDecoration.isEmpty()) textDecoration = TQLatin1String("none"); if (!textDecoration.isEmpty()) s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("text-decoration:") + textDecoration; if (vAlign() != defaultFormat->vAlign()) { s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("vertical-align:"); if (vAlign() == TQTextFormat::AlignSuperScript) s += TQLatin1String("super"); else if (vAlign() == TQTextFormat::AlignSubScript) s += TQLatin1String("sub"); else s += TQLatin1String("normal"); } if (color().rgb() != defaultFormat->color().rgb()) s += TQString(s.size()?TQLatin1String(";"):TQLatin1String("")) + TQLatin1String("color:") + col.name(); if (!s.isEmpty()) tag += TQLatin1String(""); } return tag; } TQString TQTextFormat::makeFormatEndTags(TQTextFormat* defaultFormat, const TQString& anchorHref) const { TQString tag; if (font().family() != defaultFormat->font().family() || font().pointSize() != defaultFormat->font().pointSize() || font().weight() != defaultFormat->font().weight() || font().italic() != defaultFormat->font().italic() || font().underline() != defaultFormat->font().underline() || font().strikeOut() != defaultFormat->font().strikeOut() || vAlign() != defaultFormat->vAlign() || color().rgb() != defaultFormat->color().rgb()) tag += TQLatin1String(""); if (!anchorHref.isEmpty()) tag += TQLatin1String(""); return tag; } TQTextFormat TQTextFormat::makeTextFormat(const TQStyleSheetItem *style, const QMap& attr, double scaleFontsFactor) const { TQTextFormat format(*this); if (!style) return format; if (!style->isAnchor() && style->color().isValid()) { // the style is not an anchor and defines a color. // It might be used inside an anchor and it should // override the link color. format.linkColor = false; } switch (style->verticalAlignment()) { case TQStyleSheetItem::VAlignBaseline: format.setVAlign(TQTextFormat::AlignNormal); break; case TQStyleSheetItem::VAlignSuper: format.setVAlign(TQTextFormat::AlignSuperScript); break; case TQStyleSheetItem::VAlignSub: format.setVAlign(TQTextFormat::AlignSubScript); break; } if (style->fontWeight() != TQStyleSheetItem::Undefined) format.fn.setWeight(style->fontWeight()); if (style->fontSize() != TQStyleSheetItem::Undefined) { format.fn.setPointSize(style->fontSize()); } else if (style->logicalFontSize() != TQStyleSheetItem::Undefined) { format.logicalFontSize = style->logicalFontSize(); if (format.usePixelSizes) format.fn.setPixelSize(format.stdSize); else format.fn.setPointSize(format.stdSize); style->styleSheet()->scaleFont(format.fn, format.logicalFontSize); } else if (style->logicalFontSizeStep()) { format.logicalFontSize += style->logicalFontSizeStep(); if (format.usePixelSizes) format.fn.setPixelSize(format.stdSize); else format.fn.setPointSize(format.stdSize); style->styleSheet()->scaleFont(format.fn, format.logicalFontSize); } if (!style->fontFamily().isEmpty()) format.fn.setFamily(style->fontFamily()); if (style->color().isValid()) format.col = style->color(); if (style->definesFontItalic()) format.fn.setItalic(style->fontItalic()); if (style->definesFontUnderline()) format.fn.setUnderline(style->fontUnderline()); if (style->definesFontStrikeOut()) format.fn.setStrikeOut(style->fontStrikeOut()); QMap::ConstIterator it, end = attr.end(); if (style->name() == TQLatin1String("font")) { it = attr.find(TQLatin1String("color")); if (it != end && ! (*it).isEmpty()){ format.col.setNamedColor(*it); format.linkColor = false; } it = attr.find(TQLatin1String("face")); if (it != end) { TQString family = (*it).section(TQLatin1Char(','), 0, 0); if (family.size()) format.fn.setFamily(family); } it = attr.find(TQLatin1String("size")); if (it != end) { TQString a = *it; int n = a.toInt(); if (a[0] == TQLatin1Char('+') || a[0] == TQLatin1Char('-')) n += 3; format.logicalFontSize = n; if (format.usePixelSizes) format.fn.setPixelSize(format.stdSize); else format.fn.setPointSize(format.stdSize); style->styleSheet()->scaleFont(format.fn, format.logicalFontSize); } } it = attr.find(TQLatin1String("style")); if (it != end) { TQString a = *it; int count = a.count(TQLatin1Char(';'))+1; for (int s = 0; s < count; s++) { TQString style = a.section(TQLatin1Char(';'), s, s); if (style.startsWith(TQLatin1String("font-size:")) && style.endsWith(TQLatin1String("pt"))) { format.logicalFontSize = 0; int size = int(scaleFontsFactor * style.mid(10, style.length() - 12).toDouble()); format.setPointSize(size); } else if (style.startsWith(TQLatin1String("font-style:"))) { TQString s = style.mid(11).trimmed(); if (s == TQLatin1String("normal")) format.fn.setItalic(false); else if (s == TQLatin1String("italic") || s == TQLatin1String("oblique")) format.fn.setItalic(true); } else if (style.startsWith(TQLatin1String("font-weight:"))) { TQString s = style.mid(12); bool ok = true; int n = s.toInt(&ok); if (ok) format.fn.setWeight(n/8); } else if (style.startsWith(TQLatin1String("font-family:"))) { TQString family = style.mid(12).section(TQLatin1Char(','),0,0); family.replace(TQLatin1Char('\"'), TQLatin1Char(' ')); family.replace(TQLatin1Char('\''), TQLatin1Char(' ')); family = family.trimmed(); format.fn.setFamily(family); } else if (style.startsWith(TQLatin1String("text-decoration:"))) { TQString s = style.mid( 16 ); format.fn.setOverline(s.contains(TQLatin1String("overline"))); format.fn.setStrikeOut(s.contains(TQLatin1String("line-through"))); format.fn.setUnderline(s.contains(TQLatin1String("underline"))); } else if (style.startsWith(TQLatin1String("vertical-align:"))) { TQString s = style.mid(15).trimmed(); if (s == TQLatin1String("sub")) format.setVAlign(TQTextFormat::AlignSubScript); else if (s == TQLatin1String("super")) format.setVAlign(TQTextFormat::AlignSuperScript); else format.setVAlign(TQTextFormat::AlignNormal); } else if (style.startsWith(TQLatin1String("color:"))) { format.col.setNamedColor(style.mid(6)); format.linkColor = false; } } } format.update(); return format; } #ifndef QT_NO_TEXTCUSTOMITEM struct TQPixmapInt { TQPixmapInt() : ref(0) {} TQPixmap pm; int ref; Q_DUMMY_COMPARISON_OPERATOR(TQPixmapInt) }; static QMap *pixmap_map = 0; TQTextImage::TQTextImage(TQTextDocument *p, const QMap &attr, const TQString& context, TQMimeSourceFactory &factory) : TQTextCustomItem(p) { width = height = 0; QMap::ConstIterator it, end = attr.end(); it = attr.find(TQLatin1String("width")); if (it != end) width = (*it).toInt(); it = attr.find(TQLatin1String("height")); if (it != end) height = (*it).toInt(); reg = 0; TQString imageName = attr[TQLatin1String("src")]; if (imageName.size() == 0) imageName = attr[TQLatin1String("source")]; if (!imageName.isEmpty()) { imgId = TQString::fromLatin1("%1,%2,%3,%4").arg(imageName).arg(width).arg(height).arg((quintptr)&factory); if (!pixmap_map) pixmap_map = new QMap; if (pixmap_map->contains(imgId)) { TQPixmapInt& pmi = pixmap_map->operator[](imgId); pm = pmi.pm; pmi.ref++; width = pm.width(); height = pm.height(); } else { TQImage img; const QMimeSource* m = factory.data(imageName, context); if (!m) { qCritical("TQTextImage: no mimesource for %s", imageName.latin1()); } else { if (!TQImageDrag::decode(TQT_TQMIMESOURCE_CONST(m), img)) { qCritical("TQTextImage: cannot decode %s", imageName.latin1()); } } if (!img.isNull()) { if (width == 0) { width = img.width(); if (height != 0) { width = img.width() * height / img.height(); } } if (height == 0) { height = img.height(); if (width != img.width()) { height = img.height() * width / img.width(); } } if (img.width() != width || img.height() != height){ #ifndef QT_NO_IMAGE_SMOOTHSCALE img = img.smoothScale(width, height); #endif width = img.width(); height = img.height(); } pm.convertFromImage(img); } if (!pm.isNull()) { TQPixmapInt& pmi = pixmap_map->operator[](imgId); pmi.pm = pm; pmi.ref++; } } if (pm.hasAlphaChannel()) { QRegion mask(pm.mask()); QRegion all(0, 0, pm.width(), pm.height()); reg = new QRegion(all.subtracted(mask)); } } if (pm.isNull() && (width*height)==0) width = height = 50; place = PlaceInline; if (attr[TQLatin1String("align")] == TQLatin1String("left")) place = PlaceLeft; else if (attr[TQLatin1String("align")] == TQLatin1String("right")) place = PlaceRight; tmpwidth = width; tmpheight = height; attributes = attr; } TQTextImage::~TQTextImage() { if (pixmap_map && pixmap_map->contains(imgId)) { TQPixmapInt& pmi = pixmap_map->operator[](imgId); pmi.ref--; if (!pmi.ref) { pixmap_map->remove(imgId); if (pixmap_map->isEmpty()) { delete pixmap_map; pixmap_map = 0; } } } delete reg; } TQString TQTextImage::richText() const { TQString s; s += TQLatin1String("::ConstIterator it = attributes.begin(); for (; it != attributes.end(); ++it) { s += it.key() + TQLatin1Char('='); if ((*it).contains(TQLatin1Char(' '))) s += TQLatin1Char('\"') + *it + TQLatin1String("\" "); else s += *it + TQLatin1Char(' '); } s += TQLatin1Char('>'); return s; } void TQTextImage::adjustToPainter(TQPainter* p) { width = scale(tmpwidth, p); height = scale(tmpheight, p); } #if !defined(Q_WS_X11) static TQPixmap *qrt_selection = 0; static TQSingleCleanupHandler qrt_cleanup_pixmap; static void qrt_createSelectionPixmap(const QPalette &pal) { qrt_selection = new TQPixmap(2, 2); qrt_cleanup_pixmap.set(&qrt_selection); qrt_selection->fill(Qt::color0); QBitmap m(2, 2); m.fill(Qt::color1); TQPainter p(&m); p.setPen(Qt::color0); for (int j = 0; j < 2; ++j) { p.drawPoint(j % 2, j); } p.end(); qrt_selection->setMask(m); qrt_selection->fill(pal.highlight().color()); } #endif void TQTextImage::draw(TQPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QPalette &pal, bool selected) { if (placement() != PlaceInline) { x = xpos; y = ypos; } if (pm.isNull()) { p->fillRect(x , y, width, height, pal.dark()); return; } if (is_printer(p)) { p->drawPixmap(TQRect(x, y, width, height), pm); return; } if (placement() != PlaceInline && !TQRect(xpos, ypos, width, height).intersects(TQRect(cx, cy, cw, ch))) return; if (placement() == PlaceInline) p->drawPixmap(x , y, pm); else p->drawPixmap(cx , cy, pm, cx - x, cy - y, cw, ch); if (selected && placement() == PlaceInline && is_printer(p)) { #if defined(Q_WS_X11) p->fillRect(TQRect(QPoint(x, y), pm.size()), TQBrush(pal.Highlight, Qt::Dense4Pattern)); #else // in WIN32 Qt::Dense4Pattern doesn't work correctly (transparency problem), so work around it if (!qrt_selection) qrt_createSelectionPixmap(pal); p->drawTiledPixmap(x, y, pm.width(), pm.height(), *qrt_selection); #endif } } void TQTextHorizontalLine::adjustToPainter(TQPainter* p) { height = scale(tmpheight, p); } TQTextHorizontalLine::TQTextHorizontalLine(TQTextDocument *p, const QMap &attr, const TQString &, TQMimeSourceFactory &) : TQTextCustomItem(p) { height = tmpheight = 8; QMap::ConstIterator it, end = attr.end(); it = attr.find(TQLatin1String("color")); if (it != end) color = QColor(*it); shade = attr.find(TQLatin1String("noshade")) == end; } TQTextHorizontalLine::~TQTextHorizontalLine() { } TQString TQTextHorizontalLine::richText() const { return TQLatin1String("
"); } void TQTextHorizontalLine::draw(TQPainter* p, int x, int y, int , int , int , int , const QPalette& pal, bool selected) { TQRect r(x, y, width, height); if (is_printer(p) || !shade) { QPen oldPen = p->pen(); if (!color.isValid()) p->setPen(QPen(pal.text().color(), is_printer(p) ? height/8 : qMax(2, height/4))); else p->setPen(QPen(color, is_printer(p) ? height/8 : qMax(2, height/4))); p->drawLine(r.left()-1, y + height / 2, r.right() + 1, y + height / 2); p->setPen(oldPen); } else { if (selected) p->fillRect(r, pal.highlight()); QPalette pal2(pal); if (color.isValid()) pal2.setColor(pal2.currentColorGroup(), QPalette::Dark, color); qDrawShadeLine(p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, pal2, true, height / 8); } } #endif //QT_NO_TEXTCUSTOMITEM /*****************************************************************/ // Small set of utility functions to make the parser a bit simpler // bool TQTextDocument::hasPrefix(const TQChar* doc, int length, int pos, TQChar c) { if (pos + 1 > length) return false; return doc[pos].toLower() == c.toLower(); } bool TQTextDocument::hasPrefix(const TQChar* doc, int length, int pos, const TQString& s) { if (pos + (int) s.length() > length) return false; for (int i = 0; i < (int)s.length(); i++) { if (doc[pos + i].toLower() != s[i].toLower()) return false; } return true; } #ifndef QT_NO_TEXTCUSTOMITEM static bool qt_is_cell_in_use(QList& cells, int row, int col) { for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *c = cells.at(idx); if (row >= c->row() && row < c->row() + c->rowspan() && col >= c->column() && col < c->column() + c->colspan()) return true; } return false; } TQTextCustomItem* TQTextDocument::parseTable(const QMap &attr, const TQTextFormat &fmt, const TQChar* doc, int length, int& pos, TQTextParagraph *curpar) { TQTextTable* table = new TQTextTable(this, attr); int row = -1; int col = -1; TQString rowbgcolor; TQString rowalign; TQString tablebgcolor = attr[TQLatin1String("bgcolor")]; QList multicells; TQString tagname; (void) eatSpace(doc, length, pos); while (pos < length) { if (hasPrefix(doc, length, pos, TQLatin1Char('<'))){ if (hasPrefix(doc, length, pos+1, TQLatin1Char('/'))) { tagname = parseCloseTag(doc, length, pos); if (tagname == TQLatin1String("table")) { return table; } } else { QMap attr2; bool emptyTag = false; tagname = parseOpenTag(doc, length, pos, attr2, emptyTag); if (tagname == TQLatin1String("tr")) { rowbgcolor = attr2[TQLatin1String("bgcolor")]; rowalign = attr2[TQLatin1String("align")]; row++; col = -1; } else if (tagname == TQLatin1String("td") || tagname == TQLatin1String("th")) { col++; while (qt_is_cell_in_use(multicells, row, col)) { col++; } if (row >= 0 && col >= 0) { const TQStyleSheetItem* s = sheet_->item(tagname); if (!attr2.contains(TQLatin1String("bgcolor"))) { if (!rowbgcolor.isEmpty()) attr2[TQLatin1String("bgcolor")] = rowbgcolor; else if (!tablebgcolor.isEmpty()) attr2[TQLatin1String("bgcolor")] = tablebgcolor; } if (!attr2.contains(TQLatin1String("align"))) { if (!rowalign.isEmpty()) attr2[TQLatin1String("align")] = rowalign; } // extract the cell contents int end = pos; while (end < length && !hasPrefix(doc, length, end, TQLatin1String("richText()->parentPar = curpar; if (cell->colspan() > 1 || cell->rowspan() > 1) multicells.append(cell); col += cell->colspan()-1; pos = end; } } } } else { ++pos; } } return table; } #endif // QT_NO_TEXTCUSTOMITEM bool TQTextDocument::eatSpace(const TQChar* doc, int length, int& pos, bool includeNbsp) { int old_pos = pos; while (pos < length && doc[pos].isSpace() && (includeNbsp || (doc[pos] != TQChar(TQChar::nbsp)))) pos++; return old_pos < pos; } bool TQTextDocument::eat(const TQChar* doc, int length, int& pos, TQChar c) { bool ok = pos < length && doc[pos] == c; if (ok) pos++; return ok; } /*****************************************************************/ struct Entity { const char * name; Q_UINT16 code; }; static const Entity entitylist [] = { { "AElig", 0x00c6 }, { "Aacute", 0x00c1 }, { "Acirc", 0x00c2 }, { "Agrave", 0x00c0 }, { "Alpha", 0x0391 }, { "AMP", 38 }, { "Aring", 0x00c5 }, { "Atilde", 0x00c3 }, { "Auml", 0x00c4 }, { "Beta", 0x0392 }, { "Ccedil", 0x00c7 }, { "Chi", 0x03a7 }, { "Dagger", 0x2021 }, { "Delta", 0x0394 }, { "ETH", 0x00d0 }, { "Eacute", 0x00c9 }, { "Ecirc", 0x00ca }, { "Egrave", 0x00c8 }, { "Epsilon", 0x0395 }, { "Eta", 0x0397 }, { "Euml", 0x00cb }, { "Gamma", 0x0393 }, { "GT", 62 }, { "Iacute", 0x00cd }, { "Icirc", 0x00ce }, { "Igrave", 0x00cc }, { "Iota", 0x0399 }, { "Iuml", 0x00cf }, { "Kappa", 0x039a }, { "Lambda", 0x039b }, { "LT", 60 }, { "Mu", 0x039c }, { "Ntilde", 0x00d1 }, { "Nu", 0x039d }, { "OElig", 0x0152 }, { "Oacute", 0x00d3 }, { "Ocirc", 0x00d4 }, { "Ograve", 0x00d2 }, { "Omega", 0x03a9 }, { "Omicron", 0x039f }, { "Oslash", 0x00d8 }, { "Otilde", 0x00d5 }, { "Ouml", 0x00d6 }, { "Phi", 0x03a6 }, { "Pi", 0x03a0 }, { "Prime", 0x2033 }, { "Psi", 0x03a8 }, { "QUOT", 34 }, { "Rho", 0x03a1 }, { "Scaron", 0x0160 }, { "Sigma", 0x03a3 }, { "THORN", 0x00de }, { "Tau", 0x03a4 }, { "Theta", 0x0398 }, { "Uacute", 0x00da }, { "Ucirc", 0x00db }, { "Ugrave", 0x00d9 }, { "Upsilon", 0x03a5 }, { "Uuml", 0x00dc }, { "Xi", 0x039e }, { "Yacute", 0x00dd }, { "Yuml", 0x0178 }, { "Zeta", 0x0396 }, { "aacute", 0x00e1 }, { "acirc", 0x00e2 }, { "acute", 0x00b4 }, { "aelig", 0x00e6 }, { "agrave", 0x00e0 }, { "alefsym", 0x2135 }, { "alpha", 0x03b1 }, { "amp", 38 }, { "and", 0x22a5 }, { "ang", 0x2220 }, { "apos", 0x0027 }, { "aring", 0x00e5 }, { "asymp", 0x2248 }, { "atilde", 0x00e3 }, { "auml", 0x00e4 }, { "bdquo", 0x201e }, { "beta", 0x03b2 }, { "brvbar", 0x00a6 }, { "bull", 0x2022 }, { "cap", 0x2229 }, { "ccedil", 0x00e7 }, { "cedil", 0x00b8 }, { "cent", 0x00a2 }, { "chi", 0x03c7 }, { "circ", 0x02c6 }, { "clubs", 0x2663 }, { "cong", 0x2245 }, { "copy", 0x00a9 }, { "crarr", 0x21b5 }, { "cup", 0x222a }, { "cur" "ren", 0x00a4 }, { "dArr", 0x21d3 }, { "dagger", 0x2020 }, { "darr", 0x2193 }, { "deg", 0x00b0 }, { "delta", 0x03b4 }, { "diams", 0x2666 }, { "divide", 0x00f7 }, { "eacute", 0x00e9 }, { "ecirc", 0x00ea }, { "egrave", 0x00e8 }, { "empty", 0x2205 }, { "emsp", 0x2003 }, { "ensp", 0x2002 }, { "epsilon", 0x03b5 }, { "equiv", 0x2261 }, { "eta", 0x03b7 }, { "eth", 0x00f0 }, { "euml", 0x00eb }, { "euro", 0x20ac }, { "exist", 0x2203 }, { "fnof", 0x0192 }, { "forall", 0x2200 }, { "frac12", 0x00bd }, { "frac14", 0x00bc }, { "frac34", 0x00be }, { "frasl", 0x2044 }, { "gamma", 0x03b3 }, { "ge", 0x2265 }, { "gt", 62 }, { "hArr", 0x21d4 }, { "harr", 0x2194 }, { "hearts", 0x2665 }, { "hellip", 0x2026 }, { "iacute", 0x00ed }, { "icirc", 0x00ee }, { "iexcl", 0x00a1 }, { "igrave", 0x00ec }, { "image", 0x2111 }, { "infin", 0x221e }, { "int", 0x222b }, { "iota", 0x03b9 }, { "iquest", 0x00bf }, { "isin", 0x2208 }, { "iuml", 0x00ef }, { "kappa", 0x03ba }, { "lArr", 0x21d0 }, { "lambda", 0x03bb }, { "lang", 0x2329 }, { "laquo", 0x00ab }, { "larr", 0x2190 }, { "lceil", 0x2308 }, { "ldquo", 0x201c }, { "le", 0x2264 }, { "lfloor", 0x230a }, { "lowast", 0x2217 }, { "loz", 0x25ca }, { "lrm", 0x200e }, { "lsaquo", 0x2039 }, { "lsquo", 0x2018 }, { "lt", 60 }, { "macr", 0x00af }, { "mdash", 0x2014 }, { "micro", 0x00b5 }, { "middot", 0x00b7 }, { "minus", 0x2212 }, { "mu", 0x03bc }, { "nabla", 0x2207 }, { "nbsp", 0x00a0 }, { "ndash", 0x2013 }, { "ne", 0x2260 }, { "ni", 0x220b }, { "not", 0x00ac }, { "notin", 0x2209 }, { "nsub", 0x2284 }, { "ntilde", 0x00f1 }, { "nu", 0x03bd }, { "oacute", 0x00f3 }, { "ocirc", 0x00f4 }, { "oelig", 0x0153 }, { "ograve", 0x00f2 }, { "oline", 0x203e }, { "omega", 0x03c9 }, { "omicron", 0x03bf }, { "oplus", 0x2295 }, { "or", 0x22a6 }, { "ordf", 0x00aa }, { "ordm", 0x00ba }, { "oslash", 0x00f8 }, { "otilde", 0x00f5 }, { "otimes", 0x2297 }, { "ouml", 0x00f6 }, { "para", 0x00b6 }, { "part", 0x2202 }, { "percnt", 0x0025 }, { "permil", 0x2030 }, { "perp", 0x22a5 }, { "phi", 0x03c6 }, { "pi", 0x03c0 }, { "piv", 0x03d6 }, { "plusmn", 0x00b1 }, { "pound", 0x00a3 }, { "prime", 0x2032 }, { "prod", 0x220f }, { "prop", 0x221d }, { "psi", 0x03c8 }, { "quot", 34 }, { "rArr", 0x21d2 }, { "radic", 0x221a }, { "rang", 0x232a }, { "raquo", 0x00bb }, { "rarr", 0x2192 }, { "rceil", 0x2309 }, { "rdquo", 0x201d }, { "real", 0x211c }, { "reg", 0x00ae }, { "rfloor", 0x230b }, { "rho", 0x03c1 }, { "rlm", 0x200f }, { "rsaquo", 0x203a }, { "rsquo", 0x2019 }, { "sbquo", 0x201a }, { "scaron", 0x0161 }, { "sdot", 0x22c5 }, { "sect", 0x00a7 }, { "shy", 0x00ad }, { "sigma", 0x03c3 }, { "sigmaf", 0x03c2 }, { "sim", 0x223c }, { "spades", 0x2660 }, { "sub", 0x2282 }, { "sube", 0x2286 }, { "sum", 0x2211 }, { "sup1", 0x00b9 }, { "sup2", 0x00b2 }, { "sup3", 0x00b3 }, { "sup", 0x2283 }, { "supe", 0x2287 }, { "szlig", 0x00df }, { "tau", 0x03c4 }, { "there4", 0x2234 }, { "theta", 0x03b8 }, { "thetasym", 0x03d1 }, { "thinsp", 0x2009 }, { "thorn", 0x00fe }, { "tilde", 0x02dc }, { "times", 0x00d7 }, { "trade", 0x2122 }, { "uArr", 0x21d1 }, { "uacute", 0x00fa }, { "uarr", 0x2191 }, { "ucirc", 0x00fb }, { "ugrave", 0x00f9 }, { "uml", 0x00a8 }, { "upsih", 0x03d2 }, { "upsilon", 0x03c5 }, { "uuml", 0x00fc }, { "weierp", 0x2118 }, { "xi", 0x03be }, { "yacute", 0x00fd }, { "yen", 0x00a5 }, { "yuml", 0x00ff }, { "zeta", 0x03b6 }, { "zwj", 0x200d }, { "zwnj", 0x200c }, { "", 0x0000 } }; static QMap *html_map = 0; static void qt_cleanup_html_map() { delete html_map; html_map = 0; } static QMap *htmlMap() { if (!html_map) { html_map = new QMap; qAddPostRoutine(qt_cleanup_html_map); const Entity *ent = entitylist; while(ent->code) { html_map->insert(QByteArray(ent->name), TQChar(ent->code)); ent++; } } return html_map; } TQChar TQTextDocument::parseHTMLSpecialChar(const TQChar* doc, int length, int& pos) { TQString s; pos++; int recoverpos = pos; while (pos < length && doc[pos] != TQLatin1Char(';') && !doc[pos].isSpace() && pos < recoverpos + 8) { s += doc[pos]; pos++; } if (doc[pos] != TQLatin1Char(';') && !doc[pos].isSpace()) { pos = recoverpos; return TQLatin1Char('&'); } pos++; if (s.length() > 1 && s[0] == TQLatin1Char('#')) { int off = 1; int base = 10; if (s[1] == TQLatin1Char('x')) { off = 2; base = 16; } bool ok; int num = s.mid(off).toInt(&ok, base); if (num == 151) // ### hack for designer manual return TQLatin1Char('-'); return num; } QMap::Iterator it = htmlMap()->find(s.toLatin1()); if (it != htmlMap()->end()) { return *it; } pos = recoverpos; return TQLatin1Char('&'); } TQString TQTextDocument::parseWord(const TQChar* doc, int length, int& pos, bool lower) { TQString s; if (doc[pos] == TQLatin1Char('"')) { pos++; while (pos < length && doc[pos] != TQLatin1Char('"')) { if (doc[pos] == TQLatin1Char('&')) { s += parseHTMLSpecialChar(doc, length, pos); } else { s += doc[pos]; pos++; } } eat(doc, length, pos, TQLatin1Char('"')); } else if (doc[pos] == TQLatin1Char('\'')) { pos++; while (pos < length && doc[pos] != TQLatin1Char('\'')) { s += doc[pos]; pos++; } eat(doc, length, pos, TQLatin1Char('\'')); } else { static TQString term = TQString::fromLatin1("/>"); while (pos < length && doc[pos] != TQLatin1Char('>') && !hasPrefix(doc, length, pos, term) && doc[pos] != TQLatin1Char('<') && doc[pos] != TQLatin1Char('=') && !doc[pos].isSpace()) { if (doc[pos] == TQLatin1Char('&')) { s += parseHTMLSpecialChar(doc, length, pos); } else { s += doc[pos]; pos++; } } if (lower) s = s.toLower(); } return s; } TQChar TQTextDocument::parseChar(const TQChar* doc, int length, int& pos, TQStyleSheetItem::WhiteSpaceMode wsm) { if (pos >= length) return TQChar::null; TQChar c = doc[pos++]; if (c == TQLatin1Char('<')) return TQChar::null; if (c.isSpace() && c != TQChar(TQChar::nbsp)) { if (wsm == TQStyleSheetItem::WhiteSpacePre) { if (c == TQLatin1Char('\n')) return TQChar::LineSeparator; else return c; } else { // non-pre mode: collapse whitespace except nbsp while (pos< length && doc[pos].isSpace() && doc[pos] != TQChar(TQChar::nbsp)) pos++; return TQLatin1Char(' '); } } else if (c == TQLatin1Char('&')) return parseHTMLSpecialChar(doc, length, --pos); else return c; } TQString TQTextDocument::parseOpenTag(const TQChar* doc, int length, int& pos, QMap &attr, bool& emptyTag) { emptyTag = false; pos++; if (hasPrefix(doc, length, pos, TQLatin1Char('!'))) { if (hasPrefix(doc, length, pos+1, TQLatin1String("--"))) { pos += 3; // eat comments TQString pref = TQString::fromLatin1("-->"); while (!hasPrefix(doc, length, pos, pref) && pos < length) pos++; if (hasPrefix(doc, length, pos, pref)) { pos += 3; eatSpace(doc, length, pos, true); } emptyTag = true; return TQString(); } else { // eat strange internal tags while (!hasPrefix(doc, length, pos, TQLatin1Char('>')) && pos < length) pos++; if (hasPrefix(doc, length, pos, TQLatin1Char('>'))) { pos++; eatSpace(doc, length, pos, true); } return TQString(); } } TQString tag = parseWord(doc, length, pos); eatSpace(doc, length, pos, true); static TQString term = TQString::fromLatin1("/>"); static TQString s_TRUE = TQString::fromLatin1("TRUE"); while (doc[pos] != TQLatin1Char('>') && ! (emptyTag = hasPrefix(doc, length, pos, term))) { TQString key = parseWord(doc, length, pos); eatSpace(doc, length, pos, true); if (key.isEmpty()) { // error recovery while (pos < length && doc[pos] != TQLatin1Char('>')) pos++; break; } TQString value; if (hasPrefix(doc, length, pos, TQLatin1Char('='))){ pos++; eatSpace(doc, length, pos); value = parseWord(doc, length, pos, false); } else value = s_TRUE; attr.insert(key.toLower(), value); eatSpace(doc, length, pos, true); } if (emptyTag) { eat(doc, length, pos, TQLatin1Char('/')); eat(doc, length, pos, TQLatin1Char('>')); } else eat(doc, length, pos, TQLatin1Char('>')); return tag; } TQString TQTextDocument::parseCloseTag(const TQChar* doc, int length, int& pos) { pos++; pos++; TQString tag = parseWord(doc, length, pos); eatSpace(doc, length, pos, true); eat(doc, length, pos, TQLatin1Char('>')); return tag; } TQTextFlow::TQTextFlow() { w = pagesize = 0; } TQTextFlow::~TQTextFlow() { clear(); } void TQTextFlow::clear() { #ifndef QT_NO_TEXTCUSTOMITEM while (!leftItems.isEmpty()) delete leftItems.takeFirst(); while (!rightItems.isEmpty()) delete rightItems.takeFirst(); #endif } void TQTextFlow::setWidth(int width) { w = width; } int TQTextFlow::adjustLMargin(int yp, int, int margin, int space) { #ifndef QT_NO_TEXTCUSTOMITEM for (int idx = 0; idx < leftItems.size(); ++idx) { TQTextCustomItem* item = leftItems.at(idx); if (item->ypos == -1) continue; if (yp >= item->ypos && yp < item->ypos + item->height) margin = qMax(margin, item->xpos + item->width + space); } #endif return margin; } int TQTextFlow::adjustRMargin(int yp, int, int margin, int space) { #ifndef QT_NO_TEXTCUSTOMITEM for (int idx = 0; idx < rightItems.size(); ++idx) { TQTextCustomItem* item = rightItems.at(idx); if (item->ypos == -1) continue; if (yp >= item->ypos && yp < item->ypos + item->height) margin = qMax(margin, w - item->xpos - space); } #endif return margin; } int TQTextFlow::adjustFlow(int y, int /*w*/, int h) { if (pagesize > 0) { // check pages int yinpage = y % pagesize; if (yinpage <= border_tolerance) return border_tolerance - yinpage; else if (yinpage + h > pagesize - border_tolerance) return (pagesize - yinpage) + border_tolerance; } return 0; } #ifndef QT_NO_TEXTCUSTOMITEM void TQTextFlow::unregisterFloatingItem(TQTextCustomItem* item) { leftItems.removeAll(item); rightItems.removeAll(item); } void TQTextFlow::registerFloatingItem(TQTextCustomItem* item) { if (item->placement() == TQTextCustomItem::PlaceRight) { if (!rightItems.contains(item)) rightItems.append(item); } else if (item->placement() == TQTextCustomItem::PlaceLeft && !leftItems.contains(item)) { leftItems.append(item); } } #endif // QT_NO_TEXTCUSTOMITEM TQRect TQTextFlow::boundingRect() const { TQRect br; #ifndef QT_NO_TEXTCUSTOMITEM for (int idx = 0; idx < leftItems.size(); ++idx) { TQTextCustomItem* item = leftItems.at(idx); br = br.united(item->geometry()); } for (int idx = 0; idx < rightItems.size(); ++idx) { TQTextCustomItem* item = rightItems.at(idx); br = br.united(item->geometry()); } #endif return br; } void TQTextFlow::drawFloatingItems(TQPainter* p, int cx, int cy, int cw, int ch, const QPalette &pal, bool selected) { #ifndef QT_NO_TEXTCUSTOMITEM for (int idx = 0; idx < leftItems.size(); ++idx) { TQTextCustomItem* item = leftItems.at(idx); if (item->xpos == -1 || item->ypos == -1) continue; item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected); } for (int idx = 0; idx < rightItems.size(); ++idx) { TQTextCustomItem* item = rightItems.at(idx); if (item->xpos == -1 || item->ypos == -1) continue; item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected); } #endif } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #ifndef QT_NO_TEXTCUSTOMITEM void TQTextCustomItem::pageBreak(int /*y*/ , TQTextFlow* /*flow*/) { } #endif #ifndef QT_NO_TEXTCUSTOMITEM TQTextTable::TQTextTable(TQTextDocument *p, const QMap & attr ) : TQTextCustomItem(p) { cellspacing = 2; cellpadding = 1; border = innerborder = 0; QMap::ConstIterator it, end = attr.end(); it = attr.find(TQLatin1String("cellspacing")); if (it != end) cellspacing = (*it).toInt(); it = attr.find(TQLatin1String("cellpadding")); if (it != end) cellpadding = (*it).toInt(); it = attr.find(TQLatin1String("border")); if (it != end) { if (*it == TQLatin1String("TRUE")) border = 1; else border = (*it).toInt(); } us_b = border; innerborder = us_ib = border ? 1 : 0; if (border) cellspacing += 2; us_ib = innerborder; us_cs = cellspacing; us_cp = cellpadding; outerborder = cellspacing + border; us_ob = outerborder; layout = new TQGridLayout(1, 1, cellspacing); fixwidth = 0; stretch = 0; it = attr.find(TQLatin1String("width")); if (it != end) { bool b; TQString s(*it); int w = s.toInt(&b); if (b) { fixwidth = w; } else { s = s.trimmed(); if (s.length() > 1 && s[(int)s.length()-1] == TQLatin1Char('%')) stretch = s.left(s.length()-1).toInt(); } } us_fixwidth = fixwidth; place = PlaceInline; if (attr[TQLatin1String("align")] == TQLatin1String("left")) place = PlaceLeft; else if (attr[TQLatin1String("align")] == TQLatin1String("right")) place = PlaceRight; cachewidth = 0; attributes = attr; pageBreakFor = -1; } TQTextTable::~TQTextTable() { delete layout; } TQString TQTextTable::richText() const { TQString s; s = TQLatin1String("::ConstIterator it = attributes.begin(); for (; it != attributes.end(); ++it) s += it.key() + TQLatin1Char('=') + *it + TQLatin1Char(' '); s += TQLatin1String(">\n"); int lastRow = -1; bool needEnd = false; for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *cell = cells.at(idx); if (lastRow != cell->row()) { if (lastRow != -1) s += TQLatin1String("\n"); s += TQLatin1String(""); lastRow = cell->row(); needEnd = true; } s += TQLatin1String("attributes.constBegin(); for (; it != cell->attributes.constEnd(); ++it) s += TQLatin1Char(' ') + it.key() + TQLatin1Char('=') + *it; s += TQLatin1Char('>'); s += cell->richText()->richText(); s += TQLatin1String(""); } if (needEnd) s += TQLatin1String("\n"); s += TQLatin1String("
\n"); return s; } void TQTextTable::adjustToPainter(TQPainter* p) { cellspacing = scale(us_cs, p); cellpadding = scale(us_cp, p); border = scale(us_b , p); innerborder = scale(us_ib, p); outerborder = scale(us_ob ,p); fixwidth = scale( us_fixwidth, p); width = 0; cachewidth = 0; for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *cell = cells.at(idx); cell->adjustToPainter(p); } } void TQTextTable::adjustCells(int y , int shift) { bool enlarge = false; for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *cell = cells.at(idx); TQRect r = cell->geometry(); if (y <= r.top()) { r.moveBy(0, shift); cell->setGeometry(r); enlarge = true; } else if (y <= r.bottom()) { r.rBottom() += shift; cell->setGeometry(r); enlarge = true; } } if (enlarge) height += shift; } void TQTextTable::pageBreak(int yt, TQTextFlow* flow) { if (flow->pageSize() <= 0) return; if (layout && pageBreakFor > 0 && pageBreakFor != yt) { layout->invalidate(); int h = layout->heightForWidth(width-2*outerborder); layout->setGeometry(TQRect(0, 0, width-2*outerborder, h) ); height = layout->geometry().height()+2*outerborder; } pageBreakFor = yt; for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *cell = cells.at(idx); int y = yt + outerborder + cell->geometry().y(); int shift = flow->adjustFlow(y - cellspacing, width, cell->richText()->height() + 2*cellspacing); adjustCells(y - outerborder - yt, shift); } } void TQTextTable::draw(TQPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QPalette &pal, bool selected) { if (placement() != PlaceInline) { x = xpos; y = ypos; } for (int idx = 0; idx < cells.size(); ++idx) { TQTextTableCell *cell = cells.at(idx); if ((cx < 0 && cy < 0) || TQRect(cx, cy, cw, ch).intersects(TQRect(x + outerborder + cell->geometry().x(), y + outerborder + cell->geometry().y(), cell->geometry().width(), cell->geometry().height()))) { cell->draw(p, x+outerborder, y+outerborder, cx, cy, cw, ch, pal, selected); if (border) { TQRect r(x+outerborder+cell->geometry().x() - innerborder, y+outerborder+cell->geometry().y() - innerborder, cell->geometry().width() + 2 * innerborder, cell->geometry().height() + 2 * innerborder); if (is_printer(p)) { QPen oldPen = p->pen(); TQRect r2 = r; r2.adjust(innerborder/2, innerborder/2, -innerborder/2, -innerborder/2); p->setPen(QPen(pal.text().color(), innerborder)); p->drawRect(r2); p->setPen(oldPen); } else { int s = qMax(cellspacing-2*innerborder, 0); if (s) { p->fillRect(r.left()-s, r.top(), s+1, r.height(), pal.button()); p->fillRect(r.right(), r.top(), s+1, r.height(), pal.button()); p->fillRect(r.left()-s, r.top()-s, r.width()+2*s, s, pal.button()); p->fillRect(r.left()-s, r.bottom(), r.width()+2*s, s, pal.button()); } qDrawShadePanel(p, r, pal, true, innerborder); } } } } if (border) { TQRect r (x, y, width, height); if (is_printer(p)) { TQRect r2 = r; r2.adjust(border/2, border/2, -border/2, -border/2); QPen oldPen = p->pen(); p->setPen(QPen(pal.text().color(), border)); p->drawRect(r2); p->setPen(oldPen); } else { int s = border+qMax(cellspacing-2*innerborder, 0); if (s) { p->fillRect(r.left(), r.top(), s, r.height(), pal.button()); p->fillRect(r.right()-s, r.top(), s, r.height(), pal.button()); p->fillRect(r.left(), r.top(), r.width(), s, pal.button()); p->fillRect(r.left(), r.bottom()-s, r.width(), s, pal.button()); } qDrawShadePanel(p, r, pal, false, border); } } } int TQTextTable::minimumWidth() const { return qMax(fixwidth, ((layout ? layout->minimumSize().width() : 0) + 2 * outerborder)); } void TQTextTable::resize(int nwidth) { if (fixwidth && cachewidth != 0) return; if (nwidth == cachewidth) return; cachewidth = nwidth; int w = nwidth; format(w); if (stretch) nwidth = nwidth * stretch / 100; width = nwidth; layout->invalidate(); int shw = layout->sizeHint().width() + 2*outerborder; int mw = layout->minimumSize().width() + 2*outerborder; if (stretch) width = qMax(mw, nwidth); else width = qMax(mw, qMin(nwidth, shw)); if (fixwidth) width = fixwidth; layout->invalidate(); mw = layout->minimumSize().width() + 2*outerborder; width = qMax(width, mw); int h = layout->heightForWidth(width-2*outerborder); layout->setGeometry(TQRect(0, 0, width-2*outerborder, h) ); height = layout->geometry().height()+2*outerborder; } void TQTextTable::format(int w) { for (int i = 0; i < (int)cells.count(); ++i) { TQTextTableCell *cell = cells.at(i); TQRect r = cell->geometry(); r.setWidth(w - 2*outerborder); cell->setGeometry(r); } } void TQTextTable::addCell(TQTextTableCell* cell) { cells.append(cell); layout->addMultiCell(cell, cell->row(), cell->row() + cell->rowspan()-1, cell->column(), cell->column() + cell->colspan()-1); } bool TQTextTable::enter(TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy, bool atEnd) { currCell.remove(c); if (!atEnd) return next(c, doc, parag, idx, ox, oy); currCell.insert(c, cells.count()); return prev(c, doc, parag, idx, ox, oy); } bool TQTextTable::enterAt(TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy, const QPoint &pos) { currCell.remove(c); int lastCell = -1; int lastY = -1; int i; for (i = 0; i < (int)cells.count(); ++i) { TQTextTableCell *cell = cells.at(i); if (!cell) continue; TQRect r(cell->geometry().x(), cell->geometry().y(), cell->geometry().width() + 2 * innerborder + 2 * outerborder, cell->geometry().height() + 2 * innerborder + 2 * outerborder); if (r.left() <= pos.x() && r.right() >= pos.x()) { if (cell->geometry().y() > lastY) { lastCell = i; lastY = cell->geometry().y(); } if (r.top() <= pos.y() && r.bottom() >= pos.y()) { currCell.insert(c, i); break; } } } if (i == (int) cells.count()) return false; // no cell found if (currCell.find(c) == currCell.end()) { if (lastY != -1) currCell.insert(c, lastCell); else return false; } TQTextTableCell *cell = cells.at(*currCell.find(c)); if (!cell) return false; doc = cell->richText(); parag = doc->firstParagraph(); idx = 0; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return true; } bool TQTextTable::next(TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy) { int cc = -1; if (currCell.find(c) != currCell.end()) cc = *currCell.find(c); if (cc > (int)cells.count() - 1 || cc < 0) cc = -1; currCell.remove(c); currCell.insert(c, ++cc); if (cc >= (int)cells.count()) { currCell.insert(c, 0); TQTextCustomItem::next(c, doc, parag, idx, ox, oy); TQTextTableCell *cell = cells.first(); if (!cell) return false; doc = cell->richText(); idx = -1; return true; } if (currCell.find(c) == currCell.end()) return false; TQTextTableCell *cell = cells.at(*currCell.find(c)); if (!cell) return false; doc = cell->richText(); parag = doc->firstParagraph(); idx = 0; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return true; } bool TQTextTable::prev(TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy) { int cc = -1; if (currCell.find(c) != currCell.end()) cc = *currCell.find(c); if (cc > (int)cells.count() - 1 || cc < 0) cc = cells.count(); currCell.remove(c); currCell.insert(c, --cc); if (cc < 0) { currCell.insert(c, 0); TQTextCustomItem::prev(c, doc, parag, idx, ox, oy); TQTextTableCell *cell = cells.first(); if (!cell) return false; doc = cell->richText(); idx = -1; return true; } if (currCell.find(c) == currCell.end()) return false; TQTextTableCell *cell = cells.at(*currCell.find(c)); if (!cell) return false; doc = cell->richText(); parag = doc->lastParagraph(); idx = parag->length() - 1; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return true; } bool TQTextTable::down(TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy) { if (currCell.find(c) == currCell.end()) return false; TQTextTableCell *cell = cells.at(*currCell.find(c)); if (cell->row_ == layout->numRows() - 1) { currCell.insert(c, 0); TQTextCustomItem::down(c, doc, parag, idx, ox, oy); TQTextTableCell *cell = cells.first(); if (!cell) return false; doc = cell->richText(); idx = -1; return true; } int oldRow = cell->row_; int oldCol = cell->col_; if (currCell.find(c) == currCell.end()) return false; int cc = *currCell.find(c); for (int i = cc; i < (int)cells.count(); ++i) { cell = cells.at(i); if (cell->row_ > oldRow && cell->col_ == oldCol) { currCell.insert(c, i); break; } } doc = cell->richText(); if (!cell) return false; parag = doc->firstParagraph(); idx = 0; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return true; } bool TQTextTable::up(TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy) { if (currCell.find(c) == currCell.end()) return false; TQTextTableCell *cell = cells.at(*currCell.find(c)); if (cell->row_ == 0) { currCell.insert(c, 0); TQTextCustomItem::up(c, doc, parag, idx, ox, oy); TQTextTableCell *cell = cells.first(); if (!cell) return false; doc = cell->richText(); idx = -1; return true; } int oldRow = cell->row_; int oldCol = cell->col_; if (currCell.find(c) == currCell.end()) return false; int cc = *currCell.find(c); for (int i = cc; i >= 0; --i) { cell = cells.at(i); if (cell->row_ < oldRow && cell->col_ == oldCol) { currCell.insert(c, i); break; } } doc = cell->richText(); if (!cell) return false; parag = doc->lastParagraph(); idx = parag->length() - 1; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return true; } TQTextTableCell::TQTextTableCell(TQTextTable* table, int row, int column, const QMap &attr, const TQStyleSheetItem* style, const TQTextFormat& fmt, const TQString& context, TQMimeSourceFactory &factory, TQStyleSheet *sheet, const TQString& doc) { cached_width = -1; cached_sizehint = -1; maxw = QWIDGETSIZE_MAX; minw = 0; parent = table; row_ = row; col_ = column; stretch_ = 0; richtext = new TQTextDocument(table->parent); richtext->formatCollection()->setPaintDevice(table->parent->formatCollection()->paintDevice()); richtext->bodyText = fmt.color(); richtext->setTableCell(this); QMap::ConstIterator it, end = attr.end(); int halign = style->alignment(); if (halign != TQStyleSheetItem::Undefined) richtext->setAlignment(halign); it = attr.find(TQLatin1String("align")); if (it != end && ! (*it).isEmpty()) { TQString a = (*it).toLower(); if (a == TQLatin1String("left")) richtext->setAlignment(Qt::AlignLeft); else if (a == TQLatin1String("center")) richtext->setAlignment(Qt::AlignHCenter); else if (a == TQLatin1String("right")) richtext->setAlignment(Qt::AlignRight); } align = 0; it = attr.find(TQLatin1String("valign")); if (it != end && ! (*it).isEmpty()) { TQString va = (*it).toLower(); if ( va == TQLatin1String("top") ) align |= Qt::AlignTop; else if ( va == TQLatin1String("center") || va == TQLatin1String("middle") ) align |= Qt::AlignVCenter; else if (va == TQLatin1String("bottom")) align |= Qt::AlignBottom; } richtext->setFormatter(table->parent->formatter()); richtext->setUseFormatCollection(table->parent->useFormatCollection()); richtext->setMimeSourceFactory(&factory); richtext->setStyleSheet(sheet); richtext->setRichText(doc, context, &fmt); rowspan_ = 1; colspan_ = 1; it = attr.find(TQLatin1String("colspan")); if (it != end) colspan_ = (*it).toInt(); it = attr.find(TQLatin1String("rowspan")); if (it != end) rowspan_ = (*it).toInt(); background = 0; it = attr.find(TQLatin1String("bgcolor")); if (it != end) { background = new TQBrush(QColor(*it)); } hasFixedWidth = false; it = attr.find(TQLatin1String("width")); if (it != end) { bool b; TQString s(*it); int w = s.toInt(&b); if (b) { maxw = w; minw = maxw; hasFixedWidth = true; } else { s = s.trimmed(); if (s.length() > 1 && s[(int)s.length()-1] == TQLatin1Char('%')) stretch_ = s.left(s.length()-1).toInt(); } } attributes = attr; parent->addCell(this); } TQTextTableCell::~TQTextTableCell() { delete background; background = 0; delete richtext; richtext = 0; } TQSize TQTextTableCell::tqsizeHint() const { int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance); int used = richtext->widthUsed() + extra; if (stretch_) { int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding; return QSize(qMin(w, maxw), 0).expandedTo(minimumSize()); } return QSize(used, 0).expandedTo(minimumSize()); } TQSize TQTextTableCell::tqminimumSize() const { int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance); return QSize(qMax(richtext->minimumWidth() + extra, minw), 0); } TQSize TQTextTableCell::tqmaximumSize() const { return QSize(maxw, QWIDGETSIZE_MAX); } Qt::Orientations TQTextTableCell::expandingDirections() const { return Qt::Horizontal | Qt::Vertical; } bool TQTextTableCell::isEmpty() const { return false; } void TQTextTableCell::setGeometry(const TQRect& r) { int extra = 2 * (parent->innerborder + parent->cellpadding); if (r.width() != cached_width) richtext->doLayout(TQTextFormat::painter(), r.width() - extra); cached_width = r.width(); geom = r; } TQRect TQTextTableCell::tqgeometry() const { return geom; } bool TQTextTableCell::hasHeightForWidth() const { return true; } int TQTextTableCell::heightForWidth(int w) const { int extra = 2 * (parent->innerborder + parent->cellpadding); w = qMax(minw, w); if (cached_width != w) { TQTextTableCell* that = (TQTextTableCell*) this; that->richtext->doLayout(TQTextFormat::painter(), w - extra); that->cached_width = w; } return richtext->height() + extra; } void TQTextTableCell::adjustToPainter(TQPainter* p) { TQTextParagraph *parag = richtext->firstParagraph(); while (parag) { parag->adjustToPainter(p); parag = parag->next(); } } int TQTextTableCell::horizontalAlignmentOffset() const { return parent->cellpadding; } int TQTextTableCell::verticalAlignmentOffset() const { if ((align & Qt::AlignVCenter) == Qt::AlignVCenter) return (geom.height() - richtext->height()) / 2; else if ((align & Qt::AlignBottom) == Qt::AlignBottom) return geom.height() - parent->cellpadding - richtext->height() ; return parent->cellpadding; } void TQTextTableCell::draw(TQPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QPalette &pal, bool) { if (cached_width != geom.width()) { int extra = 2 * (parent->innerborder + parent->cellpadding); richtext->doLayout(p, geom.width() - extra); cached_width = geom.width(); } QPalette pal2(pal); if (background) pal2.setBrush(QPalette::Base, *background); else if (richtext->paper()) pal2.setBrush(QPalette::Base, *richtext->paper()); p->save(); p->translate(x + geom.x(), y + geom.y()); if (background) p->fillRect(0, 0, geom.width(), geom.height(), *background); else if (richtext->paper()) p->fillRect(0, 0, geom.width(), geom.height(), *richtext->paper()); p->translate(horizontalAlignmentOffset(), verticalAlignmentOffset()); QRegion r; if (cx >= 0 && cy >= 0) richtext->draw(p, cx - (x + horizontalAlignmentOffset() + geom.x()), cy - (y + geom.y() + verticalAlignmentOffset()), cw, ch, pal2, false, false, 0); else richtext->draw(p, -1, -1, -1, -1, pal2, false, false, 0); p->restore(); } #endif QT_END_NAMESPACE #endif //QT_NO_RICHTEXT #else // USE_QT4 /**************************************************************************** ** ** Implementation of the internal TQt classes dealing with rich text ** ** Created : 990101 ** ** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. ** ** 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 requirements 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 "tqrichtext_p.h" #ifndef TQT_NO_RICHTEXT #include "tqstringlist.h" #include "tqfont.h" #include "tqtextstream.h" #include "tqfile.h" #include "tqapplication.h" #include "tqmap.h" #include "tqfileinfo.h" #include "tqstylesheet.h" #include "tqmime.h" #include "tqimage.h" #include "tqdragobject.h" #include "tqpaintdevicemetrics.h" #include "tqpainter.h" #include "tqdrawutil.h" #include "tqcursor.h" #include "tqptrstack.h" #include "tqptrdict.h" #include "tqstyle.h" #include "tqcleanuphandler.h" #include "tqtextengine_p.h" #include #include static TQTextCursor* richTextExportStart = 0; static TQTextCursor* richTextExportEnd = 0; class TQTextFormatCollection; const int border_tolerance = 2; #ifdef TQ_WS_WIN #include "tqt_windows.h" #endif #define TQChar_linesep TQChar(0x2028U) static inline bool is_printer( TQPainter *p ) { if ( !p || !p->tqdevice() ) return FALSE; return p->tqdevice()->devType() == TQInternal::Printer; } static inline int scale( int value, TQPainter *painter ) { if ( is_printer( painter ) ) { TQPaintDeviceMetrics metrics( painter->tqdevice() ); #if defined(TQ_WS_X11) value = value * metrics.logicalDpiY() / TQPaintDevice::x11AppDpiY( painter->tqdevice()->x11Screen() ); #elif defined (TQ_WS_WIN) HDC hdc = GetDC( 0 ); int gdc = GetDeviceCaps( hdc, LOGPIXELSY ); if ( gdc ) value = value * metrics.logicalDpiY() / gdc; ReleaseDC( 0, hdc ); #elif defined (TQ_WS_MAC) value = value * metrics.logicalDpiY() / 75; // ##### FIXME #elif defined (TQ_WS_TQWS) value = value * metrics.logicalDpiY() / 75; #endif } return value; } inline bool isBreakable( TQTextString *string, int pos ) { if (string->at(pos).nobreak) return FALSE; return (pos < string->length()-1 && string->at(pos+1).softBreak); } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void TQTextCommandHistory::addCommand( TQTextCommand *cmd ) { if ( current < (int)history.count() - 1 ) { TQPtrList commands; commands.setAutoDelete( FALSE ); for( int i = 0; i <= current; ++i ) { commands.insert( i, history.at( 0 ) ); history.take( 0 ); } commands.append( cmd ); history.clear(); history = commands; history.setAutoDelete( TRUE ); } else { history.append( cmd ); } if ( (int)history.count() > steps ) history.removeFirst(); else ++current; } TQTextCursor *TQTextCommandHistory::undo( TQTextCursor *c ) { if ( current > -1 ) { TQTextCursor *c2 = history.at( current )->unexecute( c ); --current; return c2; } return 0; } TQTextCursor *TQTextCommandHistory::redo( TQTextCursor *c ) { if ( current > -1 ) { if ( current < (int)history.count() - 1 ) { ++current; return history.at( current )->execute( c ); } } else { if ( history.count() > 0 ) { ++current; return history.at( current )->execute( c ); } } return 0; } bool TQTextCommandHistory::isUndoAvailable() { return current > -1; } bool TQTextCommandHistory::isRedoAvailable() { return ((current > -1) && current < ((int)history.count() - 1)) || ((current == -1) && (history.count()) > 0); } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextDeleteCommand::TQTextDeleteCommand( TQTextDocument *d, int i, int idx, const TQMemArray &str, const TQByteArray& oldStyleInfo ) : TQTextCommand( d ), id( i ), index( idx ), parag( 0 ), text( str ), styleInformation( oldStyleInfo ) { for ( int j = 0; j < (int)text.size(); ++j ) { if ( text[ j ].format() ) text[ j ].format()->addRef(); } } TQTextDeleteCommand::TQTextDeleteCommand( TQTextParagraph *p, int idx, const TQMemArray &str ) : TQTextCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str ) { for ( int i = 0; i < (int)text.size(); ++i ) { if ( text[ i ].format() ) text[ i ].format()->addRef(); } } TQTextDeleteCommand::~TQTextDeleteCommand() { for ( int i = 0; i < (int)text.size(); ++i ) { if ( text[ i ].format() ) text[ i ].format()->removeRef(); } text.resize( 0 ); } TQTextCursor *TQTextDeleteCommand::execute( TQTextCursor *c ) { TQTextParagraph *s = doc ? doc->paragAt( id ) : parag; if ( !s ) { qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId() ); return 0; } cursor.setParagraph( s ); cursor.setIndex( index ); int len = text.size(); if ( c ) *c = cursor; if ( doc ) { doc->setSelectionStart( TQTextDocument::Temp, cursor ); for ( int i = 0; i < len; ++i ) cursor.gotoNextLetter(); doc->setSelectionEnd( TQTextDocument::Temp, cursor ); doc->removeSelectedText( TQTextDocument::Temp, &cursor ); if ( c ) *c = cursor; } else { s->remove( index, len ); } return c; } TQTextCursor *TQTextDeleteCommand::unexecute( TQTextCursor *c ) { TQTextParagraph *s = doc ? doc->paragAt( id ) : parag; if ( !s ) { qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId() ); return 0; } cursor.setParagraph( s ); cursor.setIndex( index ); TQString str = TQTextString::toString( text ); cursor.insert( str, TRUE, &text ); if ( c ) *c = cursor; cursor.setParagraph( s ); cursor.setIndex( index ); #ifndef TQT_NO_DATASTREAM if ( !styleInformation.isEmpty() ) { TQDataStream styleStream( styleInformation, IO_ReadOnly ); int num; styleStream >> num; TQTextParagraph *p = s; while ( num-- && p ) { p->readStyleInformation( styleStream ); p = p->next(); } } #endif s = cursor.paragraph(); while ( s ) { s->format(); s->setChanged( TRUE ); if ( s == c->paragraph() ) break; s = s->next(); } return &cursor; } TQTextFormatCommand::TQTextFormatCommand( TQTextDocument *d, int sid, int sidx, int eid, int eidx, const TQMemArray &old, TQTextFormat *f, int fl ) : TQTextCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), format( f ), oldFormats( old ), flags( fl ) { format = d->formatCollection()->format( f ); for ( int j = 0; j < (int)oldFormats.size(); ++j ) { if ( oldFormats[ j ].format() ) oldFormats[ j ].format()->addRef(); } } TQTextFormatCommand::~TQTextFormatCommand() { format->removeRef(); for ( int j = 0; j < (int)oldFormats.size(); ++j ) { if ( oldFormats[ j ].format() ) oldFormats[ j ].format()->removeRef(); } } TQTextCursor *TQTextFormatCommand::execute( TQTextCursor *c ) { TQTextParagraph *sp = doc->paragAt( startId ); TQTextParagraph *ep = doc->paragAt( endId ); if ( !sp || !ep ) return c; TQTextCursor start( doc ); start.setParagraph( sp ); start.setIndex( startIndex ); TQTextCursor end( doc ); end.setParagraph( ep ); end.setIndex( endIndex ); doc->setSelectionStart( TQTextDocument::Temp, start ); doc->setSelectionEnd( TQTextDocument::Temp, end ); doc->setFormat( TQTextDocument::Temp, format, flags ); doc->removeSelection( TQTextDocument::Temp ); if ( endIndex == ep->length() ) end.gotoLeft(); *c = end; return c; } TQTextCursor *TQTextFormatCommand::unexecute( TQTextCursor *c ) { TQTextParagraph *sp = doc->paragAt( startId ); TQTextParagraph *ep = doc->paragAt( endId ); if ( !sp || !ep ) return 0; int idx = startIndex; int fIndex = 0; while ( fIndex < int(oldFormats.size()) ) { if ( oldFormats.at( fIndex ).c == '\n' ) { if ( idx > 0 ) { if ( idx < sp->length() && fIndex > 0 ) sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() ); if ( sp == ep ) break; sp = sp->next(); idx = 0; } fIndex++; } if ( oldFormats.at( fIndex ).format() ) sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() ); idx++; fIndex++; if ( fIndex >= (int)oldFormats.size() ) break; if ( idx >= sp->length() ) { if ( sp == ep ) break; sp = sp->next(); idx = 0; } } TQTextCursor end( doc ); end.setParagraph( ep ); end.setIndex( endIndex ); if ( endIndex == ep->length() ) end.gotoLeft(); *c = end; return c; } TQTextStyleCommand::TQTextStyleCommand( TQTextDocument *d, int fParag, int lParag, const TQByteArray& beforeChange ) : TQTextCommand( d ), firstParag( fParag ), lastParag( lParag ), before( beforeChange ) { after = readStyleInformation( d, fParag, lParag ); } TQByteArray TQTextStyleCommand::readStyleInformation( TQTextDocument* doc, int fParag, int lParag ) { TQByteArray style; #ifndef TQT_NO_DATASTREAM TQTextParagraph *p = doc->paragAt( fParag ); if ( !p ) return style; TQDataStream styleStream( style, IO_WriteOnly ); int num = lParag - fParag + 1; styleStream << num; while ( num -- && p ) { p->writeStyleInformation( styleStream ); p = p->next(); } #endif return style; } void TQTextStyleCommand::writeStyleInformation( TQTextDocument* doc, int fParag, const TQByteArray& style ) { #ifndef TQT_NO_DATASTREAM TQTextParagraph *p = doc->paragAt( fParag ); if ( !p ) return; TQDataStream styleStream( style, IO_ReadOnly ); int num; styleStream >> num; while ( num-- && p ) { p->readStyleInformation( styleStream ); p = p->next(); } #endif } TQTextCursor *TQTextStyleCommand::execute( TQTextCursor *c ) { writeStyleInformation( doc, firstParag, after ); return c; } TQTextCursor *TQTextStyleCommand::unexecute( TQTextCursor *c ) { writeStyleInformation( doc, firstParag, before ); return c; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextCursor::TQTextCursor( TQTextDocument *d ) : idx( 0 ), tmpX( -1 ), ox( 0 ), oy( 0 ), valid( TRUE ) { para = d ? d->firstParagraph() : 0; } TQTextCursor::TQTextCursor( const TQTextCursor &c ) { ox = c.ox; oy = c.oy; idx = c.idx; para = c.para; tmpX = c.tmpX; indices = c.indices; paras = c.paras; xOffsets = c.xOffsets; yOffsets = c.yOffsets; valid = c.valid; } TQTextCursor &TQTextCursor::operator=( const TQTextCursor &c ) { ox = c.ox; oy = c.oy; idx = c.idx; para = c.para; tmpX = c.tmpX; indices = c.indices; paras = c.paras; xOffsets = c.xOffsets; yOffsets = c.yOffsets; valid = c.valid; return *this; } bool TQTextCursor::operator==( const TQTextCursor &c ) const { return para == c.para && idx == c.idx; } int TQTextCursor::totalOffsetX() const { int xoff = ox; for ( TQValueStack::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit ) xoff += *xit; return xoff; } int TQTextCursor::totalOffsetY() const { int yoff = oy; for ( TQValueStack::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit ) yoff += *yit; return yoff; } #ifndef TQT_NO_TEXTCUSTOMITEM void TQTextCursor::gotoIntoNested( const TQPoint &globalPos ) { if ( !para ) return; TQ_ASSERT( para->at( idx )->isCustom() ); push(); ox = 0; int bl, y; para->lineHeightOfChar( idx, &bl, &y ); oy = y + para->rect().y(); ox = para->at( idx )->x; TQTextDocument* doc = document(); para->at( idx )->customItem()->enterAt( this, doc, para, idx, ox, oy, globalPos-TQPoint(ox,oy) ); } #endif void TQTextCursor::invalidateNested() { if ( nestedDepth() ) { TQValueStack::Iterator it = paras.begin(); TQValueStack::Iterator it2 = indices.begin(); for ( ; it != paras.end(); ++it, ++it2 ) { if ( *it == para ) continue; (*it)->tqinvalidate( 0 ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( (*it)->at( *it2 )->isCustom() ) (*it)->at( *it2 )->customItem()->tqinvalidate(); #endif } } } void TQTextCursor::insert( const QString &str, bool checkNewLine, TQMemArray *formatting ) { tmpX = -1; bool justInsert = TRUE; TQString s( str ); #if defined(TQ_WS_WIN) if ( checkNewLine ) { int i = 0; while ( ( i = s.find( '\r', i ) ) != -1 ) s.remove( i ,1 ); } #endif if ( checkNewLine ) justInsert = s.find( '\n' ) == -1; if ( justInsert ) { // we ignore new lines and insert all in the current para at the current index para->insert( idx, s.tqunicode(), s.length() ); if ( formatting ) { for ( int i = 0; i < (int)s.length(); ++i ) { if ( formatting->at( i ).format() ) { formatting->at( i ).format()->addRef(); para->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE ); } } } idx += s.length(); } else { // we split at new lines int start = -1; int end; int y = para->rect().y() + para->rect().height(); int lastIndex = 0; do { end = s.find( '\n', start + 1 ); // find line break if ( end == -1 ) // didn't find one, so end of line is end of string end = s.length(); int len = (start == -1 ? end : end - start - 1); if ( len > 0 ) // insert the line para->insert( idx, s.tqunicode() + start + 1, len ); else para->tqinvalidate( 0 ); if ( formatting ) { // set formats to the chars of the line for ( int i = 0; i < len; ++i ) { if ( formatting->at( i + lastIndex ).format() ) { formatting->at( i + lastIndex ).format()->addRef(); para->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE ); } } lastIndex += len; } start = end; // next start is at the end of this line idx += len; // increase the index of the cursor to the end of the inserted text if ( s[end] == '\n' ) { // if at the end was a line break, break the line splitAndInsertEmptyParagraph( FALSE, TRUE ); para->setEndState( -1 ); para->prev()->format( -1, FALSE ); lastIndex++; } } while ( end < (int)s.length() ); para->format( -1, FALSE ); int dy = para->rect().y() + para->rect().height() - y; TQTextParagraph *p = para; p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 ); p = p->next(); while ( p ) { p->setParagId( p->prev()->paragId() + 1 ); p->move( dy ); p->tqinvalidate( 0 ); p->setEndState( -1 ); p = p->next(); } } int h = para->rect().height(); para->format( -1, TRUE ); if ( h != para->rect().height() ) invalidateNested(); else if ( para->document() && para->document()->tqparent() ) para->document()->nextDoubleBuffered = TRUE; fixCursorPosition(); } void TQTextCursor::gotoLeft() { if ( para->string()->isRightToLeft() ) gotoNextLetter(); else gotoPreviousLetter(); } void TQTextCursor::gotoPreviousLetter() { tmpX = -1; if ( idx > 0 ) { idx = para->string()->previousCursorPosition( idx ); #ifndef TQT_NO_TEXTCUSTOMITEM const TQTextStringChar *tsc = para->at( idx ); if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) processNesting( EnterEnd ); #endif } else if ( para->prev() ) { para = para->prev(); while ( !para->isVisible() && para->prev() ) para = para->prev(); idx = para->length() - 1; } else if ( nestedDepth() ) { pop(); processNesting( Prev ); if ( idx == -1 ) { pop(); if ( idx > 0 ) { idx = para->string()->previousCursorPosition( idx ); #ifndef TQT_NO_TEXTCUSTOMITEM const TQTextStringChar *tsc = para->at( idx ); if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) processNesting( EnterEnd ); #endif } else if ( para->prev() ) { para = para->prev(); idx = para->length() - 1; } } } } void TQTextCursor::push() { indices.push( idx ); paras.push( para ); xOffsets.push( ox ); yOffsets.push( oy ); } void TQTextCursor::pop() { if ( indices.isEmpty() ) return; idx = indices.pop(); para = paras.pop(); ox = xOffsets.pop(); oy = yOffsets.pop(); } void TQTextCursor::restoreState() { while ( !indices.isEmpty() ) pop(); } bool TQTextCursor::place( const TQPoint &p, TQTextParagraph *s, bool link, bool loosePlacing, bool matchBetweenCharacters ) { TQPoint pos( p ); TQRect r; TQTextParagraph *str = s; if ( pos.y() < s->rect().y() ) { pos.setY( s->rect().y() ); #ifdef TQ_WS_MACX pos.setX( s->rect().x() ); #endif } while ( s ) { r = s->rect(); r.setWidth( document() ? document()->width() : TQWIDGETSIZE_MAX ); if ( s->isVisible() ) str = s; if ( pos.y() >= r.y() && pos.y() <= r.y() + r.height() ) break; if ( loosePlacing == TRUE && !s->next() ) { #ifdef TQ_WS_MACX pos.setX( s->rect().x() + s->rect().width() ); #endif break; } s = s->next(); } if ( !s || !str ) return FALSE; s = str; setParagraph( s ); int y = s->rect().y(); int lines = s->lines(); TQTextStringChar *chr = 0; int index = 0; int i = 0; int cy = 0; int ch = 0; for ( ; i < lines; ++i ) { chr = s->lineStartOfLine( i, &index ); cy = s->lineY( i ); ch = s->lineHeight( i ); if ( !chr ) return FALSE; if ( pos.y() <= y + cy + ch ) break; } int nextLine; if ( i < lines - 1 ) s->lineStartOfLine( i+1, &nextLine ); else nextLine = s->length(); i = index; int x = s->rect().x(); if ( pos.x() < x ) pos.setX( x + 1 ); int cw; int curpos = -1; int dist = 10000000; bool inCustom = FALSE; while ( i < nextLine ) { chr = s->at(i); int cpos = x + chr->x; cw = s->string()->width( i ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( chr->isCustom() && chr->customItem()->isNested() ) { if ( pos.x() >= cpos && pos.x() <= cpos + cw && pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) { inCustom = TRUE; curpos = i; break; } } else #endif { if( chr->rightToLeft ) cpos += cw; int d = cpos - pos.x(); bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft; if ( ( matchBetweenCharacters == TRUE && (TQABS( d ) < dist || (dist == d && dm == TRUE )) && para->string()->validCursorPosition( i ) ) || ( matchBetweenCharacters == FALSE && ( d == 0 || dm == TRUE ) ) ) { dist = TQABS( d ); if ( !link || ( pos.x() >= x + chr->x && ( loosePlacing == TRUE || pos.x() < cpos ) ) ) curpos = i; } } i++; } if ( curpos == -1 ) { if ( loosePlacing == TRUE ) curpos = s->length()-1; else return FALSE; } setIndex( curpos ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( inCustom && para->document() && para->at( curpos )->isCustom() && para->at( curpos )->customItem()->isNested() ) { TQTextDocument *oldDoc = para->document(); gotoIntoNested( pos ); if ( oldDoc == para->document() ) return TRUE; TQPoint p( pos.x() - offsetX(), pos.y() - offsetY() ); if ( !place( p, document()->firstParagraph(), link ) ) pop(); } #endif return TRUE; } bool TQTextCursor::processNesting( Operation op ) { if ( !para->document() ) return FALSE; TQTextDocument* doc = para->document(); push(); ox = para->at( idx )->x; int bl, y; para->lineHeightOfChar( idx, &bl, &y ); oy = y + para->rect().y(); bool ok = FALSE; #ifndef TQT_NO_TEXTCUSTOMITEM switch ( op ) { case EnterBegin: ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy ); break; case EnterEnd: ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy, TRUE ); break; case Next: ok = para->at( idx )->customItem()->next( this, doc, para, idx, ox, oy ); break; case Prev: ok = para->at( idx )->customItem()->prev( this, doc, para, idx, ox, oy ); break; case Down: ok = para->at( idx )->customItem()->down( this, doc, para, idx, ox, oy ); break; case Up: ok = para->at( idx )->customItem()->up( this, doc, para, idx, ox, oy ); break; } if ( !ok ) #endif pop(); return ok; } void TQTextCursor::gotoRight() { if ( para->string()->isRightToLeft() ) gotoPreviousLetter(); else gotoNextLetter(); } void TQTextCursor::gotoNextLetter() { tmpX = -1; #ifndef TQT_NO_TEXTCUSTOMITEM const TQTextStringChar *tsc = para->at( idx ); if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) { if ( processNesting( EnterBegin ) ) return; } #endif if ( idx < para->length() - 1 ) { idx = para->string()->nextCursorPosition( idx ); } else if ( para->next() ) { para = para->next(); while ( !para->isVisible() && para->next() ) para = para->next(); idx = 0; } else if ( nestedDepth() ) { pop(); processNesting( Next ); if ( idx == -1 ) { pop(); if ( idx < para->length() - 1 ) { idx = para->string()->nextCursorPosition( idx ); } else if ( para->next() ) { para = para->next(); idx = 0; } } } } void TQTextCursor::gotoUp() { int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; if (tmpX < 0) tmpX = x(); if ( indexOfLineStart == 0 ) { if ( !para->prev() ) { if ( !nestedDepth() ) return; pop(); processNesting( Up ); if ( idx == -1 ) { pop(); if ( !para->prev() ) return; idx = tmpX = 0; } else { tmpX = -1; return; } } TQTextParagraph *p = para->prev(); while ( p && !p->isVisible() ) p = p->prev(); if ( p ) para = p; int lastLine = para->lines() - 1; if ( !para->lineStartOfLine( lastLine, &indexOfLineStart ) ) return; idx = indexOfLineStart; while (idx < para->length()-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } else { --line; int oldIndexOfLineStart = indexOfLineStart; if ( !para->lineStartOfLine( line, &indexOfLineStart ) ) return; idx = indexOfLineStart; while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } fixCursorPosition(); } void TQTextCursor::gotoDown() { int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; if (tmpX < 0) tmpX = x(); if ( line == para->lines() - 1 ) { if ( !para->next() ) { if ( !nestedDepth() ) return; pop(); processNesting( Down ); if ( idx == -1 ) { pop(); if ( !para->next() ) return; idx = tmpX = 0; } else { tmpX = -1; return; } } TQTextParagraph *s = para->next(); while ( s && !s->isVisible() ) s = s->next(); if ( s ) para = s; if ( !para->lineStartOfLine( 0, &indexOfLineStart ) ) return; int end; if ( para->lines() == 1 ) end = para->length(); else para->lineStartOfLine( 1, &end ); idx = indexOfLineStart; while (idx < end-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } else { ++line; int end; if ( line == para->lines() - 1 ) end = para->length(); else para->lineStartOfLine( line + 1, &end ); if ( !para->lineStartOfLine( line, &indexOfLineStart ) ) return; idx = indexOfLineStart; while (idx < end-1 && para->at(idx)->x < tmpX) ++idx; if (idx > indexOfLineStart && para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x) --idx; } fixCursorPosition(); } void TQTextCursor::gotoLineEnd() { tmpX = -1; int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; if ( line == para->lines() - 1 ) { idx = para->length() - 1; } else { c = para->lineStartOfLine( ++line, &indexOfLineStart ); indexOfLineStart--; idx = indexOfLineStart; } } void TQTextCursor::gotoLineStart() { tmpX = -1; int indexOfLineStart; int line; TQTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; idx = indexOfLineStart; } void TQTextCursor::gotoHome() { if ( topParagraph()->document() ) gotoPosition( topParagraph()->document()->firstParagraph() ); else gotoLineStart(); } void TQTextCursor::gotoEnd() { if ( topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid() ) gotoPosition( topParagraph()->document()->lastParagraph(), topParagraph()->document()->lastParagraph()->length() - 1); else gotoLineEnd(); } void TQTextCursor::gotoPageUp( int visibleHeight ) { int targetY = globalY() - visibleHeight; TQTextParagraph* old; int index; do { old = para; index = idx; gotoUp(); } while ( (old != para || index != idx) && globalY() > targetY ); } void TQTextCursor::gotoPageDown( int visibleHeight ) { int targetY = globalY() + visibleHeight; TQTextParagraph* old; int index; do { old = para; index = idx; gotoDown(); } while ( (old != para || index != idx) && globalY() < targetY ); } void TQTextCursor::gotoWordRight() { if ( para->string()->isRightToLeft() ) gotoPreviousWord(); else gotoNextWord(); } void TQTextCursor::gotoWordLeft() { if ( para->string()->isRightToLeft() ) gotoNextWord(); else gotoPreviousWord(); } static bool is_seperator( const TQChar &c, bool onlySpace ) { if ( onlySpace ) return c.isSpace(); return c.isSpace() || c == '\t' || c == '.' || c == ',' || c == ':' || c == ';' || c == '-' || c == '<' || c == '>' || c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}'; } void TQTextCursor::gotoPreviousWord( bool onlySpace ) { gotoPreviousLetter(); tmpX = -1; TQTextString *s = para->string(); bool allowSame = FALSE; if ( idx == ((int)s->length()-1) ) return; for ( int i = idx; i >= 0; --i ) { if ( is_seperator( s->at( i ).c, onlySpace ) ) { if ( !allowSame ) continue; idx = i + 1; return; } if ( !allowSame && !is_seperator( s->at( i ).c, onlySpace ) ) allowSame = TRUE; } idx = 0; } void TQTextCursor::gotoNextWord( bool onlySpace ) { tmpX = -1; TQTextString *s = para->string(); bool allowSame = FALSE; for ( int i = idx; i < (int)s->length(); ++i ) { if ( !is_seperator( s->at( i ).c, onlySpace ) ) { if ( !allowSame ) continue; idx = i; return; } if ( !allowSame && is_seperator( s->at( i ).c, onlySpace ) ) allowSame = TRUE; } if ( idx < ((int)s->length()-1) ) { gotoLineEnd(); } else if ( para->next() ) { TQTextParagraph *p = para->next(); while ( p && !p->isVisible() ) p = p->next(); if ( s ) { para = p; idx = 0; } } else { gotoLineEnd(); } } bool TQTextCursor::atParagStart() { return idx == 0; } bool TQTextCursor::atParagEnd() { return idx == para->length() - 1; } void TQTextCursor::splitAndInsertEmptyParagraph( bool ind, bool updateIds ) { if ( !para->document() ) return; tmpX = -1; TQTextFormat *f = 0; if ( para->document()->useFormatCollection() ) { f = para->at( idx )->format(); if ( idx == para->length() - 1 && idx > 0 ) f = para->at( idx - 1 )->format(); if ( f->isMisspelled() ) { f->removeRef(); f = para->document()->formatCollection()->format( f->font(), f->color() ); } } if ( atParagEnd() ) { TQTextParagraph *n = para->next(); TQTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds ); if ( f ) s->setFormat( 0, 1, f, TRUE ); s->copyParagData( para ); if ( ind ) { int oi, ni; s->indent( &oi, &ni ); para = s; idx = ni; } else { para = s; idx = 0; } } else if ( atParagStart() ) { TQTextParagraph *p = para->prev(); TQTextParagraph *s = para->document()->createParagraph( para->document(), p, para, updateIds ); if ( f ) s->setFormat( 0, 1, f, TRUE ); s->copyParagData( para ); if ( ind ) { s->indent(); s->format(); indent(); para->format(); } } else { TQString str = para->string()->toString().mid( idx, 0xFFFFFF ); TQTextParagraph *n = para->next(); TQTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds ); s->copyParagData( para ); s->remove( 0, 1 ); s->append( str, TRUE ); for ( int i = 0; i < str.length(); ++i ) { TQTextStringChar* tsc = para->at( idx + i ); s->setFormat( i, 1, tsc->format(), TRUE ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( tsc->isCustom() ) { TQTextCustomItem * item = tsc->customItem(); s->at( i )->setCustomItem( item ); tsc->loseCustomItem(); } #endif if ( tsc->isAnchor() ) s->at( i )->setAnchor( tsc->anchorName(), tsc->anchorHref() ); } para->truncate( idx ); if ( ind ) { int oi, ni; s->indent( &oi, &ni ); para = s; idx = ni; } else { para = s; idx = 0; } } invalidateNested(); } bool TQTextCursor::remove() { tmpX = -1; if ( !atParagEnd() ) { int next = para->string()->nextCursorPosition( idx ); para->remove( idx, next-idx ); int h = para->rect().height(); para->format( -1, TRUE ); if ( h != para->rect().height() ) invalidateNested(); else if ( para->document() && para->document()->tqparent() ) para->document()->nextDoubleBuffered = TRUE; return FALSE; } else if ( para->next() ) { para->join( para->next() ); invalidateNested(); return TRUE; } return FALSE; } /* needed to implement backspace the correct way */ bool TQTextCursor::removePreviousChar() { tmpX = -1; if ( !atParagStart() ) { para->remove( idx-1, 1 ); int h = para->rect().height(); idx--; // shouldn't be needed, just to make sure. fixCursorPosition(); para->format( -1, TRUE ); if ( h != para->rect().height() ) invalidateNested(); else if ( para->document() && para->document()->tqparent() ) para->document()->nextDoubleBuffered = TRUE; return FALSE; } else if ( para->prev() ) { para = para->prev(); para->join( para->next() ); invalidateNested(); return TRUE; } return FALSE; } void TQTextCursor::indent() { int oi = 0, ni = 0; para->indent( &oi, &ni ); if ( oi == ni ) return; if ( idx >= oi ) idx += ni - oi; else idx = ni; } void TQTextCursor::fixCursorPosition() { // searches for the closest valid cursor position if ( para->string()->validCursorPosition( idx ) ) return; int lineIdx; TQTextStringChar *start = para->lineStartOfChar( idx, &lineIdx, 0 ); int x = para->string()->at( idx ).x; int diff = TQABS(start->x - x); int best = lineIdx; TQTextStringChar *c = start; ++c; TQTextStringChar *end = ¶->string()->at( para->length()-1 ); while ( c <= end && !c->lineStart ) { int xp = c->x; if ( c->rightToLeft ) xp += para->string()->width( lineIdx + (c-start) ); int ndiff = TQABS(xp - x); if ( ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start)) ) { diff = ndiff; best = lineIdx + (c-start); } ++c; } idx = best; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextDocument::TQTextDocument( TQTextDocument *p ) : par( p ), parentPar( 0 ) #ifndef TQT_NO_TEXTCUSTOMITEM , tc( 0 ) #endif , tArray( 0 ), tStopWidth( 0 ) { fCollection = par ? par->fCollection : new TQTextFormatCollection; init(); } void TQTextDocument::init() { oTextValid = TRUE; mightHaveCustomItems = FALSE; if ( par ) par->insertChild( this ); pProcessor = 0; useFC = TRUE; pFormatter = 0; indenter = 0; fParag = 0; txtFormat = TQt::AutoText; preferRichText = FALSE; pages = FALSE; focusIndicator.parag = 0; minw = 0; wused = 0; minwParag = curParag = 0; align = TQt::AlignAuto; nSelections = 1; setStyleSheet( TQStyleSheet::defaultSheet() ); #ifndef TQT_NO_MIME factory_ = TQMimeSourceFactory::defaultFactory(); #endif contxt = TQString::null; underlLinks = par ? par->underlLinks : TRUE; backBrush = 0; buf_pixmap = 0; nextDoubleBuffered = FALSE; if ( par ) withoutDoubleBuffer = par->withoutDoubleBuffer; else withoutDoubleBuffer = FALSE; lParag = fParag = createParagraph( this, 0, 0 ); cx = 0; cy = 2; if ( par ) cx = cy = 0; cw = 600; vw = 0; flow_ = new TQTextFlow; flow_->setWidth( cw ); leftmargin = rightmargin = 4; scaleFontsFactor = 1; selectionColors[ Standard ] = TQApplication::palette().color( TQPalette::Active, TQColorGroup::Highlight ); selectionText[ Standard ] = TRUE; selectionText[ IMSelectionText ] = TRUE; selectionText[ IMCompositionText ] = FALSE; commandHistory = new TQTextCommandHistory( 100 ); tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8; } TQTextDocument::~TQTextDocument() { delete commandHistory; if ( par ) par->removeChild( this ); clear(); delete flow_; if ( !par ) { delete pFormatter; delete fCollection; } delete pProcessor; delete buf_pixmap; delete indenter; delete backBrush; delete [] tArray; } void TQTextDocument::clear( bool createEmptyParag ) { while ( fParag ) { TQTextParagraph *p = fParag->next(); delete fParag; fParag = p; } if ( flow_ ) flow_->clear(); fParag = lParag = 0; if ( createEmptyParag ) fParag = lParag = createParagraph( this ); focusIndicator.parag = 0; selections.clear(); oText = TQString::null; oTextValid = FALSE; } int TQTextDocument::widthUsed() const { return wused + 2*border_tolerance; } int TQTextDocument::height() const { int h = 0; if ( lParag ) h = lParag->rect().top() + lParag->rect().height() + 1; int fh = flow_->boundingRect().bottom(); return TQMAX( h, fh ); } TQTextParagraph *TQTextDocument::createParagraph( TQTextDocument *d, TQTextParagraph *pr, TQTextParagraph *nx, bool updateIds ) { return new TQTextParagraph( d, pr, nx, updateIds ); } bool TQTextDocument::setMinimumWidth( int needed, int used, TQTextParagraph *p ) { if ( needed == -1 ) { minw = 0; wused = 0; p = 0; } if ( p == minwParag ) { if (minw > needed) { TQTextParagraph *tp = fParag; while (tp) { if (tp != p && tp->minwidth > needed) { needed = tp->minwidth; minwParag = tp; } tp = tp->n; } } minw = needed; emit minimumWidthChanged( minw ); } else if ( needed > minw ) { minw = needed; minwParag = p; emit minimumWidthChanged( minw ); } wused = TQMAX( wused, used ); wused = TQMAX( wused, minw ); cw = TQMAX( minw, cw ); return TRUE; } void TQTextDocument::setPlainText( const TQString &text ) { preferRichText = FALSE; clear(); oTextValid = TRUE; oText = text; int lastNl = 0; int nl = text.find( '\n' ); if ( nl == -1 ) { lParag = createParagraph( this, lParag, 0 ); if ( !fParag ) fParag = lParag; TQString s = text; if ( !s.isEmpty() ) { if ( s[ (int)s.length() - 1 ] == '\r' ) s.remove( s.length() - 1, 1 ); lParag->append( s ); } } else { for (;;) { lParag = createParagraph( this, lParag, 0 ); if ( !fParag ) fParag = lParag; int l = nl - lastNl; if ( l > 0 ) { if (text.tqunicode()[nl-1] == '\r') l--; TQConstString cs(text.tqunicode()+lastNl, l); lParag->append( cs.string() ); } if ( nl == (int)text.length() ) break; lastNl = nl + 1; nl = text.find( '\n', nl + 1 ); if ( nl == -1 ) nl = text.length(); } } if ( !lParag ) lParag = fParag = createParagraph( this, 0, 0 ); } struct TQ_EXPORT TQTextDocumentTag { TQTextDocumentTag(){} TQTextDocumentTag( const TQString&n, const TQStyleSheetItem* s, const TQTextFormat& f ) :name(n),style(s), format(f), tqalignment(TQt::AlignAuto), direction(TQChar::DirON),liststyle(TQStyleSheetItem::ListDisc) { wsm = TQStyleSheetItem::WhiteSpaceNormal; } TQString name; const TQStyleSheetItem* style; TQString anchorHref; TQStyleSheetItem::WhiteSpaceMode wsm; TQTextFormat format; int tqalignment : 16; int direction : 5; TQStyleSheetItem::ListStyle liststyle; TQTextDocumentTag( const TQTextDocumentTag& t ) { name = t.name; style = t.style; anchorHref = t.anchorHref; wsm = t.wsm; format = t.format; tqalignment = t.tqalignment; direction = t.direction; liststyle = t.liststyle; } TQTextDocumentTag& operator=(const TQTextDocumentTag& t) { name = t.name; style = t.style; anchorHref = t.anchorHref; wsm = t.wsm; format = t.format; tqalignment = t.tqalignment; direction = t.direction; liststyle = t.liststyle; return *this; } TQ_DUMMY_COMPARISON_OPERATOR(TQTextDocumentTag) }; #define NEWPAR do{ if ( !hasNewPar) { \ if ( !textEditMode && curpar && curpar->length()>1 && curpar->at( curpar->length()-2)->c == TQChar_linesep ) \ curpar->remove( curpar->length()-2, 1 ); \ curpar = createParagraph( this, curpar, curpar->next() ); styles.append( vec ); vec = 0;} \ hasNewPar = TRUE; \ curpar->rtext = TRUE; \ curpar->align = curtag.tqalignment; \ curpar->lstyle = curtag.liststyle; \ curpar->litem = ( curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem ); \ curpar->str->setDirection( (TQChar::Direction)curtag.direction ); \ space = TRUE; \ tabExpansionColumn = 0; \ delete vec; vec = new TQPtrVector( (uint)tags.count() + 1); \ int i = 0; \ for ( TQValueStack::Iterator it = tags.begin(); it != tags.end(); ++it ) \ vec->insert( i++, (*it).style ); \ vec->insert( i, curtag.style ); \ }while(FALSE); void TQTextDocument::setRichText( const TQString &text, const TQString &context, const TQTextFormat *initialFormat ) { preferRichText = TRUE; if ( !context.isEmpty() ) setContext( context ); clear(); fParag = lParag = createParagraph( this ); oTextValid = TRUE; oText = text; setRichTextInternal( text, 0, initialFormat ); fParag->rtext = TRUE; } void TQTextDocument::setRichTextInternal( const TQString &text, TQTextCursor* cursor, const TQTextFormat *initialFormat ) { TQTextParagraph* curpar = lParag; int pos = 0; TQValueStack tags; if ( !initialFormat ) initialFormat = formatCollection()->defaultFormat(); TQTextDocumentTag initag( "", sheet_->item(""), *initialFormat ); if ( bodyText.isValid() ) initag.format.setColor( bodyText ); TQTextDocumentTag curtag = initag; bool space = TRUE; bool canMergeLi = FALSE; bool textEditMode = FALSE; int tabExpansionColumn = 0; const TQChar* doc = text.tqunicode(); int length = text.length(); bool hasNewPar = curpar->length() <= 1; TQString anchorName; // style sheet handling for margin and line spacing calculation below TQTextParagraph* stylesPar = curpar; TQPtrVector* vec = 0; TQPtrList< TQPtrVector > styles; styles.setAutoDelete( TRUE ); if ( cursor ) { cursor->splitAndInsertEmptyParagraph(); TQTextCursor tmp = *cursor; tmp.gotoPreviousLetter(); stylesPar = curpar = tmp.paragraph(); hasNewPar = TRUE; textEditMode = TRUE; } else { NEWPAR; } // set rtext spacing to FALSE for the initial paragraph. curpar->rtext = FALSE; TQString wellKnownTags = "br hr wsp table qt body meta title"; while ( pos < length ) { if ( hasPrefix(doc, length, pos, '<' ) ){ if ( !hasPrefix( doc, length, pos+1, TQChar('/') ) ) { // open tag QMap attr; bool emptyTag = FALSE; TQString tagname = parseOpenTag(doc, length, pos, attr, emptyTag); if ( tagname.isEmpty() ) continue; // nothing we could do with this, probably parse error const TQStyleSheetItem* nstyle = sheet_->item(tagname); if ( nstyle ) { // we might have to close some 'forgotten' tags while ( !nstyle->allowedInContext( curtag.style ) ) { TQString msg; msg.sprintf( "TQText Warning: Document not valid ( '%s' not allowed in '%s' #%d)", tagname.ascii(), curtag.style->name().ascii(), pos); sheet_->error( msg ); if ( tags.isEmpty() ) break; curtag = tags.pop(); } /* special handling for p and li for HTML compatibility. We do not want to embed blocks in p, and we do not want new blocks inside non-empty lis. Plus we want to merge empty lis sometimes. */ if( nstyle->displayMode() == TQStyleSheetItem::DisplayListItem ) { canMergeLi = TRUE; } else if ( nstyle->displayMode() == TQStyleSheetItem::DisplayBlock ) { while ( curtag.style->name() == "p" ) { if ( tags.isEmpty() ) break; curtag = tags.pop(); } if ( curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem ) { // we are in a li and a new block comes along if ( nstyle->name() == "ul" || nstyle->name() == "ol" ) hasNewPar = FALSE; // we want an empty li (like most browsers) if ( !hasNewPar ) { /* do not add new blocks inside non-empty lis */ while ( curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem ) { if ( tags.isEmpty() ) break; curtag = tags.pop(); } } else if ( canMergeLi ) { /* we have an empty li and a block comes along, merge them */ nstyle = curtag.style; } canMergeLi = FALSE; } } } #ifndef TQT_NO_TEXTCUSTOMITEM TQTextCustomItem* custom = 0; #else bool custom = FALSE; #endif // some well-known tags, some have a nstyle, some not if ( wellKnownTags.find( tagname ) != -1 ) { if ( tagname == "br" ) { emptyTag = space = TRUE; int index = TQMAX( curpar->length(),1) - 1; TQTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); curpar->append( TQChar_linesep ); curpar->setFormat( index, 1, &format ); hasNewPar = false; } else if ( tagname == "hr" ) { emptyTag = space = TRUE; #ifndef TQT_NO_TEXTCUSTOMITEM custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this ); #endif } else if ( tagname == "table" ) { emptyTag = space = TRUE; #ifndef TQT_NO_TEXTCUSTOMITEM TQTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); curpar->tqsetAlignment( curtag.tqalignment ); custom = parseTable( attr, format, doc, length, pos, curpar ); #endif } else if ( tagname == "qt" || tagname == "body" ) { if ( attr.contains( "bgcolor" ) ) { TQBrush *b = new TQBrush( TQColor( attr["bgcolor"] ) ); setPaper( b ); } if ( attr.contains( "background" ) ) { #ifndef TQT_NO_MIME TQImage img; TQString bg = attr["background"]; const TQMimeSource* m = factory_->data( bg, contxt ); if ( !m ) { qWarning("TQRichText: no mimesource for %s", bg.latin1() ); } else { if ( !TQImageDrag::decode( m, img ) ) { qWarning("TQTextImage: cannot decode %s", bg.latin1() ); } } if ( !img.isNull() ) { TQBrush *b = new TQBrush( TQColor(), TQPixmap( img ) ); setPaper( b ); } #endif } if ( attr.contains( "text" ) ) { TQColor c( attr["text"] ); initag.format.setColor( c ); curtag.format.setColor( c ); bodyText = c; } if ( attr.contains( "link" ) ) linkColor = TQColor( attr["link"] ); if ( attr.contains( "title" ) ) attribs.replace( "title", attr["title"] ); if ( textEditMode ) { if ( attr.contains("style" ) ) { TQString a = attr["style"]; for ( int s = 0; s < a.contains(';')+1; s++ ) { TQString style = a.section( ';', s, s ); if ( style.startsWith("font-size:" ) && style.endsWith("pt") ) { scaleFontsFactor = double( formatCollection()->defaultFormat()->fn.pointSize() ) / style.mid( 10, style.length() - 12 ).toInt(); } } } nstyle = 0; // ignore body in textEditMode } // end qt- and body-tag handling } else if ( tagname == "meta" ) { if ( attr["name"] == "qrichtext" && attr["content"] == "1" ) textEditMode = TRUE; } else if ( tagname == "title" ) { TQString title; while ( pos < length ) { if ( hasPrefix( doc, length, pos, TQChar('<') ) && hasPrefix( doc, length, pos+1, TQChar('/') ) && parseCloseTag( doc, length, pos ) == "title" ) break; title += doc[ pos ]; ++pos; } attribs.replace( "title", title ); } } // end of well-known tag handling #ifndef TQT_NO_TEXTCUSTOMITEM if ( !custom ) // try generic custom item custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this ); #endif if ( !nstyle && !custom ) // we have no clue what this tag could be, ignore it continue; if ( custom ) { #ifndef TQT_NO_TEXTCUSTOMITEM int index = TQMAX( curpar->length(),1) - 1; TQTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); curpar->append( TQChar('*') ); TQTextFormat* f = formatCollection()->format( &format ); curpar->setFormat( index, 1, f ); curpar->at( index )->setCustomItem( custom ); if ( !curtag.anchorHref.isEmpty() ) curpar->at(index)->setAnchor( TQString::null, curtag.anchorHref ); if ( !anchorName.isEmpty() ) { curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() ); anchorName = TQString::null; } registerCustomItem( custom, curpar ); hasNewPar = FALSE; #endif } else if ( !emptyTag ) { /* if we do nesting, push curtag on the stack, otherwise reinint curag. */ if ( curtag.style->name() != tagname || nstyle->selfNesting() ) { tags.push( curtag ); } else { if ( !tags.isEmpty() ) curtag = tags.top(); else curtag = initag; } curtag.name = tagname; curtag.style = nstyle; curtag.name = tagname; curtag.style = nstyle; if ( nstyle->whiteSpaceMode() != TQStyleSheetItem::WhiteSpaceModeUndefined ) curtag.wsm = nstyle->whiteSpaceMode(); /* netscape compatibility: eat a newline and only a newline if a pre block starts */ if ( curtag.wsm == TQStyleSheetItem::WhiteSpacePre && nstyle->displayMode() == TQStyleSheetItem::DisplayBlock ) eat( doc, length, pos, '\n' ); /* ignore whitespace for inline elements if there was already one*/ if ( !textEditMode && (curtag.wsm == TQStyleSheetItem::WhiteSpaceNormal || curtag.wsm == TQStyleSheetItem::WhiteSpaceNoWrap) && ( space || nstyle->displayMode() != TQStyleSheetItem::DisplayInline ) ) eatSpace( doc, length, pos ); curtag.format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); if ( nstyle->isAnchor() ) { if ( !anchorName.isEmpty() ) anchorName += "#" + attr["name"]; else anchorName = attr["name"]; curtag.anchorHref = attr["href"]; } if ( nstyle->tqalignment() != TQStyleSheetItem::Undefined ) curtag.tqalignment = nstyle->tqalignment(); if ( nstyle->listStyle() != TQStyleSheetItem::ListStyleUndefined ) curtag.liststyle = nstyle->listStyle(); if ( nstyle->displayMode() == TQStyleSheetItem::DisplayBlock || nstyle->displayMode() == TQStyleSheetItem::DisplayListItem ) { if ( nstyle->name() == "ol" || nstyle->name() == "ul" || nstyle->name() == "li") { TQString type = attr["type"]; if ( !type.isEmpty() ) { if ( type == "1" ) { curtag.liststyle = TQStyleSheetItem::ListDecimal; } else if ( type == "a" ) { curtag.liststyle = TQStyleSheetItem::ListLowerAlpha; } else if ( type == "A" ) { curtag.liststyle = TQStyleSheetItem::ListUpperAlpha; } else { type = type.lower(); if ( type == "square" ) curtag.liststyle = TQStyleSheetItem::ListSquare; else if ( type == "disc" ) curtag.liststyle = TQStyleSheetItem::ListDisc; else if ( type == "circle" ) curtag.liststyle = TQStyleSheetItem::ListCircle; } } } /* Internally we treat ordered and bullet lists the same for margin calculations. In order to have fast pointer compares in the xMargin() functions we restrict ourselves to
    . Once we calculate the margins in the parser rathern than later, the unelegance of this approach goes awy */ if ( nstyle->name() == "ul" ) curtag.style = sheet_->item( "ol" ); if ( attr.contains( "align" ) ) { TQString align = attr["align"].lower(); if ( align == "center" ) curtag.tqalignment = TQt::AlignCenter; else if ( align == "right" ) curtag.tqalignment = TQt::AlignRight; else if ( align == "justify" ) curtag.tqalignment = TQt::AlignJustify; } if ( attr.contains( "dir" ) ) { TQString dir = attr["dir"]; if ( dir == "rtl" ) curtag.direction = TQChar::DirR; else if ( dir == "ltr" ) curtag.direction = TQChar::DirL; } NEWPAR; if ( curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem ) { if ( attr.contains( "value " ) ) curpar->setListValue( attr["value"].toInt() ); } if ( attr.contains( "style" ) ) { TQString a = attr["style"]; bool ok = TRUE; for ( int s = 0; ok && s < a.contains(';')+1; s++ ) { TQString style = a.section( ';', s, s ); if ( style.startsWith("margin-top:" ) && style.endsWith("px") ) curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok); else if ( style.startsWith("margin-bottom:" ) && style.endsWith("px") ) curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok); else if ( style.startsWith("margin-left:" ) && style.endsWith("px") ) curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok); else if ( style.startsWith("margin-right:" ) && style.endsWith("px") ) curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok); else if ( style.startsWith("text-indent:" ) && style.endsWith("px") ) curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok); } if ( !ok ) // be pressmistic curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0; } } } } else { TQString tagname = parseCloseTag( doc, length, pos ); if ( tagname.isEmpty() ) continue; // nothing we could do with this, probably parse error if ( !sheet_->item( tagname ) ) // ignore unknown tags continue; if ( tagname == "li" ) continue; // we close a block item. Since the text may continue, we need to have a new paragraph bool needNewPar = curtag.style->displayMode() == TQStyleSheetItem::DisplayBlock || curtag.style->displayMode() == TQStyleSheetItem::DisplayListItem; // html slopiness: handle unbalanched tag closing while ( curtag.name != tagname ) { TQString msg; msg.sprintf( "TQText Warning: Document not valid ( '%s' not closed before '%s' #%d)", curtag.name.ascii(), tagname.ascii(), pos); sheet_->error( msg ); if ( tags.isEmpty() ) break; curtag = tags.pop(); } // close the tag if ( !tags.isEmpty() ) curtag = tags.pop(); else curtag = initag; if ( needNewPar ) { if ( textEditMode && (tagname == "p" || tagname == "div" ) ) // preserve empty paragraphs hasNewPar = FALSE; NEWPAR; } } } else { // normal contents TQString s; TQChar c; while ( pos < length && !hasPrefix(doc, length, pos, TQChar('<') ) ){ if ( textEditMode ) { // text edit mode: we handle all white space but ignore newlines c = parseChar( doc, length, pos, TQStyleSheetItem::WhiteSpacePre ); if ( c == TQChar_linesep ) break; } else { int l = pos; c = parseChar( doc, length, pos, curtag.wsm ); // in white space pre mode: treat any space as non breakable // and expand tabs to eight character wide columns. if ( curtag.wsm == TQStyleSheetItem::WhiteSpacePre ) { if ( c == '\t' ) { c = ' '; while( (++tabExpansionColumn)%8 ) s += c; } if ( c == TQChar_linesep ) tabExpansionColumn = 0; else tabExpansionColumn++; } if ( c == ' ' || c == TQChar_linesep ) { /* avoid overlong paragraphs by forcing a new paragraph after 4096 characters. This case can occur when loading undiscovered plain text documents in rich text mode. Instead of hanging forever, we do the trick. */ if ( curtag.wsm == TQStyleSheetItem::WhiteSpaceNormal && s.length() > 4096 ) do { if ( doc[l] == '\n' ) { hasNewPar = FALSE; // for a new paragraph ... NEWPAR; hasNewPar = FALSE; // ... and make it non-reusable c = '\n'; // make sure we break below break; } } while ( ++l < pos ); } } if ( c == '\n' ) break; // break on newlines, pre delievers a TQChar_linesep bool c_isSpace = c.isSpace() && c.tqunicode() != 0x00a0U && !textEditMode; if ( curtag.wsm == TQStyleSheetItem::WhiteSpaceNormal && c_isSpace && space ) continue; if ( c == '\r' ) continue; space = c_isSpace; s += c; } if ( !s.isEmpty() && curtag.style->displayMode() != TQStyleSheetItem::DisplayNone ) { hasNewPar = FALSE; int index = TQMAX( curpar->length(),1) - 1; curpar->append( s ); if (curtag.wsm != TQStyleSheetItem::WhiteSpaceNormal) { TQTextString *str = curpar->string(); for (int i = index; i < index + s.length(); ++i) str->at(i).nobreak = TRUE; } TQTextFormat* f = formatCollection()->format( &curtag.format ); curpar->setFormat( index, s.length(), f, FALSE ); // do not use collection because we have done that already f->ref += s.length() -1; // that what friends are for... if ( !curtag.anchorHref.isEmpty() ) { for ( int i = 0; i < int(s.length()); i++ ) curpar->at(index + i)->setAnchor( TQString::null, curtag.anchorHref ); } if ( !anchorName.isEmpty() ) { for ( int i = 0; i < int(s.length()); i++ ) curpar->at(index + i)->setAnchor( anchorName, curpar->at(index + i)->anchorHref() ); anchorName = TQString::null; } } } } if ( hasNewPar && curpar != fParag && !cursor && stylesPar != curpar ) { // cleanup unused last paragraphs curpar = curpar->p; delete curpar->n; } if ( !anchorName.isEmpty() ) { curpar->at(curpar->length() - 1)->setAnchor( anchorName, curpar->at( curpar->length() - 1 )->anchorHref() ); anchorName = TQString::null; } setRichTextMarginsInternal( styles, stylesPar ); if ( cursor ) { cursor->gotoPreviousLetter(); cursor->remove(); } delete vec; } void TQTextDocument::setRichTextMarginsInternal( TQPtrList< TQPtrVector >& styles, TQTextParagraph* stylesPar ) { // margin and line spacing calculation TQPtrVector* prevStyle = 0; TQPtrVector* curStyle = styles.first(); TQPtrVector* nextStyle = styles.next(); while ( stylesPar ) { if ( !curStyle ) { stylesPar = stylesPar->next(); prevStyle = curStyle; curStyle = nextStyle; nextStyle = styles.next(); continue; } int i, mar; TQStyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0; if ( mainStyle && mainStyle->displayMode() == TQStyleSheetItem::DisplayListItem ) stylesPar->setListItem( TRUE ); int numLists = 0; for ( i = 0; i < (int)curStyle->size(); ++i ) { if ( (*curStyle)[ i ]->displayMode() == TQStyleSheetItem::DisplayBlock && (*curStyle)[ i ]->listStyle() != TQStyleSheetItem::ListStyleUndefined ) numLists++; } stylesPar->ldepth = numLists; if ( stylesPar->next() && nextStyle ) { // also set the depth of the next paragraph, required for the margin calculation numLists = 0; for ( i = 0; i < (int)nextStyle->size(); ++i ) { if ( (*nextStyle)[ i ]->displayMode() == TQStyleSheetItem::DisplayBlock && (*nextStyle)[ i ]->listStyle() != TQStyleSheetItem::ListStyleUndefined ) numLists++; } stylesPar->next()->ldepth = numLists; } // do the top margin TQStyleSheetItem* item = mainStyle; int m; if (stylesPar->utm > 0 ) { m = stylesPar->utm-1; stylesPar->utm = 0; } else { m = TQMAX(0, item->margin( TQStyleSheetItem::MarginTop ) ); if ( stylesPar->ldepth ) { if ( item->displayMode() == TQStyleSheetItem::DisplayListItem ) m /= stylesPar->ldepth * stylesPar->ldepth; else m = 0; } } for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { item = (*curStyle)[ i ]; if ( prevStyle && i < (int) prevStyle->size() && ( item->displayMode() == TQStyleSheetItem::DisplayBlock && (*prevStyle)[ i ] == item ) ) break; // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags if ( item->listStyle() != TQStyleSheetItem::ListStyleUndefined && ( ( i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) ) continue; mar = TQMAX( 0, item->margin( TQStyleSheetItem::MarginTop ) ); m = TQMAX( m, mar ); } stylesPar->utm = m - stylesPar->topMargin(); // do the bottom margin item = mainStyle; if (stylesPar->ubm > 0 ) { m = stylesPar->ubm-1; stylesPar->ubm = 0; } else { m = TQMAX(0, item->margin( TQStyleSheetItem::MarginBottom ) ); if ( stylesPar->ldepth ) { if ( item->displayMode() == TQStyleSheetItem::DisplayListItem ) m /= stylesPar->ldepth * stylesPar->ldepth; else m = 0; } } for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { item = (*curStyle)[ i ]; if ( nextStyle && i < (int) nextStyle->size() && ( item->displayMode() == TQStyleSheetItem::DisplayBlock && (*nextStyle)[ i ] == item ) ) break; // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags if ( item->listStyle() != TQStyleSheetItem::ListStyleUndefined && ( ( i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) ) continue; mar = TQMAX(0, item->margin( TQStyleSheetItem::MarginBottom ) ); m = TQMAX( m, mar ); } stylesPar->ubm = m - stylesPar->bottomMargin(); // do the left margin, simplyfied item = mainStyle; if (stylesPar->ulm > 0 ) { m = stylesPar->ulm-1; stylesPar->ulm = 0; } else { m = TQMAX( 0, item->margin( TQStyleSheetItem::MarginLeft ) ); } for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { item = (*curStyle)[ i ]; m += TQMAX( 0, item->margin( TQStyleSheetItem::MarginLeft ) ); } stylesPar->ulm = m - stylesPar->leftMargin(); // do the right margin, simplyfied item = mainStyle; if (stylesPar->urm > 0 ) { m = stylesPar->urm-1; stylesPar->urm = 0; } else { m = TQMAX( 0, item->margin( TQStyleSheetItem::MarginRight ) ); } for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { item = (*curStyle)[ i ]; m += TQMAX( 0, item->margin( TQStyleSheetItem::MarginRight ) ); } stylesPar->urm = m - stylesPar->rightMargin(); // do the first line margin, which really should be called text-indent item = mainStyle; if (stylesPar->uflm > 0 ) { m = stylesPar->uflm-1; stylesPar->uflm = 0; } else { m = TQMAX( 0, item->margin( TQStyleSheetItem::MarginFirstLine ) ); } for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { item = (*curStyle)[ i ]; mar = TQMAX( 0, item->margin( TQStyleSheetItem::MarginFirstLine ) ); m = TQMAX( m, mar ); } stylesPar->uflm =m - stylesPar->firstLineMargin(); // do the bogus line "spacing", which really is just an extra margin item = mainStyle; for ( i = (int)curStyle->size() - 1 ; i >= 0; --i ) { item = (*curStyle)[ i ]; if ( item->lineSpacing() != TQStyleSheetItem::Undefined ) { stylesPar->ulinespacing = item->lineSpacing(); if ( formatCollection() && stylesPar->ulinespacing < formatCollection()->defaultFormat()->height() ) stylesPar->ulinespacing += formatCollection()->defaultFormat()->height(); break; } } stylesPar = stylesPar->next(); prevStyle = curStyle; curStyle = nextStyle; nextStyle = styles.next(); } } void TQTextDocument::setText( const TQString &text, const TQString &context ) { focusIndicator.parag = 0; selections.clear(); if ( ((txtFormat == TQt::AutoText) && (TQStyleSheet::mightBeRichText( text ))) || (txtFormat == TQt::RichText) ) setRichText( text, context ); else setPlainText( text ); } TQString TQTextDocument::plainText() const { TQString buffer; TQString s; TQTextParagraph *p = fParag; while ( p ) { if ( !p->mightHaveCustomItems ) { const TQTextString *ts = p->string(); // workaround VC++ and Borland s = ts->toString(); // with FALSE we don't fix spaces (nbsp) } else { for ( int i = 0; i < p->length() - 1; ++i ) { #ifndef TQT_NO_TEXTCUSTOMITEM if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { s += "\n"; TQTextTable *t = (TQTextTable*)p->at( i )->customItem(); TQPtrList cells = t->tableCells(); for ( TQTextTableCell *c = cells.first(); c; c = cells.next() ) s += c->richText()->plainText() + "\n"; s += "\n"; } } else #endif { s += p->at( i )->c; } } } s.remove( s.length() - 1, 1 ); if ( p->next() ) s += "\n"; buffer += s; p = p->next(); } return buffer; } static TQString align_to_string( int a ) { if ( a & TQt::AlignRight ) return " align=\"right\""; if ( a & TQt::AlignHCenter ) return " align=\"center\""; if ( a & TQt::AlignJustify ) return " align=\"justify\""; return TQString::null; } static TQString direction_to_string( int d ) { if ( d != TQChar::DirON ) return ( d == TQChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"" ); return TQString::null; } static TQString list_value_to_string( int v ) { if ( v != -1 ) return " listvalue=\"" + TQString::number( v ) + "\""; return TQString::null; } static TQString list_style_to_string( int v ) { switch( v ) { case TQStyleSheetItem::ListDecimal: return "\"1\""; case TQStyleSheetItem::ListLowerAlpha: return "\"a\""; case TQStyleSheetItem::ListUpperAlpha: return "\"A\""; case TQStyleSheetItem::ListDisc: return "\"disc\""; case TQStyleSheetItem::ListSquare: return "\"square\""; case TQStyleSheetItem::ListCircle: return "\"circle\""; default: return TQString::null; } } static inline bool list_is_ordered( int v ) { return v == TQStyleSheetItem::ListDecimal || v == TQStyleSheetItem::ListLowerAlpha || v == TQStyleSheetItem::ListUpperAlpha; } static TQString margin_to_string( TQStyleSheetItem* style, int t, int b, int l, int r, int fl ) { TQString s; if ( l > 0 ) s += TQString(!!s?";":"") + "margin-left:" + TQString::number(l+TQMAX(0,style->margin(TQStyleSheetItem::MarginLeft))) + "px"; if ( r > 0 ) s += TQString(!!s?";":"") + "margin-right:" + TQString::number(r+TQMAX(0,style->margin(TQStyleSheetItem::MarginRight))) + "px"; if ( t > 0 ) s += TQString(!!s?";":"") + "margin-top:" + TQString::number(t+TQMAX(0,style->margin(TQStyleSheetItem::MarginTop))) + "px"; if ( b > 0 ) s += TQString(!!s?";":"") + "margin-bottom:" + TQString::number(b+TQMAX(0,style->margin(TQStyleSheetItem::MarginBottom))) + "px"; if ( fl > 0 ) s += TQString(!!s?";":"") + "text-indent:" + TQString::number(fl+TQMAX(0,style->margin(TQStyleSheetItem::MarginFirstLine))) + "px"; if ( !!s ) return " style=\"" + s + "\""; return TQString::null; } TQString TQTextDocument::richText() const { TQString s = ""; if ( !par ) { s += "defaultFormat()->font().pointSize() ); s += "pt;font-family:"; s += formatCollection()->defaultFormat()->font().family(); s +="\">"; } TQTextParagraph* p = fParag; TQStyleSheetItem* item_p = styleSheet()->item("p"); TQStyleSheetItem* item_div = styleSheet()->item("div"); TQStyleSheetItem* item_ul = styleSheet()->item("ul"); TQStyleSheetItem* item_ol = styleSheet()->item("ol"); TQStyleSheetItem* item_li = styleSheet()->item("li"); if ( !item_p || !item_div || !item_ul || !item_ol || !item_li ) { qWarning( "TQTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)" ); return TQString::null; } int pastListDepth = 0; int listDepth = 0; #if 0 int futureListDepth = 0; #endif TQMemArray listStyles(10); while ( p ) { listDepth = p->listDepth(); if ( listDepth < pastListDepth ) { for ( int i = pastListDepth; i > listDepth; i-- ) s += list_is_ordered( listStyles[i] ) ? "
" : ""; s += '\n'; } else if ( listDepth > pastListDepth ) { s += '\n'; listStyles.resize( TQMAX( (int)listStyles.size(), listDepth+1 ) ); TQString list_type; listStyles[listDepth] = p->listStyle(); if ( !list_is_ordered( p->listStyle() ) || item_ol->listStyle() != p->listStyle() ) list_type = " type=" + list_style_to_string( p->listStyle() ); for ( int i = pastListDepth; i < listDepth; i++ ) { s += list_is_ordered( p->listStyle() ) ? ""; } } else { s += '\n'; } TQString ps = p->richText(); #if 0 // for the bottom margin we need to know whether we are at the end of a list futureListDepth = 0; if ( listDepth > 0 && p->next() ) futureListDepth = p->next()->listDepth(); #endif if ( richTextExportStart && richTextExportStart->paragraph() ==p && richTextExportStart->index() == 0 ) s += ""; if ( p->isListItem() ) { s += "listStyle() != listStyles[listDepth] ) s += " type=" + list_style_to_string( p->listStyle() ); s +=align_to_string( p->tqalignment() ); s += margin_to_string( item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm ); s += list_value_to_string( p->listValue() ); s += direction_to_string( p->direction() ); s +=">"; s += ps; s += ""; } else if ( p->listDepth() ) { s += "tqalignment() ); s += margin_to_string( item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm ); s +=direction_to_string( p->direction() ); s += ">"; s += ps; s += ""; } else { // normal paragraph item s += "tqalignment() ); s += margin_to_string( item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm ); s +=direction_to_string( p->direction() ); s += ">"; s += ps; s += "

"; } pastListDepth = listDepth; p = p->next(); } while ( listDepth > 0 ) { s += list_is_ordered( listStyles[listDepth] ) ? "" : ""; listDepth--; } if ( !par ) s += "\n\n"; return s; } TQString TQTextDocument::text() const { if ( ((txtFormat == TQt::AutoText) && preferRichText) || (txtFormat == TQt::RichText) ) return richText(); return plainText(); } TQString TQTextDocument::text( int parag ) const { TQTextParagraph *p = paragAt( parag ); if ( !p ) return TQString::null; if ( ((txtFormat == TQt::AutoText) && preferRichText) || (txtFormat == TQt::RichText) ) return p->richText(); else return p->string()->toString(); } void TQTextDocument::tqinvalidate() { TQTextParagraph *s = fParag; while ( s ) { s->tqinvalidate( 0 ); s = s->next(); } } void TQTextDocument::selectionStart( int id, int ¶gId, int &index ) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return; TQTextDocumentSelection &sel = *it; paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId(); index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index(); } TQTextCursor TQTextDocument::selectionStartCursor( int id) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return TQTextCursor( this ); TQTextDocumentSelection &sel = *it; if ( sel.swapped ) return sel.endCursor; return sel.startCursor; } TQTextCursor TQTextDocument::selectionEndCursor( int id) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return TQTextCursor( this ); TQTextDocumentSelection &sel = *it; if ( !sel.swapped ) return sel.endCursor; return sel.startCursor; } void TQTextDocument::selectionEnd( int id, int ¶gId, int &index ) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return; TQTextDocumentSelection &sel = *it; paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId(); index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index(); } void TQTextDocument::addSelection( int id ) { nSelections = TQMAX( nSelections, id + 1 ); } static void setSelectionEndHelper( int id, TQTextDocumentSelection &sel, TQTextCursor &start, TQTextCursor &end ) { TQTextCursor c1 = start; TQTextCursor c2 = end; if ( sel.swapped ) { c1 = end; c2 = start; } c1.paragraph()->removeSelection( id ); c2.paragraph()->removeSelection( id ); if ( c1.paragraph() != c2.paragraph() ) { c1.paragraph()->setSelection( id, c1.index(), c1.paragraph()->length() - 1 ); c2.paragraph()->setSelection( id, 0, c2.index() ); } else { c1.paragraph()->setSelection( id, TQMIN( c1.index(), c2.index() ), TQMAX( c1.index(), c2.index() ) ); } sel.startCursor = start; sel.endCursor = end; if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() ) sel.swapped = sel.startCursor.index() > sel.endCursor.index(); } bool TQTextDocument::setSelectionEnd( int id, const TQTextCursor &cursor ) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return FALSE; TQTextDocumentSelection &sel = *it; TQTextCursor start = sel.startCursor; TQTextCursor end = cursor; if ( start == end ) { removeSelection( id ); setSelectionStart( id, cursor ); return TRUE; } if ( sel.endCursor.paragraph() == end.paragraph() ) { setSelectionEndHelper( id, sel, start, end ); return TRUE; } bool inSelection = FALSE; TQTextCursor c( this ); TQTextCursor tmp = sel.startCursor; if ( sel.swapped ) tmp = sel.endCursor; tmp.restoreState(); TQTextCursor tmp2 = cursor; tmp2.restoreState(); c.setParagraph( tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph() ); bool hadStart = FALSE; bool hadEnd = FALSE; bool hadStartParag = FALSE; bool hadEndParag = FALSE; bool hadOldStart = FALSE; bool hadOldEnd = FALSE; bool leftSelection = FALSE; sel.swapped = FALSE; for ( ;; ) { if ( c == start ) hadStart = TRUE; if ( c == end ) hadEnd = TRUE; if ( c.paragraph() == start.paragraph() ) hadStartParag = TRUE; if ( c.paragraph() == end.paragraph() ) hadEndParag = TRUE; if ( c == sel.startCursor ) hadOldStart = TRUE; if ( c == sel.endCursor ) hadOldEnd = TRUE; if ( !sel.swapped && ( (hadEnd && !hadStart) || (hadEnd && hadStart && (start.paragraph() == end.paragraph()) && start.index() > end.index()) ) ) sel.swapped = TRUE; if ( ((c == end) && hadStartParag) || ((c == start) && hadEndParag )) { TQTextCursor tmp = c; tmp.restoreState(); if ( tmp.paragraph() != c.paragraph() ) { int sstart = tmp.paragraph()->selectionStart( id ); tmp.paragraph()->removeSelection( id ); tmp.paragraph()->setSelection( id, sstart, tmp.index() ); } } if ( inSelection && ( ((c == end) && hadStart) || ((c == start) && hadEnd) ) ) leftSelection = TRUE; else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) ) inSelection = TRUE; bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection( id ) && c.atParagEnd(); c.paragraph()->removeSelection( id ); if ( inSelection ) { if ( c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph() ) { c.paragraph()->setSelection( id, TQMIN( start.index(), end.index() ), TQMAX( start.index(), end.index() ) ); } else if ( c.paragraph() == start.paragraph() && !hadEndParag ) { c.paragraph()->setSelection( id, start.index(), c.paragraph()->length() - 1 ); } else if ( c.paragraph() == end.paragraph() && !hadStartParag ) { c.paragraph()->setSelection( id, end.index(), c.paragraph()->length() - 1 ); } else if ( c.paragraph() == end.paragraph() && hadEndParag ) { c.paragraph()->setSelection( id, 0, end.index() ); } else if ( c.paragraph() == start.paragraph() && hadStartParag ) { c.paragraph()->setSelection( id, 0, start.index() ); } else { c.paragraph()->setSelection( id, 0, c.paragraph()->length() - 1 ); } } if ( leftSelection ) inSelection = FALSE; if ( noSelectionAnymore ) break; // *ugle*hack optimization TQTextParagraph *p = c.paragraph(); if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph() ) { c.gotoNextLetter(); if ( p == lastParagraph() && c.atParagEnd() ) break; } else { if ( p->document()->tqparent() ) do { c.gotoNextLetter(); } while ( c.paragraph() == p ); else c.setParagraph( p->next() ); } } if ( !sel.swapped ) sel.startCursor.paragraph()->setSelection( id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1 ); sel.startCursor = start; sel.endCursor = end; if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() ) sel.swapped = sel.startCursor.index() > sel.endCursor.index(); setSelectionEndHelper( id, sel, start, end ); return TRUE; } void TQTextDocument::selectAll( int id ) { removeSelection( id ); TQTextDocumentSelection sel; sel.swapped = FALSE; TQTextCursor c( this ); c.setParagraph( fParag ); c.setIndex( 0 ); sel.startCursor = c; c.setParagraph( lParag ); c.setIndex( lParag->length() - 1 ); sel.endCursor = c; selections.insert( id, sel ); TQTextParagraph *p = fParag; while ( p ) { p->setSelection( id, 0, p->length() - 1 ); p = p->next(); } for ( TQTextDocument *d = childList.first(); d; d = childList.next() ) d->selectAll( id ); } bool TQTextDocument::removeSelection( int id ) { if ( !selections.contains( id ) ) return FALSE; TQTextDocumentSelection &sel = selections[ id ]; TQTextCursor start = sel.swapped ? sel.endCursor : sel.startCursor; TQTextCursor end = sel.swapped ? sel.startCursor : sel.endCursor; TQTextParagraph* p = 0; while ( start != end ) { if ( p != start.paragraph() ) { p = start.paragraph(); p->removeSelection( id ); //### avoid endless loop by all means necessary, did somebody mention refactoring? if ( !tqparent() && p == lParag ) break; } start.gotoNextLetter(); } p = start.paragraph(); p->removeSelection( id ); selections.remove( id ); return TRUE; } TQString TQTextDocument::selectedText( int id, bool asRichText ) const { QMap::ConstIterator it = selections.find( id ); if ( it == selections.end() ) return TQString::null; TQTextDocumentSelection sel = *it; TQTextCursor c1 = sel.startCursor; TQTextCursor c2 = sel.endCursor; if ( sel.swapped ) { c2 = sel.startCursor; c1 = sel.endCursor; } /* 3.0.3 improvement: Make it possible to get a reasonable selection inside a table. This approach is very conservative: make sure that both cursors have the same depth level and point to paragraphs within the same text document. Meaning if you select text in two table cells, you will get the entire table. This is still far better than the 3.0.2, where you always got the entire table. ### Fix this properly when refactoring */ while ( c2.nestedDepth() > c1.nestedDepth() ) c2.oneUp(); while ( c1.nestedDepth() > c2.nestedDepth() ) c1.oneUp(); while ( c1.nestedDepth() && c2.nestedDepth() && c1.paragraph()->document() != c2.paragraph()->document() ) { c1.oneUp(); c2.oneUp(); } // do not trust sel_swapped with tables. Fix this properly when refactoring as well if ( c1.paragraph()->paragId() > c2.paragraph()->paragId() || (c1.paragraph() == c2.paragraph() && c1.index() > c2.index() ) ) { TQTextCursor tmp = c1; c2 = c1; c1 = tmp; } // end selection 3.0.3 improvement if ( asRichText && !tqparent() ) { richTextExportStart = &c1; richTextExportEnd = &c2; TQString sel = richText(); int from = sel.find( "" ); if ( from >= 0 ) { from += 20; // find the previous span and move it into the start fragment before we clip it TQString prevspan; int pspan = sel.findRev( " sel.findRev( "', pspan ); prevspan = sel.mid( pspan, spanend - pspan + 1 ); } int to = sel.findRev( "" ); if ( from <= to ) sel = "" + prevspan + sel.mid( from, to - from ); } richTextExportStart = richTextExportEnd = 0; return sel; } TQString s; if ( c1.paragraph() == c2.paragraph() ) { TQTextParagraph *p = c1.paragraph(); int end = c2.index(); if ( p->at( TQMAX( 0, end - 1 ) )->isCustom() ) ++end; if ( !p->mightHaveCustomItems ) { s += p->string()->toString().mid( c1.index(), end - c1.index() ); } else { for ( int i = c1.index(); i < end; ++i ) { #ifndef TQT_NO_TEXTCUSTOMITEM if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { s += "\n"; TQTextTable *t = (TQTextTable*)p->at( i )->customItem(); TQPtrList cells = t->tableCells(); for ( TQTextTableCell *c = cells.first(); c; c = cells.next() ) s += c->richText()->plainText() + "\n"; s += "\n"; } } else #endif { s += p->at( i )->c; } } } } else { TQTextParagraph *p = c1.paragraph(); int start = c1.index(); while ( p ) { int end = p == c2.paragraph() ? c2.index() : p->length() - 1; if ( p == c2.paragraph() && p->at( TQMAX( 0, end - 1 ) )->isCustom() ) ++end; if ( !p->mightHaveCustomItems ) { s += p->string()->toString().mid( start, end - start ); if ( p != c2.paragraph() ) s += "\n"; } else { for ( int i = start; i < end; ++i ) { #ifndef TQT_NO_TEXTCUSTOMITEM if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { s += "\n"; TQTextTable *t = (TQTextTable*)p->at( i )->customItem(); TQPtrList cells = t->tableCells(); for ( TQTextTableCell *c = cells.first(); c; c = cells.next() ) s += c->richText()->plainText() + "\n"; s += "\n"; } } else #endif { s += p->at( i )->c; } } } start = 0; if ( p == c2.paragraph() ) break; p = p->next(); } } // ### workaround for plain text export until we get proper // mime types: turn tqunicode line seperators into the more // widely understood \n. Makes copy and pasting code snipplets // from within Assistent possible TQChar* uc = (TQChar*) s.tqunicode(); for ( int ii = 0; ii < s.length(); ii++ ) { if ( uc[(int)ii] == TQChar_linesep ) uc[(int)ii] = TQChar('\n'); else if ( uc[(int)ii] == TQChar::nbsp ) uc[(int)ii] = TQChar(' '); } return s; } void TQTextDocument::setFormat( int id, TQTextFormat *f, int flags ) { QMap::ConstIterator it = selections.find( id ); if ( it == selections.end() ) return; TQTextDocumentSelection sel = *it; TQTextCursor c1 = sel.startCursor; TQTextCursor c2 = sel.endCursor; if ( sel.swapped ) { c2 = sel.startCursor; c1 = sel.endCursor; } c2.restoreState(); c1.restoreState(); if ( c1.paragraph() == c2.paragraph() ) { c1.paragraph()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags ); return; } c1.paragraph()->setFormat( c1.index(), c1.paragraph()->length() - c1.index(), f, TRUE, flags ); TQTextParagraph *p = c1.paragraph()->next(); while ( p && p != c2.paragraph() ) { p->setFormat( 0, p->length(), f, TRUE, flags ); p = p->next(); } c2.paragraph()->setFormat( 0, c2.index(), f, TRUE, flags ); } void TQTextDocument::removeSelectedText( int id, TQTextCursor *cursor ) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return; TQTextDocumentSelection sel = *it; TQTextCursor c1 = sel.startCursor; TQTextCursor c2 = sel.endCursor; if ( sel.swapped ) { c2 = sel.startCursor; c1 = sel.endCursor; } // ### no support for editing tables yet if ( c1.nestedDepth() || c2.nestedDepth() ) return; c2.restoreState(); c1.restoreState(); *cursor = c1; removeSelection( id ); if ( c1.paragraph() == c2.paragraph() ) { c1.paragraph()->remove( c1.index(), c2.index() - c1.index() ); return; } if ( c1.paragraph() == fParag && c1.index() == 0 && c2.paragraph() == lParag && c2.index() == lParag->length() - 1 ) cursor->setValid( FALSE ); bool didGoLeft = FALSE; if ( c1.index() == 0 && c1.paragraph() != fParag ) { cursor->gotoPreviousLetter(); didGoLeft = cursor->isValid(); } c1.paragraph()->remove( c1.index(), c1.paragraph()->length() - 1 - c1.index() ); TQTextParagraph *p = c1.paragraph()->next(); int dy = 0; TQTextParagraph *tmp; while ( p && p != c2.paragraph() ) { tmp = p->next(); dy -= p->rect().height(); delete p; p = tmp; } c2.paragraph()->remove( 0, c2.index() ); while ( p ) { p->move( dy ); p->tqinvalidate( 0 ); p->setEndState( -1 ); p = p->next(); } c1.paragraph()->join( c2.paragraph() ); if ( didGoLeft ) cursor->gotoNextLetter(); } void TQTextDocument::indentSelection( int id ) { QMap::Iterator it = selections.find( id ); if ( it == selections.end() ) return; TQTextDocumentSelection sel = *it; TQTextParagraph *startParag = sel.startCursor.paragraph(); TQTextParagraph *endParag = sel.endCursor.paragraph(); if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) { endParag = sel.startCursor.paragraph(); startParag = sel.endCursor.paragraph(); } TQTextParagraph *p = startParag; while ( p && p != endParag ) { p->indent(); p = p->next(); } } void TQTextDocument::addCommand( TQTextCommand *cmd ) { commandHistory->addCommand( cmd ); } TQTextCursor *TQTextDocument::undo( TQTextCursor *c ) { return commandHistory->undo( c ); } TQTextCursor *TQTextDocument::redo( TQTextCursor *c ) { return commandHistory->redo( c ); } bool TQTextDocument::find( TQTextCursor& cursor, const TQString &expr, bool cs, bool wo, bool forward ) { removeSelection( Standard ); TQTextParagraph *p = 0; if ( expr.isEmpty() ) return FALSE; for (;;) { if ( p != cursor.paragraph() ) { p = cursor.paragraph(); TQString s = cursor.paragraph()->string()->toString(); int start = cursor.index(); for ( ;; ) { int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs ); int end = res + expr.length(); if ( res == -1 || ( !forward && start <= res ) ) break; if ( !wo || ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) && ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) ) ) { removeSelection( Standard ); cursor.setIndex( forward ? end : res ); setSelectionStart( Standard, cursor ); cursor.setIndex( forward ? res : end ); setSelectionEnd( Standard, cursor ); if ( !forward ) cursor.setIndex( res ); return TRUE; } start = res + (forward ? 1 : -1); } } if ( forward ) { if ( cursor.paragraph() == lastParagraph() && cursor.atParagEnd() ) break; cursor.gotoNextLetter(); } else { if ( cursor.paragraph() == firstParagraph() && cursor.atParagStart() ) break; cursor.gotoPreviousLetter(); } } return FALSE; } void TQTextDocument::setTextFormat( TQt::TextFormat f ) { txtFormat = f; if ( fParag == lParag && fParag->length() <= 1 ) fParag->rtext = ( f == TQt::RichText ); } TQt::TextFormat TQTextDocument::textFormat() const { return txtFormat; } bool TQTextDocument::inSelection( int selId, const TQPoint &pos ) const { QMap::ConstIterator it = selections.find( selId ); if ( it == selections.end() ) return FALSE; TQTextDocumentSelection sel = *it; TQTextParagraph *startParag = sel.startCursor.paragraph(); TQTextParagraph *endParag = sel.endCursor.paragraph(); if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() && sel.startCursor.paragraph()->selectionStart( selId ) == sel.endCursor.paragraph()->selectionEnd( selId ) ) return FALSE; if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) { endParag = sel.startCursor.paragraph(); startParag = sel.endCursor.paragraph(); } TQTextParagraph *p = startParag; while ( p ) { if ( p->rect().contains( pos ) ) { bool inSel = FALSE; int selStart = p->selectionStart( selId ); int selEnd = p->selectionEnd( selId ); int y = 0; int h = 0; for ( int i = 0; i < p->length(); ++i ) { if ( i == selStart ) inSel = TRUE; if ( i == selEnd ) break; if ( p->at( i )->lineStart ) { y = (*p->lineStarts.find( i ))->y; h = (*p->lineStarts.find( i ))->h; } if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) { if ( inSel && pos.x() >= p->at( i )->x && pos.x() <= p->at( i )->x + p->at( i )->format()->width( p->at( i )->c ) ) return TRUE; } } } if ( pos.y() < p->rect().y() ) break; if ( p == endParag ) break; p = p->next(); } return FALSE; } void TQTextDocument::doLayout( TQPainter *p, int w ) { minw = wused = 0; if ( !is_printer( p ) ) p = 0; withoutDoubleBuffer = ( p != 0 ); TQPainter * oldPainter = TQTextFormat::painter(); TQTextFormat::setPainter( p ); tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8; flow_->setWidth( w ); cw = w; vw = w; TQTextParagraph *parag = fParag; while ( parag ) { parag->tqinvalidate( 0 ); if ( p ) parag->adjustToPainter( p ); parag->format(); parag = parag->next(); } TQTextFormat::setPainter( oldPainter ); } TQPixmap *TQTextDocument::bufferPixmap( const TQSize &s ) { if ( !buf_pixmap ) buf_pixmap = new TQPixmap( s.expandedTo( TQSize(1,1) ) ); else if ( buf_pixmap->size() != s ) buf_pixmap->resize( s.expandedTo( buf_pixmap->size() ) ); return buf_pixmap; } void TQTextDocument::draw( TQPainter *p, const TQRect &rect, const TQColorGroup &cg, const TQBrush *paper ) { if ( !firstParagraph() ) return; if ( paper ) { p->setBrushOrigin( -int( p->translationX() ), -int( p->translationY() ) ); p->fillRect( rect, *paper ); } TQPainter * oldPainter = TQTextFormat::painter(); TQTextFormat::setPainter( p ); if ( formatCollection()->defaultFormat()->color() != cg.text() ) setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() ); TQTextParagraph *parag = firstParagraph(); while ( parag ) { if ( !parag->isValid() ) parag->format(); int y = parag->rect().y(); TQRect pr( parag->rect() ); pr.setX( 0 ); pr.setWidth( TQWIDGETSIZE_MAX ); if ( !rect.isNull() && !rect.intersects( pr ) ) { parag = parag->next(); continue; } p->translate( 0, y ); if ( rect.isValid() ) parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() ); else parag->paint( *p, cg, 0, FALSE ); p->translate( 0, -y ); parag = parag->next(); if ( !flow()->isEmpty() ) flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE ); } TQTextFormat::setPainter(oldPainter); } void TQTextDocument::drawParagraph( TQPainter *p, TQTextParagraph *parag, int cx, int cy, int cw, int ch, TQPixmap *&doubleBuffer, const TQColorGroup &cg, bool drawCursor, TQTextCursor *cursor, bool resetChanged ) { TQPainter *painter = 0; if ( resetChanged ) parag->setChanged( FALSE ); TQRect ir( parag->rect() ); #ifndef TQT_NO_TEXTCUSTOMITEM if (!parag->tableCell()) #endif ir.setWidth(width()); bool uDoubleBuffer = useDoubleBuffer( parag, p ); if ( uDoubleBuffer ) { painter = new TQPainter; if ( cx >= 0 && cy >= 0 ) ir = ir.intersect( TQRect( cx, cy, cw, ch ) ); if ( !doubleBuffer || ir.width() > doubleBuffer->width() || ir.height() > doubleBuffer->height() ) { doubleBuffer = bufferPixmap( ir.size() ); painter->begin( doubleBuffer ); } else { painter->begin( doubleBuffer ); } } else { painter = p; painter->translate( ir.x(), ir.y() ); } painter->setBrushOrigin( -ir.x(), -ir.y() ); if ( uDoubleBuffer || is_printer( painter ) ) painter->fillRect( TQRect( 0, 0, ir.width(), ir.height() ), parag->backgroundBrush( cg ) ); else if ( cursor && cursor->paragraph() == parag ) painter->fillRect( TQRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ), parag->backgroundBrush( cg ) ); painter->translate( -( ir.x() - parag->rect().x() ), -( ir.y() - parag->rect().y() ) ); parag->paint( *painter, cg, drawCursor ? cursor : 0, TRUE, cx, cy, cw, ch ); if ( uDoubleBuffer ) { delete painter; painter = 0; p->drawPixmap( ir.topLeft(), *doubleBuffer, TQRect( TQPoint( 0, 0 ), ir.size() ) ); } else { painter->translate( -ir.x(), -ir.y() ); } parag->document()->nextDoubleBuffered = FALSE; } TQTextParagraph *TQTextDocument::draw( TQPainter *p, int cx, int cy, int cw, int ch, const TQColorGroup &cg, bool onlyChanged, bool drawCursor, TQTextCursor *cursor, bool resetChanged ) { if ( withoutDoubleBuffer || (par && par->withoutDoubleBuffer) ) { withoutDoubleBuffer = TRUE; TQRect r; draw( p, r, cg ); return 0; } withoutDoubleBuffer = FALSE; if ( !firstParagraph() ) return 0; TQPainter * oldPainter = TQTextFormat::painter(); TQTextFormat::setPainter( p ); if ( formatCollection()->defaultFormat()->color() != cg.text() ) setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() ); if ( cx < 0 && cy < 0 ) { cx = 0; cy = 0; cw = width(); ch = height(); } TQTextParagraph *lastFormatted = 0; TQTextParagraph *parag = firstParagraph(); TQPixmap *doubleBuffer = 0; while ( parag ) { lastFormatted = parag; if ( !parag->isValid() ) parag->format(); TQRect pr = parag->rect(); pr.setWidth( parag->document()->width() ); if ( pr.y() > cy + ch ) goto floating; TQRect clipr( cx, cy, cw, ch ); if ( !pr.intersects( clipr ) || ( onlyChanged && !parag->hasChanged() ) ) { pr.setWidth( parag->document()->width() ); parag = parag->next(); continue; } drawParagraph( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged ); parag = parag->next(); } parag = lastParagraph(); floating: if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) { if ( !parag->document()->tqparent() ) { TQRect fillRect = TQRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(), parag->document()->height() - ( parag->rect().y() + parag->rect().height() ) ); if ( TQRect( cx, cy, cw, ch ).intersects( fillRect ) ) p->fillRect( fillRect, cg.brush( TQColorGroup::Base ) ); } if ( !flow()->isEmpty() ) { TQRect cr( cx, cy, cw, ch ); flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE ); } } if ( buf_pixmap && buf_pixmap->height() > 300 ) { delete buf_pixmap; buf_pixmap = 0; } TQTextFormat::setPainter(oldPainter); return lastFormatted; } /* #### this function only sets the default font size in the format collection */ void TQTextDocument::setDefaultFormat( const TQFont &font, const TQColor &color ) { bool reformat = font != fCollection->defaultFormat()->font(); for ( TQTextDocument *d = childList.first(); d; d = childList.next() ) d->setDefaultFormat( font, color ); fCollection->updateDefaultFormat( font, color, sheet_ ); if ( !reformat ) return; tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8; // tqinvalidate paragraphs and custom items TQTextParagraph *p = fParag; while ( p ) { p->tqinvalidate( 0 ); #ifndef TQT_NO_TEXTCUSTOMITEM for ( int i = 0; i < p->length() - 1; ++i ) if ( p->at( i )->isCustom() ) p->at( i )->customItem()->tqinvalidate(); #endif p = p->next(); } } #ifndef TQT_NO_TEXTCUSTOMITEM void TQTextDocument::registerCustomItem( TQTextCustomItem *i, TQTextParagraph *p ) { if ( i && i->placement() != TQTextCustomItem::PlaceInline ) { flow_->registerFloatingItem( i ); p->registerFloatingItem( i ); } if (i) i->setParagraph( p ); p->mightHaveCustomItems = mightHaveCustomItems = TRUE; } void TQTextDocument::unregisterCustomItem( TQTextCustomItem *i, TQTextParagraph *p ) { p->unregisterFloatingItem( i ); i->setParagraph( 0 ); flow_->unregisterFloatingItem( i ); } #endif bool TQTextDocument::hasFocusParagraph() const { return !!focusIndicator.parag; } TQString TQTextDocument::focusHref() const { return focusIndicator.href; } TQString TQTextDocument::focusName() const { return focusIndicator.name; } bool TQTextDocument::focusNextPrevChild( bool next ) { if ( !focusIndicator.parag ) { if ( next ) { focusIndicator.parag = fParag; focusIndicator.start = 0; focusIndicator.len = 0; } else { focusIndicator.parag = lParag; focusIndicator.start = lParag->length(); focusIndicator.len = 0; } } else { focusIndicator.parag->setChanged( TRUE ); } focusIndicator.href = TQString::null; focusIndicator.name = TQString::null; if ( next ) { TQTextParagraph *p = focusIndicator.parag; int index = focusIndicator.start + focusIndicator.len; while ( p ) { for ( int i = index; i < p->length(); ++i ) { if ( p->at( i )->isAnchor() ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = p->at( i )->anchorHref(); focusIndicator.name = p->at( i )->anchorName(); while ( i < p->length() ) { if ( !p->at( i )->isAnchor() ) return TRUE; focusIndicator.len++; i++; } #ifndef TQT_NO_TEXTCUSTOMITEM } else if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { TQTextTable *t = (TQTextTable*)p->at( i )->customItem(); TQPtrList cells = t->tableCells(); // first try to continue TQTextTableCell *c; bool resetCells = TRUE; for ( c = cells.first(); c; c = cells.next() ) { if ( c->richText()->hasFocusParagraph() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return TRUE; } else { resetCells = FALSE; c = cells.next(); break; } } } // now really try if ( resetCells ) c = cells.first(); for ( ; c; c = cells.next() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return TRUE; } } } #endif } } index = 0; p = p->next(); } } else { TQTextParagraph *p = focusIndicator.parag; int index = focusIndicator.start - 1; if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 ) index++; while ( p ) { for ( int i = index; i >= 0; --i ) { if ( p->at( i )->isAnchor() ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = p->at( i )->anchorHref(); focusIndicator.name = p->at( i )->anchorName(); while ( i >= -1 ) { if ( i < 0 || !p->at( i )->isAnchor() ) { focusIndicator.start++; return TRUE; } if ( i < 0 ) break; focusIndicator.len++; focusIndicator.start--; i--; } #ifndef TQT_NO_TEXTCUSTOMITEM } else if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { TQTextTable *t = (TQTextTable*)p->at( i )->customItem(); TQPtrList cells = t->tableCells(); // first try to continue TQTextTableCell *c; bool resetCells = TRUE; for ( c = cells.last(); c; c = cells.prev() ) { if ( c->richText()->hasFocusParagraph() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return TRUE; } else { resetCells = FALSE; c = cells.prev(); break; } } if ( cells.at() == 0 ) break; } // now really try if ( resetCells ) c = cells.last(); for ( ; c; c = cells.prev() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); focusIndicator.name = c->richText()->focusName(); return TRUE; } if ( cells.at() == 0 ) break; } } #endif } } p = p->prev(); if ( p ) index = p->length() - 1; } } focusIndicator.parag = 0; return FALSE; } int TQTextDocument::length() const { int l = -1; TQTextParagraph *p = fParag; while ( p ) { l += p->length(); p = p->next(); } return TQMAX(0,l); } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ int TQTextFormat::width( const TQChar &c ) const { if ( c.tqunicode() == 0xad ) // soft hyphen return 0; if ( !pntr || !pntr->isActive() ) { if ( c == '\t' ) return fm.width( ' ' ); if ( ha == AlignNormal ) { int w; if ( c.row() ) w = fm.width( c ); else w = widths[ c.tqunicode() ]; if ( w == 0 && !c.row() ) { w = fm.width( c ); ( (TQTextFormat*)this )->widths[ c.tqunicode() ] = w; } return w; } else { TQFont f( fn ); if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); TQFontMetrics fm_( f ); return fm_.width( c ); } } TQFont f( fn ); if ( ha != AlignNormal ) { if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); } applyFont( f ); return pntr_fm->width( c ); } int TQTextFormat::width( const TQString &str, int pos ) const { int w = 0; if ( str.tqunicode()[ pos ].tqunicode() == 0xad ) return w; if ( !pntr || !pntr->isActive() ) { if ( ha == AlignNormal ) { w = fm.charWidth( str, pos ); } else { TQFont f( fn ); if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); TQFontMetrics fm_( f ); w = fm_.charWidth( str, pos ); } } else { TQFont f( fn ); if ( ha != AlignNormal ) { if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); } applyFont( f ); w = pntr_fm->charWidth( str, pos ); } return w; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextString::TQTextString() { bidiDirty = TRUE; bidi = FALSE; rightToLeft = FALSE; dir = TQChar::DirON; } TQTextString::TQTextString( const TQTextString &s ) { bidiDirty = TRUE; bidi = s.bidi; rightToLeft = s.rightToLeft; dir = s.dir; data = s.data; data.detach(); for ( int i = 0; i < (int)data.size(); ++i ) { TQTextFormat *f = data[i].format(); if ( f ) f->addRef(); } } void TQTextString::insert( int index, const QString &s, TQTextFormat *f ) { insert( index, s.tqunicode(), s.length(), f ); } void TQTextString::insert( int index, const QChar *tqunicode, int len, TQTextFormat *f ) { int os = data.size(); data.resize( data.size() + len, TQGArray::SpeedOptim ); if ( index < os ) { memmove( data.data() + index + len, data.data() + index, sizeof( TQTextStringChar ) * ( os - index ) ); } TQTextStringChar *ch = data.data() + index; for ( int i = 0; i < len; ++i ) { ch->x = 0; ch->lineStart = 0; ch->d.format = 0; ch->nobreak = FALSE; ch->type = TQTextStringChar::Regular; ch->d.format = f; ch->rightToLeft = 0; ch->c = tqunicode[i]; ++ch; } bidiDirty = TRUE; } TQTextString::~TQTextString() { clear(); } void TQTextString::insert( int index, TQTextStringChar *c, bool doAddRefFormat ) { int os = data.size(); data.resize( data.size() + 1, TQGArray::SpeedOptim ); if ( index < os ) { memmove( data.data() + index + 1, data.data() + index, sizeof( TQTextStringChar ) * ( os - index ) ); } TQTextStringChar &ch = data[ (int)index ]; ch.c = c->c; ch.x = 0; ch.lineStart = 0; ch.rightToLeft = 0; ch.d.format = 0; ch.type = TQTextStringChar::Regular; ch.nobreak = FALSE; if ( doAddRefFormat && c->format() ) c->format()->addRef(); ch.setFormat( c->format() ); bidiDirty = TRUE; } int TQTextString::appendParagraphs( TQTextParagraph *start, TQTextParagraph *end ) { int paragCount = 0; int newLength = data.size(); TQTextParagraph *p = start; for (; p != end; p = p->next()) { newLength += p->length(); ++paragCount; } const int oldLength = data.size(); data.resize(newLength, TQGArray::SpeedOptim); TQTextStringChar *d = &data[oldLength]; for (p = start; p != end; p = p->next()) { const TQTextStringChar * const src = p->at(0); int i = 0; for (; i < p->length() - 1; ++i) { d[i].c = src[i].c; d[i].x = 0; d[i].lineStart = 0; d[i].rightToLeft = 0; d[i].type = TQTextStringChar::Regular; d[i].nobreak = FALSE; d[i].d.format = src[i].format(); if (d[i].d.format) d[i].d.format->addRef(); } d[i].x = 0; d[i].lineStart = 0; d[i].nobreak = FALSE; d[i].type = TQTextStringChar::Regular; d[i].d.format = 0; d[i].rightToLeft = 0; d[i].c = '\n'; d += p->length(); } bidiDirty = TRUE; return paragCount; } void TQTextString::truncate( int index ) { index = TQMAX( index, 0 ); index = TQMIN( index, (int)data.size() - 1 ); if ( index < (int)data.size() ) { for ( int i = index + 1; i < (int)data.size(); ++i ) { TQTextStringChar &ch = data[ i ]; #ifndef TQT_NO_TEXTCUSTOMITEM if ( !(ch.type == TQTextStringChar::Regular) ) { delete ch.customItem(); if ( ch.d.custom->format ) ch.d.custom->format->removeRef(); delete ch.d.custom; ch.d.custom = 0; } else #endif if ( ch.format() ) { ch.format()->removeRef(); } } } data.truncate( index ); bidiDirty = TRUE; } void TQTextString::remove( int index, int len ) { for ( int i = index; i < (int)data.size() && i - index < len; ++i ) { TQTextStringChar &ch = data[ i ]; #ifndef TQT_NO_TEXTCUSTOMITEM if ( !(ch.type == TQTextStringChar::Regular) ) { delete ch.customItem(); if ( ch.d.custom->format ) ch.d.custom->format->removeRef(); delete ch.d.custom; ch.d.custom = 0; } else #endif if ( ch.format() ) { ch.format()->removeRef(); } } memmove( data.data() + index, data.data() + index + len, sizeof( TQTextStringChar ) * ( data.size() - index - len ) ); data.resize( data.size() - len, TQGArray::SpeedOptim ); bidiDirty = TRUE; } void TQTextString::clear() { for ( int i = 0; i < (int)data.count(); ++i ) { TQTextStringChar &ch = data[ i ]; #ifndef TQT_NO_TEXTCUSTOMITEM if ( !(ch.type == TQTextStringChar::Regular) ) { if ( ch.customItem() && ch.customItem()->placement() == TQTextCustomItem::PlaceInline ) delete ch.customItem(); if ( ch.d.custom->format ) ch.d.custom->format->removeRef(); delete ch.d.custom; ch.d.custom = 0; } else #endif if ( ch.format() ) { ch.format()->removeRef(); } } data.resize( 0 ); bidiDirty = TRUE; } void TQTextString::setFormat( int index, TQTextFormat *f, bool useCollection ) { TQTextStringChar &ch = data[ index ]; if ( useCollection && ch.format() ) ch.format()->removeRef(); ch.setFormat( f ); } void TQTextString::checkBidi() const { TQTextString *that = (TQTextString *)this; that->bidiDirty = FALSE; int length = data.size(); if ( !length ) { that->bidi = FALSE; that->rightToLeft = dir == TQChar::DirR; return; } const TQTextStringChar *start = data.data(); const TQTextStringChar *end = start + length; ((TQTextString *)this)->stringCache = toString(data); // determines the properties we need for layouting TQTextEngine textEngine( toString(), 0 ); textEngine.direction = (TQChar::Direction) dir; textEngine.itemize(TQTextEngine::SingleLine); const TQCharAttributes *ca = textEngine.attributes() + length-1; TQTextStringChar *ch = (TQTextStringChar *)end - 1; TQScriptItem *item = &textEngine.items[textEngine.items.size()-1]; unsigned char bidiLevel = item->analysis.bidiLevel; if ( bidiLevel ) that->bidi = TRUE; int pos = length-1; while ( ch >= start ) { if ( item->position > pos ) { --item; TQ_ASSERT( item >= &textEngine.items[0] ); TQ_ASSERT( item < &textEngine.items[textEngine.items.size()] ); bidiLevel = item->analysis.bidiLevel; if ( bidiLevel ) that->bidi = TRUE; } ch->softBreak = ca->softBreak; ch->whiteSpace = ca->whiteSpace; ch->charStop = ca->charStop; ch->wordStop = ca->wordStop; ch->bidiLevel = bidiLevel; ch->rightToLeft = (bidiLevel%2); --ch; --ca; --pos; } if ( dir == TQChar::DirR ) { that->bidi = TRUE; that->rightToLeft = TRUE; } else if ( dir == TQChar::DirL ) { that->rightToLeft = FALSE; } else { that->rightToLeft = (textEngine.direction == TQChar::DirR); } } void TQTextDocument::setStyleSheet( TQStyleSheet *s ) { if ( !s ) return; sheet_ = s; list_tm = list_bm = par_tm = par_bm = 12; list_lm = 40; li_tm = li_bm = 0; TQStyleSheetItem* item = s->item( "ol" ); if ( item ) { list_tm = TQMAX(0,item->margin( TQStyleSheetItem::MarginTop )); list_bm = TQMAX(0,item->margin( TQStyleSheetItem::MarginBottom )); list_lm = TQMAX(0,item->margin( TQStyleSheetItem::MarginLeft )); } if ( (item = s->item( "li" ) ) ) { li_tm = TQMAX(0,item->margin( TQStyleSheetItem::MarginTop )); li_bm = TQMAX(0,item->margin( TQStyleSheetItem::MarginBottom )); } if ( (item = s->item( "p" ) ) ) { par_tm = TQMAX(0,item->margin( TQStyleSheetItem::MarginTop )); par_bm = TQMAX(0,item->margin( TQStyleSheetItem::MarginBottom )); } } void TQTextDocument::setUnderlineLinks( bool b ) { underlLinks = b; for ( TQTextDocument *d = childList.first(); d; d = childList.next() ) d->setUnderlineLinks( b ); } void TQTextStringChar::setFormat( TQTextFormat *f ) { if ( type == Regular ) { d.format = f; } else { #ifndef TQT_NO_TEXTCUSTOMITEM if ( !d.custom ) { d.custom = new CustomData; d.custom->custom = 0; } d.custom->format = f; #endif } } #ifndef TQT_NO_TEXTCUSTOMITEM void TQTextStringChar::setCustomItem( TQTextCustomItem *i ) { if ( type == Regular ) { TQTextFormat *f = format(); d.custom = new CustomData; d.custom->format = f; } else { delete d.custom->custom; } d.custom->custom = i; type = (type == Anchor ? CustomAnchor : Custom); } void TQTextStringChar::loseCustomItem() { if ( type == Custom ) { TQTextFormat *f = d.custom->format; d.custom->custom = 0; delete d.custom; type = Regular; d.format = f; } else if ( type == CustomAnchor ) { d.custom->custom = 0; type = Anchor; } } #endif TQString TQTextStringChar::anchorName() const { if ( type == Regular ) return TQString::null; else return d.custom->anchorName; } TQString TQTextStringChar::anchorHref() const { if ( type == Regular ) return TQString::null; else return d.custom->anchorHref; } void TQTextStringChar::setAnchor( const TQString& name, const TQString& href ) { if ( type == Regular ) { TQTextFormat *f = format(); d.custom = new CustomData; #ifndef TQT_NO_TEXTCUSTOMITEM d.custom->custom = 0; #endif d.custom->format = f; type = Anchor; } else if ( type == Custom ) { type = CustomAnchor; } d.custom->anchorName = name; d.custom->anchorHref = href; } int TQTextString::width( int idx ) const { int w = 0; TQTextStringChar *c = &at( idx ); if ( !c->charStop || c->c.tqunicode() == 0xad || c->c.tqunicode() == 0x2028 ) return 0; #ifndef TQT_NO_TEXTCUSTOMITEM if( c->isCustom() ) { if( c->customItem()->placement() == TQTextCustomItem::PlaceInline ) w = c->customItem()->width; } else #endif { int r = c->c.row(); if(r < 0x06 #ifndef TQ_WS_WIN // Uniscribe's handling of Asian makes the condition below fail. || (r > 0x1f && !(r > 0xd7 && r < 0xe0)) #endif ) { w = c->format()->width( c->c ); } else { w = c->format()->width(toString(), idx); } } return w; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextParagraph::TQTextParagraph( TQTextDocument *d, TQTextParagraph *pr, TQTextParagraph *nx, bool updateIds ) : p( pr ), n( nx ), docOrPseudo( d ), changed(FALSE), firstFormat(TRUE), firstPProcess(TRUE), needPreProcess(FALSE), fullWidth(TRUE), lastInFrame(FALSE), visible(TRUE), breakable(TRUE), movedDown(FALSE), mightHaveCustomItems(FALSE), hasdoc( d != 0 ), litem(FALSE), rtext(FALSE), align( 0 ), lstyle( TQStyleSheetItem::ListDisc ), invalid( 0 ), mSelections( 0 ), #ifndef TQT_NO_TEXTCUSTOMITEM mFloatingItems( 0 ), #endif utm( 0 ), ubm( 0 ), ulm( 0 ), urm( 0 ), uflm( 0 ), ulinespacing( 0 ), tabStopWidth(0), minwidth(0), tArray(0), eData( 0 ), ldepth( 0 ) { lstyle = TQStyleSheetItem::ListDisc; if ( !hasdoc ) docOrPseudo = new TQTextParagraphPseudoDocument; bgcol = 0; list_val = -1; painttqdevice = 0; TQTextFormat* defFormat = formatCollection()->defaultFormat(); if ( !hasdoc ) { tabStopWidth = defFormat->width( 'x' ) * 8; pseudoDocument()->commandHistory = new TQTextCommandHistory( 100 ); } if ( p ) p->n = this; if ( n ) n->p = this; if ( !p && hasdoc ) document()->setFirstParagraph( this ); if ( !n && hasdoc ) document()->setLastParagraph( this ); state = -1; if ( p ) id = p->id + 1; else id = 0; if ( n && updateIds ) { TQTextParagraph *s = n; while ( s ) { s->id = s->p->id + 1; s->invalidateStyleCache(); s = s->n; } } str = new TQTextString(); TQChar ch(' '); str->insert( 0, &ch, 1, formatCollection()->defaultFormat() ); } TQTextParagraph::~TQTextParagraph() { delete str; if ( hasdoc ) { register TQTextDocument *doc = document(); if ( this == doc->minwParag ) { doc->minwParag = 0; doc->minw = 0; } if ( this == doc->curParag ) doc->curParag = 0; } else { delete pseudoDocument(); } delete [] tArray; delete eData; QMap::Iterator it = lineStarts.begin(); for ( ; it != lineStarts.end(); ++it ) delete *it; if ( mSelections ) delete mSelections; #ifndef TQT_NO_TEXTCUSTOMITEM if ( mFloatingItems ) delete mFloatingItems; #endif if ( p ) p->setNext( n ); if ( n ) n->setPrev( p ); delete bgcol; } void TQTextParagraph::setNext( TQTextParagraph *s ) { n = s; if ( !n && hasdoc ) document()->setLastParagraph( this ); } void TQTextParagraph::setPrev( TQTextParagraph *s ) { p = s; if ( !p && hasdoc ) document()->setFirstParagraph( this ); } void TQTextParagraph::tqinvalidate( int chr ) { if ( invalid < 0 ) invalid = chr; else invalid = TQMIN( invalid, chr ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( mFloatingItems ) { for ( TQTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) i->ypos = -1; } #endif invalidateStyleCache(); } void TQTextParagraph::invalidateStyleCache() { if ( list_val < 0 ) list_val = -1; } void TQTextParagraph::insert( int index, const QString &s ) { insert( index, s.tqunicode(), s.length() ); } void TQTextParagraph::insert( int index, const QChar *tqunicode, int len ) { if ( hasdoc && !document()->useFormatCollection() && document()->preProcessor() ) str->insert( index, tqunicode, len, document()->preProcessor()->format( TQTextPreProcessor::Standard ) ); else str->insert( index, tqunicode, len, formatCollection()->defaultFormat() ); tqinvalidate( index ); needPreProcess = TRUE; } void TQTextParagraph::truncate( int index ) { str->truncate( index ); insert( length(), " " ); needPreProcess = TRUE; } void TQTextParagraph::remove( int index, int len ) { if ( index + len - str->length() > 0 ) return; #ifndef TQT_NO_TEXTCUSTOMITEM for ( int i = index; i < index + len; ++i ) { TQTextStringChar *c = at( i ); if ( hasdoc && c->isCustom() ) { document()->unregisterCustomItem( c->customItem(), this ); } } #endif str->remove( index, len ); tqinvalidate( 0 ); needPreProcess = TRUE; } void TQTextParagraph::join( TQTextParagraph *s ) { int oh = r.height() + s->r.height(); n = s->n; if ( n ) n->p = this; else if ( hasdoc ) document()->setLastParagraph( this ); int start = str->length(); if ( length() > 0 && at( length() - 1 )->c == ' ' ) { remove( length() - 1, 1 ); --start; } append( s->str->toString(), TRUE ); for ( int i = 0; i < s->length(); ++i ) { if ( !hasdoc || document()->useFormatCollection() ) { s->str->at( i ).format()->addRef(); str->setFormat( i + start, s->str->at( i ).format(), TRUE ); } #ifndef TQT_NO_TEXTCUSTOMITEM if ( s->str->at( i ).isCustom() ) { TQTextCustomItem * item = s->str->at( i ).customItem(); str->at( i + start ).setCustomItem( item ); s->str->at( i ).loseCustomItem(); if ( hasdoc ) { document()->unregisterCustomItem( item, s ); document()->registerCustomItem( item, this ); } } if ( s->str->at( i ).isAnchor() ) { str->at( i + start ).setAnchor( s->str->at( i ).anchorName(), s->str->at( i ).anchorHref() ); } #endif } if ( !extraData() && s->extraData() ) { setExtraData( s->extraData() ); s->setExtraData( 0 ); } else if ( extraData() && s->extraData() ) { extraData()->join( s->extraData() ); } delete s; tqinvalidate( 0 ); r.setHeight( oh ); needPreProcess = TRUE; if ( n ) { TQTextParagraph *s = n; s->tqinvalidate( 0 ); while ( s ) { s->id = s->p->id + 1; s->state = -1; s->needPreProcess = TRUE; s->changed = TRUE; s->invalidateStyleCache(); s = s->n; } } format(); state = -1; } void TQTextParagraph::move( int &dy ) { if ( dy == 0 ) return; changed = TRUE; r.moveBy( 0, dy ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( mFloatingItems ) { for ( TQTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) i->ypos += dy; } #endif if ( p ) p->lastInFrame = TRUE; // do page breaks if required if ( hasdoc && document()->isPageBreakEnabled() ) { int shift; if ( ( shift = document()->formatter()->formatVertically( document(), this ) ) ) { if ( p ) p->setChanged( TRUE ); dy += shift; } } } void TQTextParagraph::format( int start, bool doMove ) { if ( !str || str->length() == 0 || !formatter() ) return; if ( hasdoc && document()->preProcessor() && ( needPreProcess || state == -1 ) ) document()->preProcessor()->process( document(), this, invalid <= 0 ? 0 : invalid ); needPreProcess = FALSE; if ( invalid == -1 ) return; r.moveTopLeft( TQPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) ); if ( p ) p->lastInFrame = FALSE; movedDown = FALSE; bool formattedAgain = FALSE; formatAgain: r.setWidth( documentWidth() ); #ifndef TQT_NO_TEXTCUSTOMITEM if ( hasdoc && mFloatingItems ) { for ( TQTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) { i->ypos = r.y(); if ( i->placement() == TQTextCustomItem::PlaceRight ) { i->xpos = r.x() + r.width() - i->width; } } } #endif QMap oldLineStarts = lineStarts; lineStarts.clear(); int y = formatter()->format( document(), this, start, oldLineStarts ); r.setWidth( TQMAX( r.width(), formatter()->minimumWidth() ) ); QMap::Iterator it = oldLineStarts.begin(); for ( ; it != oldLineStarts.end(); ++it ) delete *it; if ( !hasdoc ) { // qt_format_text bounding rect handling it = lineStarts.begin(); int usedw = 0; for ( ; it != lineStarts.end(); ++it ) usedw = TQMAX( usedw, (*it)->w ); if ( r.width() <= 0 ) { // if the user specifies an invalid rect, this means that the // bounding box should grow to the width that the text actually // needs r.setWidth( usedw ); } else { r.setWidth( TQMIN( usedw, r.width() ) ); } } if ( y != r.height() ) r.setHeight( y ); if ( !visible ) { r.setHeight( 0 ); } else { int minw = minwidth = formatter()->minimumWidth(); int wused = formatter()->widthUsed(); wused = TQMAX( minw, wused ); if ( hasdoc ) { document()->setMinimumWidth( minw, wused, this ); } else { pseudoDocument()->minw = TQMAX( pseudoDocument()->minw, minw ); pseudoDocument()->wused = TQMAX( pseudoDocument()->wused, wused ); } } // do page breaks if required if ( hasdoc && document()->isPageBreakEnabled() ) { int shift = document()->formatter()->formatVertically( document(), this ); if ( shift && !formattedAgain ) { formattedAgain = TRUE; goto formatAgain; } } if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) { int dy = ( r.y() + r.height() ) - n->r.y(); TQTextParagraph *s = n; bool makeInvalid = p && p->lastInFrame; while ( s && dy ) { if ( !s->isFullWidth() ) makeInvalid = TRUE; if ( makeInvalid ) s->tqinvalidate( 0 ); s->move( dy ); if ( s->lastInFrame ) makeInvalid = TRUE; s = s->n; } } firstFormat = FALSE; changed = TRUE; invalid = -1; //##### string()->setTextChanged( FALSE ); } int TQTextParagraph::lineHeightOfChar( int i, int *bl, int *y ) const { if ( !isValid() ) ( (TQTextParagraph*)this )->format(); QMap::ConstIterator it = lineStarts.end(); --it; for ( ;; ) { if ( i >= it.key() ) { if ( bl ) *bl = ( *it )->baseLine; if ( y ) *y = ( *it )->y; return ( *it )->h; } if ( it == lineStarts.begin() ) break; --it; } qWarning( "TQTextParagraph::lineHeightOfChar: couldn't find lh for %d", i ); return 15; } TQTextStringChar *TQTextParagraph::lineStartOfChar( int i, int *index, int *line ) const { if ( !isValid() ) ( (TQTextParagraph*)this )->format(); int l = (int)lineStarts.count() - 1; QMap::ConstIterator it = lineStarts.end(); --it; for ( ;; ) { if ( i >= it.key() ) { if ( index ) *index = it.key(); if ( line ) *line = l; return &str->at( it.key() ); } if ( it == lineStarts.begin() ) break; --it; --l; } qWarning( "TQTextParagraph::lineStartOfChar: couldn't find %d", i ); return 0; } int TQTextParagraph::lines() const { if ( !isValid() ) ( (TQTextParagraph*)this )->format(); return (int)lineStarts.count(); } TQTextStringChar *TQTextParagraph::lineStartOfLine( int line, int *index ) const { if ( !isValid() ) ( (TQTextParagraph*)this )->format(); if ( line >= 0 && line < (int)lineStarts.count() ) { QMap::ConstIterator it = lineStarts.begin(); while ( line-- > 0 ) ++it; int i = it.key(); if ( index ) *index = i; return &str->at( i ); } qWarning( "TQTextParagraph::lineStartOfLine: couldn't find %d", line ); return 0; } int TQTextParagraph::leftGap() const { if ( !isValid() ) ( (TQTextParagraph*)this )->format(); if ( str->length() == 0) return 0; int line = 0; int x = str->length() ? str->at(0).x : 0; /* set x to x of first char */ if ( str->isBidi() ) { for ( int i = 1; i < str->length()-1; ++i ) x = TQMIN(x, str->at(i).x); return x; } QMap::ConstIterator it = lineStarts.begin(); while (line < (int)lineStarts.count()) { int i = it.key(); /* char index */ x = TQMIN(x, str->at(i).x); ++it; ++line; } return x; } void TQTextParagraph::setFormat( int index, int len, TQTextFormat *f, bool useCollection, int flags ) { if ( !f ) return; if ( index < 0 ) index = 0; if ( index > str->length() - 1 ) index = str->length() - 1; if ( index + len >= str->length() ) len = str->length() - index; TQTextFormatCollection *fc = 0; if ( useCollection ) fc = formatCollection(); TQTextFormat *of; for ( int i = 0; i < len; ++i ) { of = str->at( i + index ).format(); if ( !changed && ( !of || f->key() != of->key() ) ) changed = TRUE; if ( invalid == -1 && ( f->font().family() != of->font().family() || f->font().pointSize() != of->font().pointSize() || f->font().weight() != of->font().weight() || f->font().italic() != of->font().italic() || f->vAlign() != of->vAlign() ) ) { tqinvalidate( 0 ); } if ( flags == -1 || flags == TQTextFormat::Format || !fc ) { if ( fc ) f = fc->format( f ); str->setFormat( i + index, f, useCollection ); } else { TQTextFormat *fm = fc->format( of, f, flags ); str->setFormat( i + index, fm, useCollection ); } } } void TQTextParagraph::indent( int *oldIndent, int *newIndent ) { if ( !hasdoc || !document()->indent() || isListItem() ) { if ( oldIndent ) *oldIndent = 0; if ( newIndent ) *newIndent = 0; if ( oldIndent && newIndent ) *newIndent = *oldIndent; return; } document()->indent()->indent( document(), this, oldIndent, newIndent ); } void TQTextParagraph::paint( TQPainter &painter, const TQColorGroup &cg, TQTextCursor *cursor, bool drawSelections, int clipx, int clipy, int clipw, int cliph ) { if ( !visible ) return; int i, y, h, baseLine, xstart, xend = 0; i = y =h = baseLine = 0; TQRect cursorRect; drawSelections &= ( mSelections != 0 ); // macintosh full-width selection style bool fullWidthStyle = TQApplication::tqstyle().tqstyleHint(TQStyle::SH_RichText_FullWidthSelection); int fullSelectionWidth = 0; if ( drawSelections && fullWidthStyle ) fullSelectionWidth = (hasdoc ? document()->width() : r.width()); TQString qstr = str->toString(); // detach string qstr.setLength(qstr.length()); // ### workaround so that \n are not drawn, actually this should // be fixed in TQFont somewhere (under Windows you get ugly boxes // otherwise) TQChar* uc = (TQChar*) qstr.tqunicode(); for ( int ii = 0; ii < qstr.length(); ii++ ) if ( uc[(int)ii]== '\n' || uc[(int)ii] == '\t' ) uc[(int)ii] = 0x20; int line = -1; int paintStart = 0; TQTextStringChar *chr = 0; TQTextStringChar *nextchr = at( 0 ); for ( i = 0; i < length(); i++ ) { chr = nextchr; if ( i < length()-1 ) nextchr = at( i+1 ); // we flush at end of document bool flush = (i == length()-1); bool ignoreSoftHyphen = FALSE; if ( !flush ) { // we flush at end of line flush |= nextchr->lineStart; // we flush on format changes flush |= ( nextchr->format() != chr->format() ); // we flush on link changes flush |= ( nextchr->isLink() != chr->isLink() ); // we flush on start of run flush |= ( nextchr->bidiLevel != chr->bidiLevel ); // we flush on bidi changes flush |= ( nextchr->rightToLeft != chr->rightToLeft ); // we flush before and after tabs flush |= ( chr->c == '\t' || nextchr->c == '\t' ); // we flush on soft hypens if (chr->c.tqunicode() == 0xad) { flush = TRUE; if (!nextchr->lineStart) ignoreSoftHyphen = TRUE; } // we flush on custom items flush |= chr->isCustom(); // we flush before custom items flush |= nextchr->isCustom(); // when painting justified, we flush on spaces if ((tqalignment() & TQt::AlignJustify) == TQt::AlignJustify ) flush |= chr->whiteSpace; } // init a new line if ( chr->lineStart ) { ++line; paintStart = i; lineInfo( line, y, h, baseLine ); if ( clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph ) { // outside clip area, leave break; } // if this is the first line and we are a list item, draw the the bullet label if ( line == 0 && isListItem() ) { int x = chr->x; if (str->isBidi()) { if (str->isRightToLeft()) { x = chr->x + str->width(0); for (int k = 1; k < length(); ++k) { if (str->at(k).lineStart) break; x = TQMAX(x, str->at(k).x + str->width(k)); } } else { x = chr->x; for (int k = 1; k < length(); ++k) { if (str->at(k).lineStart) break; x = TQMIN(x, str->at(k).x); } } } drawLabel( &painter, x, y, 0, 0, baseLine, cg ); } } // check for cursor mark if ( cursor && this == cursor->paragraph() && i == cursor->index() ) { TQTextStringChar *c = i == 0 ? chr : chr - 1; cursorRect.setRect( cursor->x() , y + baseLine - c->format()->ascent(), 1, c->format()->height() ); } if ( flush ) { // something changed, draw what we have so far if ( chr->rightToLeft ) { xstart = chr->x; xend = at( paintStart )->x + str->width( paintStart ); } else { xstart = at( paintStart )->x; xend = chr->x; if ( i < length() - 1 ) { if ( !str->at( i + 1 ).lineStart && str->at( i + 1 ).rightToLeft == chr->rightToLeft ) xend = str->at( i + 1 ).x; else xend += str->width( i ); } } if ( (clipx == -1 || clipw <= 0 || (xend >= clipx && xstart <= clipx + clipw)) && ( clipy == -1 || clipy < y+r.y()+h ) ) { if ( !chr->isCustom() ) drawString( painter, qstr, paintStart, i - paintStart + (ignoreSoftHyphen ? 0 : 1), xstart, y, baseLine, xend-xstart, h, drawSelections, fullSelectionWidth, chr, cg, chr->rightToLeft ); #ifndef TQT_NO_TEXTCUSTOMITEM else if ( chr->customItem()->placement() == TQTextCustomItem::PlaceInline ) { bool inSelection = FALSE; if (drawSelections) { QMap::ConstIterator it = mSelections->find( TQTextDocument::Standard ); inSelection = (it != mSelections->end() && (*it).start <= i && (*it).end > i); } chr->customItem()->draw( &painter, chr->x, y, clipx == -1 ? clipx : (clipx - r.x()), clipy == -1 ? clipy : (clipy - r.y()), clipw, cliph, cg, inSelection ); } #endif } paintStart = i+1; } } // time to draw the cursor const int cursor_extent = 4; if ( !cursorRect.isNull() && cursor && ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw)) ) { painter.fillRect( cursorRect, cg.color( TQColorGroup::Text ) ); painter.save(); if ( string()->isBidi() ) { if ( at( cursor->index() )->rightToLeft ) { painter.setPen( TQt::black ); painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); } else { painter.setPen( TQt::black ); painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); } } painter.restore(); } } //#define BIDI_DEBUG void TQTextParagraph::setColorForSelection( TQColor &color, TQPainter &painter, const TQColorGroup& cg, int selection ) { if (selection < 0) return; color = ( hasdoc && selection != TQTextDocument::Standard ) ? document()->selectionColor( selection ) : cg.color( TQColorGroup::Highlight ); if ( selection == TQTextDocument::IMCompositionText ) { #ifndef TQ_WS_MACX int h1, s1, v1, h2, s2, v2; TQT_TQCOLOR_OBJECT(cg.color( TQColorGroup::Base )).hsv( &h1, &s1, &v1 ); TQT_TQCOLOR_OBJECT(cg.color( TQColorGroup::Background )).hsv( &h2, &s2, &v2 ); color.setHsv( h1, s1, ( v1 + v2 ) / 2 ); #else color = TQt::lightGray; #endif painter.setPen( cg.color( TQColorGroup::Text ) ); } else if ( selection == TQTextDocument::IMSelectionText ) { color = cg.color( TQColorGroup::Dark ); painter.setPen( cg.color( TQColorGroup::BrightText ) ); } else if ( !hasdoc || document()->invertSelectionText( selection ) ) { painter.setPen( cg.color( TQColorGroup::HighlightedText ) ); } } void TQTextParagraph::drawString( TQPainter &painter, const TQString &str, int start, int len, int xstart, int y, int baseLine, int w, int h, bool drawSelections, int fullSelectionWidth, TQTextStringChar *formatChar, const TQColorGroup& cg, bool rightToLeft ) { bool plainText = hasdoc ? document()->textFormat() == TQt::PlainText : FALSE; TQTextFormat* format = formatChar->format(); if ( !plainText || (hasdoc && (format->color() != document()->formatCollection()->defaultFormat()->color())) ) painter.setPen( TQPen( format->color() ) ); else painter.setPen( cg.text() ); painter.setFont( format->font() ); if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() ) { if ( format->useLinkColor() ) painter.setPen(document()->linkColor.isValid() ? document()->linkColor : cg.link()); if ( document()->underlineLinks() ) { TQFont fn = format->font(); fn.setUnderline( TRUE ); painter.setFont( fn ); } } TQPainter::TextDirection dir = rightToLeft ? TQPainter::RTL : TQPainter::LTR; int real_length = len; if (len && dir != TQPainter::RTL && start + len == length() ) // don't draw the last character (trailing space) len--; if (len && str.tqunicode()[start+len-1] == TQChar_linesep) len--; TQTextFormat::VerticalAlignment vAlign = format->vAlign(); if ( vAlign != TQTextFormat::AlignNormal ) { // sub or superscript TQFont f( painter.font() ); if ( format->fontSizesInPixels() ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); painter.setFont( f ); int h = painter.fontMetrics().height(); baseLine += (vAlign == TQTextFormat::AlignSubScript) ? h/6 : -h/2; } bool allSelected = FALSE; if (drawSelections) { QMap::ConstIterator it = mSelections->find( TQTextDocument::Standard ); allSelected = (it != mSelections->end() && (*it).start <= start && (*it).end >= start+len); } if (!allSelected) painter.drawText(xstart, y + baseLine, str, start, len, dir); #ifdef BIDI_DEBUG painter.save(); painter.setPen ( TQt::red ); painter.drawLine( xstart, y, xstart, y + baseLine ); painter.drawLine( xstart, y + baseLine/2, xstart + 10, y + baseLine/2 ); int w = 0; int i = 0; while( i < len ) w += painter.fontMetrics().charWidth( str, start + i++ ); painter.setPen ( TQt::blue ); painter.drawLine( xstart + w - 1, y, xstart + w - 1, y + baseLine ); painter.drawLine( xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2 ); painter.restore(); #endif // check if we are in a selection and draw it if (drawSelections) { QMap::ConstIterator it = mSelections->end(); while ( it != mSelections->begin() ) { --it; int selStart = (*it).start; int selEnd = (*it).end; int tmpw = w; selStart = TQMAX(selStart, start); int real_selEnd = TQMIN(selEnd, start+real_length); selEnd = TQMIN(selEnd, start+len); bool extendRight = FALSE; bool extendLeft = FALSE; bool selWrap = (real_selEnd == length()-1 && n && n->hasSelection(it.key())); if (selWrap || this->str->at(real_selEnd).lineStart) { extendRight = (fullSelectionWidth != 0); if (!extendRight && !rightToLeft) tmpw += painter.fontMetrics().width(' '); } if (fullSelectionWidth && (selStart == 0 || this->str->at(selStart).lineStart)) { extendLeft = TRUE; } if (this->str->isRightToLeft() != rightToLeft) extendLeft = extendRight = FALSE; if (this->str->isRightToLeft()) { bool tmp = extendLeft; extendLeft = extendRight; extendRight = tmp; } if ((selStart < real_selEnd) || (selWrap && fullSelectionWidth && extendRight && // don't draw the standard selection on a printer= ((it.key() != TQTextDocument::Standard) || (!is_printer( &painter))))) { int selection = it.key(); TQColor color; setColorForSelection( color, painter, cg, selection ); if (selStart != start || selEnd != start + len || selWrap) { // have to clip painter.save(); int cs, ce; if (rightToLeft) { cs = (selEnd != start + len) ? this->str->at(this->str->previousCursorPosition(selEnd)).x : xstart; ce = (selStart != start) ? this->str->at(this->str->previousCursorPosition(selStart)).x : xstart+tmpw; } else { cs = (selStart != start) ? this->str->at(selStart).x : xstart; ce = (selEnd != start + len) ? this->str->at(selEnd).x : xstart+tmpw; } TQRect r(cs, y, ce-cs, h); if (extendLeft) r.setLeft(0); if (extendRight) r.setRight(fullSelectionWidth); TQRegion reg(r); if ( painter.hasClipping() ) reg &= painter.clipRegion(TQPainter::CoordPainter); painter.setClipRegion(reg, TQPainter::CoordPainter); } int xleft = xstart; if ( extendLeft ) { tmpw += xstart; xleft = 0; } if ( extendRight ) tmpw = fullSelectionWidth - xleft; painter.fillRect( xleft, y, tmpw, h, color ); painter.drawText( xstart, y + baseLine, str, start, len, dir ); // draw preedit's underline if (selection == TQTextDocument::IMCompositionText) painter.drawLine(xstart, y + baseLine + 1, xstart + w, y + baseLine + 1); if (selStart != start || selEnd != start + len || selWrap) painter.restore(); } } } if ( format->isMisspelled() ) { painter.save(); painter.setPen( TQPen( TQt::red, 1, TQt::DotLine ) ); painter.drawLine( xstart, y + baseLine + 1, xstart + w, y + baseLine + 1 ); painter.restore(); } if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() && document()->focusIndicator.parag == this && ( (document()->focusIndicator.start >= start && document()->focusIndicator.start + document()->focusIndicator.len <= start + len) || (document()->focusIndicator.start <= start && document()->focusIndicator.start + document()->focusIndicator.len >= start + len )) ) painter.drawWinFocusRect( TQRect( xstart, y, w, h ) ); } void TQTextParagraph::drawLabel( TQPainter* p, int x, int y, int w, int h, int base, const TQColorGroup& cg ) { TQRect r ( x, y, w, h ); TQStyleSheetItem::ListStyle s = listStyle(); p->save(); TQTextFormat *format = at( 0 )->format(); if ( format ) { p->setPen( format->color() ); p->setFont( format->font() ); } TQFontMetrics fm( p->fontMetrics() ); int size = fm.lineSpacing() / 3; bool rtl = str->isRightToLeft(); switch ( s ) { case TQStyleSheetItem::ListDecimal: case TQStyleSheetItem::ListLowerAlpha: case TQStyleSheetItem::ListUpperAlpha: { if ( list_val == -1 ) { // uninitialised list value, calcluate the right one int depth = listDepth(); list_val--; // ### evil, square and expensive. This needs to be done when formatting, not when painting TQTextParagraph* s = prev(); int depth_s; while ( s && (depth_s = s->listDepth()) >= depth ) { if ( depth_s == depth && s->isListItem() ) list_val--; s = s->prev(); } } int n = list_val; if ( n < -1 ) n = -n - 1; TQString l; switch ( s ) { case TQStyleSheetItem::ListLowerAlpha: if ( n < 27 ) { l = TQChar( ('a' + (char) (n-1))); break; } case TQStyleSheetItem::ListUpperAlpha: if ( n < 27 ) { l = TQChar( ('A' + (char) (n-1))); break; } break; default: //TQStyleSheetItem::ListDecimal: l.setNum( n ); break; } if (rtl) l.prepend(" ."); else l += TQString::tqfromLatin1(". "); int x = ( rtl ? r.left() : r.right() - fm.width(l)); p->drawText( x, r.top() + base, l ); } break; case TQStyleSheetItem::ListSquare: { int x = rtl ? r.left() + size : r.right() - size*2; TQRect er( x, r.top() + fm.height() / 2 - size / 2, size, size ); p->fillRect( er , cg.brush( TQColorGroup::Text ) ); } break; case TQStyleSheetItem::ListCircle: { int x = rtl ? r.left() + size : r.right() - size*2; TQRect er( x, r.top() + fm.height() / 2 - size / 2, size, size); p->drawEllipse( er ); } break; case TQStyleSheetItem::ListDisc: default: { p->setBrush( cg.brush( TQColorGroup::Text )); int x = rtl ? r.left() + size : r.right() - size*2; TQRect er( x, r.top() + fm.height() / 2 - size / 2, size, size); p->drawEllipse( er ); p->setBrush( TQt::NoBrush ); } break; } p->restore(); } #ifndef TQT_NO_DATASTREAM void TQTextParagraph::readStyleInformation( TQDataStream& stream ) { int int_align, int_lstyle; uchar uchar_litem, uchar_rtext, uchar_dir; stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir; align = int_align; lstyle = (TQStyleSheetItem::ListStyle) int_lstyle; litem = uchar_litem; rtext = uchar_rtext; str->setDirection( (TQChar::Direction)uchar_dir ); TQTextParagraph* s = prev() ? prev() : this; while ( s ) { s->tqinvalidate( 0 ); s = s->next(); } } void TQTextParagraph::writeStyleInformation( TQDataStream& stream ) const { stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction(); } #endif void TQTextParagraph::setListItem( bool li ) { if ( (bool)litem == li ) return; litem = li; changed = TRUE; TQTextParagraph* s = prev() ? prev() : this; while ( s ) { s->tqinvalidate( 0 ); s = s->next(); } } void TQTextParagraph::setListDepth( int depth ) { if ( !hasdoc || depth == ldepth ) return; ldepth = depth; TQTextParagraph* s = prev() ? prev() : this; while ( s ) { s->tqinvalidate( 0 ); s = s->next(); } } int *TQTextParagraph::tabArray() const { int *ta = tArray; if ( !ta && hasdoc ) ta = document()->tabArray(); return ta; } int TQTextParagraph::nextTab( int, int x ) { int *ta = tArray; if ( hasdoc ) { if ( !ta ) ta = document()->tabArray(); tabStopWidth = document()->tabStopWidth(); } if ( ta ) { int i = 0; while ( ta[ i ] ) { if ( ta[ i ] >= x ) return tArray[ i ]; ++i; } return tArray[ 0 ]; } else { int d; if ( tabStopWidth != 0 ) d = x / tabStopWidth; else return x; return tabStopWidth * ( d + 1 ); } } void TQTextParagraph::adjustToPainter( TQPainter *p ) { #ifndef TQT_NO_TEXTCUSTOMITEM for ( int i = 0; i < length(); ++i ) { if ( at( i )->isCustom() ) at( i )->customItem()->adjustToPainter( p ); } #endif } TQTextFormatCollection *TQTextParagraph::formatCollection() const { if ( hasdoc ) return document()->formatCollection(); TQTextFormatCollection* fc = &pseudoDocument()->collection; if ( painttqdevice != fc->paintDevice() ) fc->setPaintDevice( painttqdevice ); return fc; } TQString TQTextParagraph::richText() const { TQString s; TQTextStringChar *formatChar = 0; TQString spaces; bool doStart = richTextExportStart && richTextExportStart->paragraph() == this; bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this; int i; TQString lastAnchorName; for ( i = 0; i < length()-1; ++i ) { if ( doStart && i && richTextExportStart->index() == i ) s += ""; if ( doEnd && richTextExportEnd->index() == i ) s += ""; TQTextStringChar *c = &str->at( i ); if ( c->isAnchor() && !c->anchorName().isEmpty() && c->anchorName() != lastAnchorName ) { lastAnchorName = c->anchorName(); if ( c->anchorName().contains( '#' ) ) { TQStringList l = TQStringList::split( '#', c->anchorName() ); for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) s += ""; } else { s += "anchorName() + "\">"; } } if ( !formatChar ) { s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(), 0, TQString::null, c->anchorHref() ); formatChar = c; } else if ( ( formatChar->format()->key() != c->format()->key() ) || (c->anchorHref() != formatChar->anchorHref() ) ) { s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(), formatChar->format() , formatChar->anchorHref(), c->anchorHref() ); formatChar = c; } if ( c->c == '<' ) s += "<"; else if ( c->c == '>' ) s += ">"; else if ( c->c =='&' ) s += "&"; else if ( c->c =='\"' ) s += """; #ifndef TQT_NO_TEXTCUSTOMITEM else if ( c->isCustom() ) s += c->customItem()->richText(); #endif else if ( c->c == '\n' || c->c == TQChar_linesep ) s += "
"; // space on purpose for compatibility with Netscape, Lynx & Co. else s += c->c; } if ( doEnd && richTextExportEnd->index() == i ) s += ""; if ( formatChar ) s += formatChar->format()->makeFormatEndTags( formatCollection()->defaultFormat(), formatChar->anchorHref() ); return s; } void TQTextParagraph::addCommand( TQTextCommand *cmd ) { if ( !hasdoc ) pseudoDocument()->commandHistory->addCommand( cmd ); else document()->commands()->addCommand( cmd ); } TQTextCursor *TQTextParagraph::undo( TQTextCursor *c ) { if ( !hasdoc ) return pseudoDocument()->commandHistory->undo( c ); return document()->commands()->undo( c ); } TQTextCursor *TQTextParagraph::redo( TQTextCursor *c ) { if ( !hasdoc ) return pseudoDocument()->commandHistory->redo( c ); return document()->commands()->redo( c ); } int TQTextParagraph::topMargin() const { int m = 0; if ( rtext ) { m = isListItem() ? (document()->li_tm/TQMAX(1,listDepth()*listDepth())) : ( listDepth() ? 0 : document()->par_tm ); if ( listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth() ) ) m = TQMAX( m, document()->list_tm ); } m += utm; return scale( m, TQTextFormat::painter() ); } int TQTextParagraph::bottomMargin() const { int m = 0; if ( rtext ) { m = isListItem() ? (document()->li_bm/TQMAX(1,listDepth()*listDepth())) : ( listDepth() ? 0 : document()->par_bm ); if ( listDepth() == 1 &&( !next() || next()->listDepth() < listDepth() ) ) m = TQMAX( m, document()->list_bm ); } m += ubm; return scale( m, TQTextFormat::painter() ); } int TQTextParagraph::leftMargin() const { int m = ulm; if ( listDepth() && !string()->isRightToLeft() ) m += listDepth() * document()->list_lm; return scale( m, TQTextFormat::painter() ); } int TQTextParagraph::firstLineMargin() const { int m = uflm; return scale( m, TQTextFormat::painter() ); } int TQTextParagraph::rightMargin() const { int m = urm; if ( listDepth() && string()->isRightToLeft() ) m += listDepth() * document()->list_lm; return scale( m, TQTextFormat::painter() ); } int TQTextParagraph::lineSpacing() const { int l = ulinespacing; l = scale( l, TQTextFormat::painter() ); return l; } void TQTextParagraph::copyParagData( TQTextParagraph *parag ) { rtext = parag->rtext; lstyle = parag->lstyle; ldepth = parag->ldepth; litem = parag->litem; align = parag->align; utm = parag->utm; ubm = parag->ubm; urm = parag->urm; ulm = parag->ulm; uflm = parag->uflm; ulinespacing = parag->ulinespacing; TQColor *c = parag->backgroundColor(); if ( c ) setBackgroundColor( *c ); str->setDirection( parag->str->direction() ); } void TQTextParagraph::show() { if ( visible || !hasdoc ) return; visible = TRUE; } void TQTextParagraph::hide() { if ( !visible || !hasdoc ) return; visible = FALSE; } void TQTextParagraph::setDirection( TQChar::Direction d ) { if ( str && str->direction() != d ) { str->setDirection( d ); tqinvalidate( 0 ); } } TQChar::Direction TQTextParagraph::direction() const { return (str ? str->direction() : TQChar::DirON ); } void TQTextParagraph::setChanged( bool b, bool recursive ) { changed = b; if ( recursive ) { if ( document() && document()->parentParagraph() ) document()->parentParagraph()->setChanged( b, recursive ); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextPreProcessor::TQTextPreProcessor() { } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatter::TQTextFormatter() : thisminw(0), thiswused(0), wrapEnabled( TRUE ), wrapColumn( -1 ), biw( FALSE ) { } TQTextLineStart *TQTextFormatter::formatLine( TQTextParagraph *parag, TQTextString *string, TQTextLineStart *line, TQTextStringChar *startChar, TQTextStringChar *lastChar, int align, int space ) { if ( lastChar < startChar ) return new TQTextLineStart; #ifndef TQT_NO_COMPLEXTEXT if( string->isBidi() ) return bidiReorderLine( parag, string, line, startChar, lastChar, align, space ); #endif int start = (startChar - &string->at(0)); int last = (lastChar - &string->at(0) ); // ignore white space at the end of the line. TQTextStringChar *ch = lastChar; while ( ch > startChar && ch->whiteSpace ) { space += ch->format()->width( ' ' ); --ch; } if (space < 0) space = 0; // do tqalignment Auto == Left in this case if ( align & TQt::AlignHCenter || align & TQt::AlignRight ) { if ( align & TQt::AlignHCenter ) space /= 2; for ( int j = start; j <= last; ++j ) string->at( j ).x += space; } else if ( align & TQt::AlignJustify ) { int numSpaces = 0; // End at "last-1", the last space ends up with a width of 0 for ( int j = last-1; j >= start; --j ) { // Start at last tab, if any. TQTextStringChar &ch = string->at( j ); if ( ch.c == '\t' ) { start = j+1; break; } if(ch.whiteSpace) numSpaces++; } int toAdd = 0; for ( int k = start + 1; k <= last; ++k ) { TQTextStringChar &ch = string->at( k ); if( numSpaces && ch.whiteSpace ) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } string->at( k ).x += toAdd; } } if ( last >= 0 && last < string->length() ) line->w = string->at( last ).x + string->width( last ); else line->w = 0; return new TQTextLineStart; } #ifndef TQT_NO_COMPLEXTEXT #ifdef BIDI_DEBUG #include #endif // collects one line of the paragraph and transforms it to visual order TQTextLineStart *TQTextFormatter::bidiReorderLine( TQTextParagraph * /*parag*/, TQTextString *text, TQTextLineStart *line, TQTextStringChar *startChar, TQTextStringChar *lastChar, int align, int space ) { // ignore white space at the end of the line. int endSpaces = 0; while ( lastChar > startChar && lastChar->whiteSpace ) { space += lastChar->format()->width( ' ' ); --lastChar; ++endSpaces; } int start = (startChar - &text->at(0)); int last = (lastChar - &text->at(0) ); int length = lastChar - startChar + 1; int x = startChar->x; unsigned char _levels[256]; int _visual[256]; unsigned char *levels = _levels; int *visual = _visual; if ( length > 255 ) { levels = (unsigned char *)malloc( length*sizeof( unsigned char ) ); visual = (int *)malloc( length*sizeof( int ) ); } //qDebug("bidiReorderLine: length=%d (%d-%d)", length, start, last ); TQTextStringChar *ch = startChar; unsigned char *l = levels; while ( ch <= lastChar ) { //qDebug( " level: %d", ch->bidiLevel ); *(l++) = (ch++)->bidiLevel; } TQTextEngine::bidiReorder( length, levels, visual ); // now construct the reordered string out of the runs... int numSpaces = 0; // set the correct tqalignment. This is a bit messy.... if( align == TQt::AlignAuto ) { // align according to directionality of the paragraph... if ( text->isRightToLeft() ) align = TQt::AlignRight; } // This is not really correct, but as we can't make the scrollbar move to the left of the origin, // this ensures all text can be scrolled to and read. if (space < 0) space = 0; if ( align & TQt::AlignHCenter ) x += space/2; else if ( align & TQt::AlignRight ) x += space; else if ( align & TQt::AlignJustify ) { // End at "last-1", the last space ends up with a width of 0 for ( int j = last-1; j >= start; --j ) { // Start at last tab, if any. TQTextStringChar &ch = text->at( j ); if ( ch.c == '\t' ) { start = j+1; break; } if(ch.whiteSpace) numSpaces++; } } int toAdd = 0; int xorig = x; TQTextStringChar *lc = startChar + visual[0]; for ( int i = 0; i < length; i++ ) { TQTextStringChar *ch = startChar + visual[i]; if (numSpaces && ch->whiteSpace) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } if (lc->format() != ch->format() && !ch->c.isSpace() && lc->format()->font().italic() && !ch->format()->font().italic()) { int rb = lc->format()->fontMetrics().rightBearing(lc->c); if (rb < 0) x -= rb; } ch->x = x + toAdd; ch->rightToLeft = ch->bidiLevel % 2; //qDebug("visual: %d (%x) placed at %d rightToLeft=%d", visual[i], ch->c.tqunicode(), x +toAdd, ch->rightToLeft ); int ww = 0; if ( ch->c.tqunicode() >= 32 || ch->c == '\t' || ch->c == '\n' || ch->isCustom() ) { ww = text->width( start+visual[i] ); } else { ww = ch->format()->width( ' ' ); } x += ww; lc = ch; } x += toAdd; while ( endSpaces-- ) { ++lastChar; int sw = lastChar->format()->width( ' ' ); if ( text->isRightToLeft() ) { xorig -= sw; lastChar->x = xorig; ch->rightToLeft = TRUE; } else { lastChar->x = x; x += sw; ch->rightToLeft = FALSE; } } line->w = x; if ( length > 255 ) { free( levels ); free( visual ); } return new TQTextLineStart; } #endif void TQTextFormatter::insertLineStart( TQTextParagraph *parag, int index, TQTextLineStart *ls ) { QMap::Iterator it; if ( ( it = parag->lineStartList().find( index ) ) == parag->lineStartList().end() ) { parag->lineStartList().insert( index, ls ); } else { delete *it; parag->lineStartList().remove( it ); parag->lineStartList().insert( index, ls ); } } /* Standard pagebreak algorithm using TQTextFlow::adjustFlow. Returns the shift of the paragraphs bottom line. */ int TQTextFormatter::formatVertically( TQTextDocument* doc, TQTextParagraph* parag ) { int oldHeight = parag->rect().height(); QMap& lineStarts = parag->lineStartList(); QMap::Iterator it = lineStarts.begin(); int h = parag->prev() ? TQMAX(parag->prev()->bottomMargin(),parag->topMargin() ) / 2: 0; for ( ; it != lineStarts.end() ; ++it ) { TQTextLineStart * ls = it.data(); ls->y = h; TQTextStringChar *c = ¶g->string()->at(it.key()); #ifndef TQT_NO_TEXTCUSTOMITEM if ( c && c->customItem() && c->customItem()->ownLine() ) { int h = c->customItem()->height; c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() ); int delta = c->customItem()->height - h; ls->h += delta; if ( delta ) parag->setMovedDown( TRUE ); } else #endif { int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h ); ls->y += shift; if ( shift ) parag->setMovedDown( TRUE ); } h = ls->y + ls->h; } int m = parag->bottomMargin(); if ( !parag->next() ) m = 0; else m = TQMAX(m, parag->next()->topMargin() ) / 2; h += m; parag->setHeight( h ); return h - oldHeight; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatterBreakInWords::TQTextFormatterBreakInWords() { } #define SPACE(s) s int TQTextFormatterBreakInWords::format( TQTextDocument *doc,TQTextParagraph *parag, int start, const QMap & ) { // make sure bidi information is correct. (void )parag->string()->isBidi(); TQTextStringChar *c = 0; TQTextStringChar *firstChar = 0; int left = doc ? parag->leftMargin() + doc->leftMargin() : 0; int x = left + ( doc ? parag->firstLineMargin() : 0 ); int dw = parag->documentVisibleWidth() - ( doc ? doc->rightMargin() : 0 ); int y = parag->prev() ? TQMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; int h = y; int len = parag->length(); if ( doc ) x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 4 ); int rm = parag->rightMargin(); int w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); bool fullWidth = TRUE; int minw = 0; int wused = 0; bool wrapEnabled = isWrapEnabled( parag ); start = 0; //######### what is the point with start?! (Matthias) if ( start == 0 ) c = ¶g->string()->at( 0 ); int i = start; TQTextLineStart *lineStart = new TQTextLineStart( y, y, 0 ); insertLineStart( parag, 0, lineStart ); TQPainter *painter = TQTextFormat::painter(); int col = 0; int ww = 0; TQChar lastChr; for ( ; i < len; ++i, ++col ) { if ( c ) lastChr = c->c; c = ¶g->string()->at( i ); // ### the lines below should not be needed if ( painter ) c->format()->setPainter( painter ); if ( i > 0 ) { c->lineStart = 0; } else { c->lineStart = 1; firstChar = c; } if ( c->c.tqunicode() >= 32 || c->isCustom() ) { ww = parag->string()->width( i ); } else if ( c->c == '\t' ) { int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } else { ww = c->format()->width( ' ' ); } #ifndef TQT_NO_TEXTCUSTOMITEM if ( c->isCustom() && c->customItem()->ownLine() ) { x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); c->customItem()->resize( w - x ); w = dw; y += h; h = c->height(); lineStart = new TQTextLineStart( y, h, h ); insertLineStart( parag, i, lineStart ); c->lineStart = 1; firstChar = c; x = 0xffffff; continue; } #endif if ( wrapEnabled && ( ((wrapAtColumn() == -1) && (x + ww > w)) || ((wrapAtColumn() != -1) && (col >= wrapAtColumn())) ) ) { x = doc ? parag->document()->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw; y += h; h = c->height(); lineStart = formatLine( parag, parag->string(), lineStart, firstChar, c-1 ); lineStart->y = y; insertLineStart( parag, i, lineStart ); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; col = 0; if ( wrapAtColumn() != -1 ) minw = TQMAX( minw, w ); } else if ( lineStart ) { lineStart->baseLine = TQMAX( lineStart->baseLine, c->ascent() ); h = TQMAX( h, c->height() ); lineStart->h = h; } c->x = x; x += ww; wused = TQMAX( wused, x ); } int m = parag->bottomMargin(); if ( !parag->next() ) m = 0; else m = TQMAX(m, parag->next()->topMargin() ) / 2; parag->setFullWidth( fullWidth ); y += h + m; if ( doc ) minw += doc->rightMargin(); if ( !wrapEnabled ) minw = TQMAX(minw, wused); thisminw = minw; thiswused = wused; return y; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatterBreakWords::TQTextFormatterBreakWords() { } #define DO_FLOW( lineStart ) do{ if ( doc && doc->isPageBreakEnabled() ) { \ int yflow = lineStart->y + parag->rect().y();\ int shift = doc->flow()->adjustFlow( yflow, dw, lineStart->h ); \ lineStart->y += shift;\ y += shift;\ }}while(FALSE) int TQTextFormatterBreakWords::format( TQTextDocument *doc, TQTextParagraph *parag, int start, const QMap & ) { // make sure bidi information is correct. (void )parag->string()->isBidi(); TQTextStringChar *c = 0; TQTextStringChar *firstChar = 0; TQTextString *string = parag->string(); int left = doc ? parag->leftMargin() + doc->leftMargin() : 0; int x = left + ( doc ? parag->firstLineMargin() : 0 ); int y = parag->prev() ? TQMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; int h = y; int len = parag->length(); if ( doc ) x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 0 ); int dw = parag->documentVisibleWidth() - ( doc ? ( left != x ? 0 : doc->rightMargin() ) : 0 ); int curLeft = x; int rm = parag->rightMargin(); int rdiff = doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 0 ) : 0; int w = dw - rdiff; bool fullWidth = TRUE; int marg = left + rdiff; int minw = 0; int wused = 0; int tminw = marg; int linespacing = doc ? parag->lineSpacing() : 0; bool wrapEnabled = isWrapEnabled( parag ); start = 0; int i = start; TQTextLineStart *lineStart = new TQTextLineStart( y, y, 0 ); insertLineStart( parag, 0, lineStart ); int lastBreak = -1; int tmpBaseLine = 0, tmph = 0; bool lastWasNonInlineCustom = FALSE; int align = parag->tqalignment(); if ( align == TQt::AlignAuto && doc && doc->tqalignment() != TQt::AlignAuto ) align = doc->tqalignment(); align &= TQt::AlignHorizontal_Mask; // ### hack. The last char in the paragraph is always invisible, // ### and somehow sometimes has a wrong format. It changes // ### between // layouting and printing. This corrects some // ### layouting errors in BiDi mode due to this. if ( len > 1 ) { c = ¶g->string()->at(len - 1); if (!c->isAnchor()) { if (c->format()) c->format()->removeRef(); c->setFormat( string->at( len - 2 ).format() ); if (c->format()) c->format()->addRef(); } } c = ¶g->string()->at( 0 ); TQPainter *painter = TQTextFormat::painter(); int col = 0; int ww = 0; TQChar lastChr = c->c; TQTextFormat *lastFormat = c->format(); for ( ; i < len; ++i, ++col ) { if ( i ) { c = ¶g->string()->at(i-1); lastChr = c->c; lastFormat = c->format(); } bool lastWasOwnLineCustomItem = lastBreak == -2; bool hadBreakableChar = lastBreak != -1; bool lastWasHardBreak = lastChr == TQChar_linesep; // ### next line should not be needed if ( painter ) c->format()->setPainter( painter ); c = &string->at( i ); if (lastFormat != c->format() && !c->c.isSpace() && lastFormat->font().italic() && !c->format()->font().italic()) { int rb = lastFormat->fontMetrics().rightBearing(lastChr); if (rb < 0) x -= rb; } if ( ((i > 0) && (x > curLeft || ww == 0)) || lastWasNonInlineCustom ) { c->lineStart = 0; } else { c->lineStart = 1; firstChar = c; } // ignore non spacing marks for column count. if (col != 0 && ::category(c->c) == TQChar::Mark_NonSpacing) --col; #ifndef TQT_NO_TEXTCUSTOMITEM lastWasNonInlineCustom = ( c->isCustom() && c->customItem()->placement() != TQTextCustomItem::PlaceInline ); #endif if ( c->c.tqunicode() >= 32 || c->isCustom() ) { ww = string->width( i ); } else if ( c->c == '\t' ) { if ( align == TQt::AlignRight || align == TQt::AlignCenter ) { // we can not (yet) do tabs ww = c->format()->width(' ' ); } else { int tabx = lastWasHardBreak ? (left + ( doc ? parag->firstLineMargin() : 0 )) : x; int nx = parag->nextTab( i, tabx - left ) + left; if ( nx < tabx ) // strrrange... ww = 0; else ww = nx - tabx; } } else { ww = c->format()->width( ' ' ); } #ifndef TQT_NO_TEXTCUSTOMITEM TQTextCustomItem* ci = c->customItem(); if ( c->isCustom() && ci->ownLine() ) { TQTextLineStart *lineStart2 = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x - ww) ); x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); ci->resize(w - x); if ( ci->width < w - x ) { if ( align & TQt::AlignHCenter ) x = ( w - ci->width ) / 2; else if ( align & TQt::AlignRight ) { x = w - ci->width; } } c->x = x; curLeft = x; if ( i == 0 || !isBreakable(string, i-1) || string->at( i - 1 ).lineStart == 0 ) { y += TQMAX( h, TQMAX( tmph, linespacing ) ); tmph = c->height(); h = tmph; lineStart = lineStart2; lineStart->y = y; insertLineStart( parag, i, lineStart ); c->lineStart = 1; firstChar = c; } else { tmph = c->height(); h = tmph; delete lineStart2; } lineStart->h = h; lineStart->baseLine = h; tmpBaseLine = lineStart->baseLine; lastBreak = -2; x = w; minw = TQMAX( minw, tminw ); int tw = ci->minimumWidth() + ( doc ? doc->leftMargin() : 0 ); if ( tw < TQWIDGETSIZE_MAX ) tminw = tw; else tminw = marg; wused = TQMAX( wused, ci->width ); continue; } else if ( c->isCustom() && ci->placement() != TQTextCustomItem::PlaceInline ) { int tw = ci->minimumWidth(); if ( tw < TQWIDGETSIZE_MAX ) minw = TQMAX( minw, tw ); } #endif // we break if // 1. the last character was a hard break (TQChar_linesep) or // 2. the last charater was a own-line custom item (eg. table or ruler) or // 3. wrapping was enabled, it was not a space and following // condition is true: We either had a breakable character // previously or we ar allowed to break in words and - either // we break at w pixels and the current char would exceed that // or - we break at a column and the current character would // exceed that. if ( lastWasHardBreak || lastWasOwnLineCustomItem || ( wrapEnabled && ( (!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) && ( (wrapAtColumn() == -1 && x + ww > w) || (wrapAtColumn() != -1 && col >= wrapAtColumn()) ) ) ) ) ) { if ( wrapAtColumn() != -1 ) minw = TQMAX( minw, x + ww ); // if a break was forced (no breakable char, hard break or own line custom item), break immediately.... if ( !hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem ) { if ( lineStart ) { lineStart->baseLine = TQMAX( lineStart->baseLine, tmpBaseLine ); h = TQMAX( h, tmph ); lineStart->h = h; DO_FLOW( lineStart ); } lineStart = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x) ); x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); if ( !doc && c->c == '\t' ) { // qt_format_text tab handling int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } curLeft = x; y += TQMAX( h, linespacing ); tmph = c->height(); h = 0; lineStart->y = y; insertLineStart( parag, i, lineStart ); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; tmpBaseLine = lineStart->baseLine; lastBreak = -1; col = 0; if ( allowBreakInWords() || lastWasHardBreak ) { minw = TQMAX(minw, tminw); tminw = marg + ww; } } else { // ... otherwise if we had a breakable char, break there DO_FLOW( lineStart ); c->x = x; i = lastBreak; lineStart = formatLine( parag, string, lineStart, firstChar, parag->at( lastBreak ),align, SPACE(w - string->at( i+1 ).x) ); x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); if ( !doc && c->c == '\t' ) { // qt_format_text tab handling int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } curLeft = x; y += TQMAX( h, linespacing ); tmph = c->height(); h = tmph; lineStart->y = y; insertLineStart( parag, i + 1, lineStart ); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; tmpBaseLine = lineStart->baseLine; lastBreak = -1; col = 0; minw = TQMAX(minw, tminw); tminw = marg; continue; } } else if (lineStart && isBreakable(string, i)) { if ( len <= 2 || i < len - 1 ) { tmpBaseLine = TQMAX( tmpBaseLine, c->ascent() ); tmph = TQMAX( tmph, c->height() ); } minw = TQMAX( minw, tminw ); tminw = marg + ww; lineStart->baseLine = TQMAX( lineStart->baseLine, tmpBaseLine ); h = TQMAX( h, tmph ); lineStart->h = h; if ( i < len - 2 || c->c != ' ' ) lastBreak = i; } else { tminw += ww; int cascent = c->ascent(); int cheight = c->height(); int belowBaseLine = TQMAX( tmph - tmpBaseLine, cheight-cascent ); tmpBaseLine = TQMAX( tmpBaseLine, cascent ); tmph = tmpBaseLine + belowBaseLine; } c->x = x; x += ww; wused = TQMAX( wused, x ); } if ( lineStart ) { lineStart->baseLine = TQMAX( lineStart->baseLine, tmpBaseLine ); h = TQMAX( h, tmph ); lineStart->h = h; // last line in a paragraph is not justified if ( align == TQt::AlignJustify ) align = TQt::AlignAuto; DO_FLOW( lineStart ); lineStart = formatLine( parag, string, lineStart, firstChar, c, align, SPACE(w - x) ); delete lineStart; } minw = TQMAX( minw, tminw ); if ( doc ) minw += doc->rightMargin(); int m = parag->bottomMargin(); if ( !parag->next() ) m = 0; else m = TQMAX(m, parag->next()->topMargin() ) / 2; parag->setFullWidth( fullWidth ); y += TQMAX( h, linespacing ) + m; wused += rm; if ( !wrapEnabled || wrapAtColumn() != -1 ) minw = TQMAX(minw, wused); // This is the case where we are breaking wherever we darn well please // in cases like that, the minw should not be the length of the entire // word, because we necessarily want to show the word on the whole line. // example: word wrap in iconview if ( allowBreakInWords() && minw > wused ) minw = wused; thisminw = minw; thiswused = wused; return y; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextIndent::TQTextIndent() { } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TQTextFormatCollection::TQTextFormatCollection() : cKey( 307 ), painttqdevice( 0 ) { defFormat = new TQTextFormat( TQApplication::font(), TQApplication::palette().color( TQPalette::Active, TQColorGroup::Text ) ); lastFormat = cres = 0; cflags = -1; cKey.setAutoDelete( TRUE ); cachedFormat = 0; } TQTextFormatCollection::~TQTextFormatCollection() { delete defFormat; } void TQTextFormatCollection::setPaintDevice( TQPaintDevice *pd ) { painttqdevice = pd; #if defined(TQ_WS_X11) int scr = ( painttqdevice ) ? painttqdevice->x11Screen() : TQPaintDevice::x11AppScreen(); defFormat->fn.tqt_x11SetScreen( scr ); defFormat->update(); TQDictIterator it( cKey ); TQTextFormat *format; while ( ( format = it.current() ) != 0 ) { ++it; format->fn.tqt_x11SetScreen( scr ); format->update(); } #endif // TQ_WS_X11 } TQTextFormat *TQTextFormatCollection::format( TQTextFormat *f ) { if ( f->tqparent() == this || f == defFormat ) { lastFormat = f; lastFormat->addRef(); return lastFormat; } if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) { lastFormat->addRef(); return lastFormat; } TQTextFormat *fm = cKey.find( f->key() ); if ( fm ) { lastFormat = fm; lastFormat->addRef(); return lastFormat; } if ( f->key() == defFormat->key() ) return defFormat; lastFormat = createFormat( *f ); lastFormat->collection = this; cKey.insert( lastFormat->key(), lastFormat ); return lastFormat; } TQTextFormat *TQTextFormatCollection::format( TQTextFormat *of, TQTextFormat *nf, int flags ) { if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) { cres->addRef(); return cres; } cres = createFormat( *of ); kof = of->key(); knf = nf->key(); cflags = flags; if ( flags & TQTextFormat::Bold ) cres->fn.setBold( nf->fn.bold() ); if ( flags & TQTextFormat::Italic ) cres->fn.setItalic( nf->fn.italic() ); if ( flags & TQTextFormat::Underline ) cres->fn.setUnderline( nf->fn.underline() ); if ( flags & TQTextFormat::StrikeOut ) cres->fn.setStrikeOut( nf->fn.strikeOut() ); if ( flags & TQTextFormat::Family ) cres->fn.setFamily( nf->fn.family() ); if ( flags & TQTextFormat::Size ) { if ( of->usePixelSizes ) cres->fn.setPixelSize( nf->fn.pixelSize() ); else cres->fn.setPointSize( nf->fn.pointSize() ); } if ( flags & TQTextFormat::Color ) cres->col = nf->col; if ( flags & TQTextFormat::Misspelled ) cres->missp = nf->missp; if ( flags & TQTextFormat::VAlign ) cres->ha = nf->ha; cres->update(); TQTextFormat *fm = cKey.find( cres->key() ); if ( !fm ) { cres->collection = this; cKey.insert( cres->key(), cres ); } else { delete cres; cres = fm; cres->addRef(); } return cres; } TQTextFormat *TQTextFormatCollection::format( const TQFont &f, const TQColor &c ) { if ( cachedFormat && cfont == f && ccol == c ) { cachedFormat->addRef(); return cachedFormat; } TQString key = TQTextFormat::getKey( f, c, FALSE, TQTextFormat::AlignNormal ); cachedFormat = cKey.find( key ); cfont = f; ccol = c; if ( cachedFormat ) { cachedFormat->addRef(); return cachedFormat; } if ( key == defFormat->key() ) return defFormat; cachedFormat = createFormat( f, c ); cachedFormat->collection = this; cKey.insert( cachedFormat->key(), cachedFormat ); if ( cachedFormat->key() != key ) qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1() ); return cachedFormat; } void TQTextFormatCollection::remove( TQTextFormat *f ) { if ( lastFormat == f ) lastFormat = 0; if ( cres == f ) cres = 0; if ( cachedFormat == f ) cachedFormat = 0; if (cKey.find(f->key()) == f) cKey.remove( f->key() ); } #define UPDATE( up, lo, rest ) \ if ( font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest() ) \ fm->fn.set##up##rest( font.lo##rest() ) void TQTextFormatCollection::updateDefaultFormat( const TQFont &font, const TQColor &color, TQStyleSheet *sheet ) { TQDictIterator it( cKey ); TQTextFormat *fm; bool usePixels = font.pointSize() == -1; bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() : font.pointSize() != defFormat->fn.pointSize(); int base = usePixels ? font.pixelSize() : font.pointSize(); while ( ( fm = it.current() ) ) { ++it; UPDATE( F, f, amily ); UPDATE( W, w, eight ); UPDATE( B, b, old ); UPDATE( I, i, talic ); UPDATE( U, u, nderline ); if ( changeSize ) { fm->stdSize = base; fm->usePixelSizes = usePixels; if ( usePixels ) fm->fn.setPixelSize( fm->stdSize ); else fm->fn.setPointSize( fm->stdSize ); sheet->scaleFont( fm->fn, fm->logicalFontSize ); } if ( color.isValid() && color != defFormat->col && fm->col == defFormat->col ) fm->col = color; fm->update(); } defFormat->fn = font; defFormat->col = color; defFormat->update(); defFormat->stdSize = base; defFormat->usePixelSizes = usePixels; updateKeys(); } // the keys in cKey have changed, rebuild the hashtable void TQTextFormatCollection::updateKeys() { if ( cKey.isEmpty() ) return; cKey.setAutoDelete( FALSE ); TQTextFormat** formats = new TQTextFormat*[ cKey.count() + 1 ]; TQTextFormat **f = formats; TQDictIterator it( cKey ); while ( ( *f = it.current() ) ) { ++it; ++f; } cKey.clear(); for ( f = formats; *f; f++ ) cKey.insert( (*f)->key(), *f ); cKey.setAutoDelete( TRUE ); delete [] formats; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void TQTextFormat::setBold( bool b ) { if ( b == fn.bold() ) return; fn.setBold( b ); update(); } void TQTextFormat::setMisspelled( bool b ) { if ( b == (bool)missp ) return; missp = b; update(); } void TQTextFormat::setVAlign( VerticalAlignment a ) { if ( a == ha ) return; ha = a; update(); } void TQTextFormat::setItalic( bool b ) { if ( b == fn.italic() ) return; fn.setItalic( b ); update(); } void TQTextFormat::setUnderline( bool b ) { if ( b == fn.underline() ) return; fn.setUnderline( b ); update(); } void TQTextFormat::setStrikeOut( bool b ) { if ( b == fn.strikeOut() ) return; fn.setStrikeOut( b ); update(); } void TQTextFormat::setFamily( const TQString &f ) { if ( f == fn.family() ) return; fn.setFamily( f ); update(); } void TQTextFormat::setPointSize( int s ) { if ( s == fn.pointSize() ) return; fn.setPointSize( s ); usePixelSizes = FALSE; update(); } void TQTextFormat::setFont( const TQFont &f ) { if ( f == fn && !k.isEmpty() ) return; fn = f; update(); } void TQTextFormat::setColor( const TQColor &c ) { if ( c == col ) return; col = c; update(); } TQString TQTextFormat::makeFormatChangeTags( TQTextFormat* defaultFormat, TQTextFormat *f, const TQString& oldAnchorHref, const TQString& anchorHref ) const { TQString tag; if ( f ) tag += f->makeFormatEndTags( defaultFormat, oldAnchorHref ); if ( !anchorHref.isEmpty() ) tag += ""; if ( font() != defaultFormat->font() || vAlign() != defaultFormat->vAlign() || color().rgb() != defaultFormat->color().rgb() ) { TQString s; if ( font().family() != defaultFormat->font().family() ) s += TQString(!!s?";":"") + "font-family:" + fn.family(); if ( font().italic() && font().italic() != defaultFormat->font().italic() ) s += TQString(!!s?";":"") + "font-style:" + (font().italic() ? "italic" : "normal"); if ( font().pointSize() != defaultFormat->font().pointSize() ) s += TQString(!!s?";":"") + "font-size:" + TQString::number( fn.pointSize() ) + "pt"; if ( font().weight() != defaultFormat->font().weight() ) s += TQString(!!s?";":"") + "font-weight:" + TQString::number( fn.weight() * 8 ); TQString textDecoration; bool none = FALSE; if ( font().underline() != defaultFormat->font().underline() ) { if (font().underline()) textDecoration = "underline"; else none = TRUE; } if ( font().overline() != defaultFormat->font().overline() ) { if (font().overline()) textDecoration += " overline"; else none = TRUE; } if ( font().strikeOut() != defaultFormat->font().strikeOut() ) { if (font().strikeOut()) textDecoration += " line-through"; else none = TRUE; } if (none && textDecoration.isEmpty()) textDecoration = "none"; if (!textDecoration.isEmpty()) s += TQString(!!s?";":"") + "text-decoration:" + textDecoration; if ( vAlign() != defaultFormat->vAlign() ) { s += TQString(!!s?";":"") + "vertical-align:"; if ( vAlign() == TQTextFormat::AlignSuperScript ) s += "super"; else if ( vAlign() == TQTextFormat::AlignSubScript ) s += "sub"; else s += "normal"; } if ( color().rgb() != defaultFormat->color().rgb() ) s += TQString(!!s?";":"") + "color:" + col.name(); if ( !s.isEmpty() ) tag += ""; } return tag; } TQString TQTextFormat::makeFormatEndTags( TQTextFormat* defaultFormat, const TQString& anchorHref ) const { TQString tag; if ( font().family() != defaultFormat->font().family() || font().pointSize() != defaultFormat->font().pointSize() || font().weight() != defaultFormat->font().weight() || font().italic() != defaultFormat->font().italic() || font().underline() != defaultFormat->font().underline() || font().strikeOut() != defaultFormat->font().strikeOut() || vAlign() != defaultFormat->vAlign() || color().rgb() != defaultFormat->color().rgb() ) tag += ""; if ( !anchorHref.isEmpty() ) tag += ""; return tag; } TQTextFormat TQTextFormat::makeTextFormat( const TQStyleSheetItem *style, const QMap& attr, double scaleFontsFactor ) const { TQTextFormat format(*this); if (!style ) return format; if ( !style->isAnchor() && style->color().isValid() ) { // the style is not an anchor and defines a color. // It might be used inside an anchor and it should // override the link color. format.linkColor = FALSE; } switch ( style->verticalAlignment() ) { case TQStyleSheetItem::VAlignBaseline: format.setVAlign( TQTextFormat::AlignNormal ); break; case TQStyleSheetItem::VAlignSuper: format.setVAlign( TQTextFormat::AlignSuperScript ); break; case TQStyleSheetItem::VAlignSub: format.setVAlign( TQTextFormat::AlignSubScript ); break; } if ( style->fontWeight() != TQStyleSheetItem::Undefined ) format.fn.setWeight( style->fontWeight() ); if ( style->fontSize() != TQStyleSheetItem::Undefined ) { format.fn.setPointSize( style->fontSize() ); } else if ( style->logicalFontSize() != TQStyleSheetItem::Undefined ) { format.logicalFontSize = style->logicalFontSize(); if ( format.usePixelSizes ) format.fn.setPixelSize( format.stdSize ); else format.fn.setPointSize( format.stdSize ); style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); } else if ( style->logicalFontSizeStep() ) { format.logicalFontSize += style->logicalFontSizeStep(); if ( format.usePixelSizes ) format.fn.setPixelSize( format.stdSize ); else format.fn.setPointSize( format.stdSize ); style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); } if ( !style->fontFamily().isEmpty() ) format.fn.setFamily( style->fontFamily() ); if ( style->color().isValid() ) format.col = style->color(); if ( style->definesFontItalic() ) format.fn.setItalic( style->fontItalic() ); if ( style->definesFontUnderline() ) format.fn.setUnderline( style->fontUnderline() ); if ( style->definesFontStrikeOut() ) format.fn.setStrikeOut( style->fontStrikeOut() ); if ( style->name() == "font") { if ( attr.contains("color") ) { TQString s = attr["color"]; if ( !s.isEmpty() ) { format.col.setNamedColor( s ); format.linkColor = FALSE; } } if ( attr.contains("face") ) { TQString a = attr["face"]; TQString family = a.section( ',', 0, 0 ); if ( !!family ) format.fn.setFamily( family ); } if ( attr.contains("size") ) { TQString a = attr["size"]; int n = a.toInt(); if ( a[0] == '+' || a[0] == '-' ) n += 3; format.logicalFontSize = n; if ( format.usePixelSizes ) format.fn.setPixelSize( format.stdSize ); else format.fn.setPointSize( format.stdSize ); style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); } } if ( attr.contains("style" ) ) { TQString a = attr["style"]; for ( int s = 0; s < a.contains(';')+1; s++ ) { TQString style = a.section( ';', s, s ); if ( style.startsWith("font-size:" ) && style.endsWith("pt") ) { format.logicalFontSize = 0; int size = int( scaleFontsFactor * style.mid( 10, style.length() - 12 ).toDouble() ); format.setPointSize( size ); } else if ( style.startsWith("font-style:" ) ) { TQString s = TQT_TQSTRING(style.mid( 11 )).stripWhiteSpace(); if ( s == "normal" ) format.fn.setItalic( FALSE ); else if ( s == "italic" || s == "oblique" ) format.fn.setItalic( TRUE ); } else if ( style.startsWith("font-weight:" ) ) { TQString s = style.mid( 12 ); bool ok = TRUE; int n = s.toInt( &ok ); if ( ok ) format.fn.setWeight( n/8 ); } else if ( style.startsWith("font-family:" ) ) { TQString family = style.mid(12).section(',',0,0); family.replace( '\"', ' ' ); family.replace( '\'', ' ' ); family = family.stripWhiteSpace(); format.fn.setFamily( family ); } else if ( style.startsWith("text-decoration:" ) ) { TQString s = style.mid( 16 ); format.fn.setOverline( s.find("overline") != -1 ); format.fn.setStrikeOut( s.find("line-through") != -1 ); format.fn.setUnderline( s.find("underline") != -1 ); } else if ( style.startsWith("vertical-align:" ) ) { TQString s = TQT_TQSTRING(style.mid( 15 )).stripWhiteSpace(); if ( s == "sub" ) format.setVAlign( TQTextFormat::AlignSubScript ); else if ( s == "super" ) format.setVAlign( TQTextFormat::AlignSuperScript ); else format.setVAlign( TQTextFormat::AlignNormal ); } else if ( style.startsWith("color:" ) ) { format.col.setNamedColor( style.mid(6) ); format.linkColor = FALSE; } } } format.update(); return format; } #ifndef TQT_NO_TEXTCUSTOMITEM struct TQPixmapInt { TQPixmapInt() : ref( 0 ) {} TQPixmap pm; int ref; TQ_DUMMY_COMPARISON_OPERATOR(TQPixmapInt) }; static QMap *pixmap_map = 0; TQTextImage::TQTextImage( TQTextDocument *p, const QMap &attr, const TQString& context, TQMimeSourceFactory &factory ) : TQTextCustomItem( p ) { width = height = 0; if ( attr.contains("width") ) width = attr["width"].toInt(); if ( attr.contains("height") ) height = attr["height"].toInt(); reg = 0; TQString imageName = attr["src"]; if (!imageName) imageName = attr["source"]; if ( !imageName.isEmpty() ) { imgId = TQString( "%1,%2,%3,%4" ).arg( imageName ).arg( width ).arg( height ).arg( (ulong)&factory ); if ( !pixmap_map ) pixmap_map = new QMap; if ( pixmap_map->contains( imgId ) ) { TQPixmapInt& pmi = pixmap_map->operator[](imgId); pm = pmi.pm; pmi.ref++; width = pm.width(); height = pm.height(); } else { TQImage img; const TQMimeSource* m = factory.data( imageName, context ); if ( !m ) { qWarning("TQTextImage: no mimesource for %s", imageName.latin1() ); } else { if ( !TQImageDrag::decode( m, img ) ) { qWarning("TQTextImage: cannot decode %s", imageName.latin1() ); } } if ( !img.isNull() ) { if ( width == 0 ) { width = img.width(); if ( height != 0 ) { width = img.width() * height / img.height(); } } if ( height == 0 ) { height = img.height(); if ( width != img.width() ) { height = img.height() * width / img.width(); } } if ( img.width() != width || img.height() != height ){ #ifndef TQT_NO_IMAGE_SMOOTHSCALE img = img.smoothScale(width, height); #endif width = img.width(); height = img.height(); } pm.convertFromImage( img ); } if ( !pm.isNull() ) { TQPixmapInt& pmi = pixmap_map->operator[](imgId); pmi.pm = pm; pmi.ref++; } } if ( pm.tqmask() ) { TQRegion tqmask( *pm.tqmask() ); TQRegion all( 0, 0, pm.width(), pm.height() ); reg = new TQRegion( all.subtract( tqmask ) ); } } if ( pm.isNull() && (width*height)==0 ) width = height = 50; place = PlaceInline; if ( attr["align"] == "left" ) place = PlaceLeft; else if ( attr["align"] == "right" ) place = PlaceRight; tmpwidth = width; tmpheight = height; attributes = attr; } TQTextImage::~TQTextImage() { if ( pixmap_map && pixmap_map->contains( imgId ) ) { TQPixmapInt& pmi = pixmap_map->operator[](imgId); pmi.ref--; if ( !pmi.ref ) { pixmap_map->remove( imgId ); if ( pixmap_map->isEmpty() ) { delete pixmap_map; pixmap_map = 0; } } } delete reg; } TQString TQTextImage::richText() const { TQString s; s += "::ConstIterator it = attributes.begin(); for ( ; it != attributes.end(); ++it ) { s += it.key() + "="; if ( (*it).find( ' ' ) != -1 ) s += "\"" + *it + "\"" + " "; else s += *it + " "; } s += ">"; return s; } void TQTextImage::adjustToPainter( TQPainter* p ) { width = scale( tmpwidth, p ); height = scale( tmpheight, p ); } #if !defined(TQ_WS_X11) #include #include static TQPixmap *qrt_selection = 0; static TQSingleCleanupHandler qrt_cleanup_pixmap; static void qrt_createSelectionPixmap( const TQColorGroup &cg ) { qrt_selection = new TQPixmap( 2, 2 ); qrt_cleanup_pixmap.set( &qrt_selection ); qrt_selection->fill( TQt::color0 ); TQBitmap m( 2, 2 ); m.fill( TQt::color1 ); TQPainter p( &m ); p.setPen( TQt::color0 ); for ( int j = 0; j < 2; ++j ) { p.drawPoint( j % 2, j ); } p.end(); qrt_selection->setMask( m ); qrt_selection->fill( cg.highlight() ); } #endif void TQTextImage::draw( TQPainter* p, int x, int y, int cx, int cy, int cw, int ch, const TQColorGroup& cg, bool selected ) { if ( placement() != PlaceInline ) { x = xpos; y = ypos; } if ( pm.isNull() ) { p->fillRect( x , y, width, height, cg.dark() ); return; } if ( is_printer( p ) ) { p->drawPixmap( TQRect( x, y, width, height ), pm ); return; } if ( placement() != PlaceInline && !TQRect( xpos, ypos, width, height ).intersects( TQRect( cx, cy, cw, ch ) ) ) return; if ( placement() == PlaceInline ) p->drawPixmap( x , y, pm ); else p->drawPixmap( cx , cy, pm, cx - x, cy - y, cw, ch ); if ( selected && placement() == PlaceInline && is_printer( p ) ) { #if defined(TQ_WS_X11) p->fillRect( TQRect( TQPoint( x, y ), pm.size() ), TQBrush( cg.highlight(), Qt::Dense4Pattern) ); #else // in WIN32 Dense4Pattern doesn't work correctly (transparency problem), so work around it if ( !qrt_selection ) qrt_createSelectionPixmap( cg ); p->drawTiledPixmap( x, y, pm.width(), pm.height(), *qrt_selection ); #endif } } void TQTextHorizontalLine::adjustToPainter( TQPainter* p ) { height = scale( tmpheight, p ); } TQTextHorizontalLine::TQTextHorizontalLine( TQTextDocument *p, const QMap &attr, const TQString &, TQMimeSourceFactory & ) : TQTextCustomItem( p ) { height = tmpheight = 8; if ( attr.find( "color" ) != attr.end() ) color = TQColor( *attr.find( "color" ) ); shade = attr.find( "noshade" ) == attr.end(); } TQTextHorizontalLine::~TQTextHorizontalLine() { } TQString TQTextHorizontalLine::richText() const { return "
"; } void TQTextHorizontalLine::draw( TQPainter* p, int x, int y, int , int , int , int , const TQColorGroup& cg, bool selected ) { TQRect r( x, y, width, height); if ( is_printer( p ) || !shade ) { TQPen oldPen = p->pen(); if ( !color.isValid() ) p->setPen( TQPen( cg.text(), is_printer( p ) ? height/8 : TQMAX( 2, height/4 ) ) ); else p->setPen( TQPen( color, is_printer( p ) ? height/8 : TQMAX( 2, height/4 ) ) ); p->drawLine( r.left()-1, y + height / 2, r.right() + 1, y + height / 2 ); p->setPen( oldPen ); } else { TQColorGroup g( cg ); if ( color.isValid() ) g.setColor( TQColorGroup::Dark, color ); if ( selected ) p->fillRect( r, g.highlight() ); qDrawShadeLine( p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, g, TRUE, height / 8 ); } } #endif //TQT_NO_TEXTCUSTOMITEM /*****************************************************************/ // Small set of utility functions to make the parser a bit simpler // bool TQTextDocument::hasPrefix(const TQChar* doc, int length, int pos, TQChar c) { if ( pos + 1 > length ) return FALSE; return doc[ pos ].lower() == c.lower(); } bool TQTextDocument::hasPrefix( const TQChar* doc, int length, int pos, const TQString& s ) { if ( pos + (int) s.length() > length ) return FALSE; for ( int i = 0; i < (int)s.length(); i++ ) { if ( doc[ pos + i ].lower() != s[ i ].lower() ) return FALSE; } return TRUE; } #ifndef TQT_NO_TEXTCUSTOMITEM static bool qt_is_cell_in_use( TQPtrList& cells, int row, int col ) { for ( TQTextTableCell* c = cells.first(); c; c = cells.next() ) { if ( row >= c->row() && row < c->row() + c->rowspan() && col >= c->column() && col < c->column() + c->colspan() ) return TRUE; } return FALSE; } TQTextCustomItem* TQTextDocument::parseTable( const QMap &attr, const TQTextFormat &fmt, const TQChar* doc, int length, int& pos, TQTextParagraph *curpar ) { TQTextTable* table = new TQTextTable( this, attr ); int row = -1; int col = -1; TQString rowbgcolor; TQString rowalign; TQString tablebgcolor = attr["bgcolor"]; TQPtrList multicells; TQString tagname; (void) eatSpace(doc, length, pos); while ( pos < length) { if (hasPrefix(doc, length, pos, TQChar('<')) ){ if (hasPrefix(doc, length, pos+1, TQChar('/'))) { tagname = parseCloseTag( doc, length, pos ); if ( tagname == "table" ) { return table; } } else { QMap attr2; bool emptyTag = FALSE; tagname = parseOpenTag( doc, length, pos, attr2, emptyTag ); if ( tagname == "tr" ) { rowbgcolor = attr2["bgcolor"]; rowalign = attr2["align"]; row++; col = -1; } else if ( tagname == "td" || tagname == "th" ) { col++; while ( qt_is_cell_in_use( multicells, row, col ) ) { col++; } if ( row >= 0 && col >= 0 ) { const TQStyleSheetItem* s = sheet_->item(tagname); if ( !attr2.contains("bgcolor") ) { if (!rowbgcolor.isEmpty() ) attr2["bgcolor"] = rowbgcolor; else if (!tablebgcolor.isEmpty() ) attr2["bgcolor"] = tablebgcolor; } if ( !attr2.contains("align") ) { if (!rowalign.isEmpty() ) attr2["align"] = rowalign; } // extract the cell contents int end = pos; while ( end < length && !hasPrefix( doc, length, end, "richText()->parentPar = curpar; if ( cell->colspan() > 1 || cell->rowspan() > 1 ) multicells.append( cell ); col += cell->colspan()-1; pos = end; } } } } else { ++pos; } } return table; } #endif // TQT_NO_TEXTCUSTOMITEM bool TQTextDocument::eatSpace(const TQChar* doc, int length, int& pos, bool includeNbsp ) { int old_pos = pos; while (pos < length && doc[pos].isSpace() && ( includeNbsp || (doc[pos] != TQChar::nbsp ) ) ) pos++; return old_pos < pos; } bool TQTextDocument::eat(const TQChar* doc, int length, int& pos, TQChar c) { bool ok = pos < length && doc[pos] == c; if ( ok ) pos++; return ok; } /*****************************************************************/ struct Entity { const char * name; TQ_UINT16 code; }; static const Entity entitylist [] = { { "AElig", 0x00c6 }, { "Aacute", 0x00c1 }, { "Acirc", 0x00c2 }, { "Agrave", 0x00c0 }, { "Alpha", 0x0391 }, { "AMP", 38 }, { "Aring", 0x00c5 }, { "Atilde", 0x00c3 }, { "Auml", 0x00c4 }, { "Beta", 0x0392 }, { "Ccedil", 0x00c7 }, { "Chi", 0x03a7 }, { "Dagger", 0x2021 }, { "Delta", 0x0394 }, { "ETH", 0x00d0 }, { "Eacute", 0x00c9 }, { "Ecirc", 0x00ca }, { "Egrave", 0x00c8 }, { "Epsilon", 0x0395 }, { "Eta", 0x0397 }, { "Euml", 0x00cb }, { "Gamma", 0x0393 }, { "GT", 62 }, { "Iacute", 0x00cd }, { "Icirc", 0x00ce }, { "Igrave", 0x00cc }, { "Iota", 0x0399 }, { "Iuml", 0x00cf }, { "Kappa", 0x039a }, { "Lambda", 0x039b }, { "LT", 60 }, { "Mu", 0x039c }, { "Ntilde", 0x00d1 }, { "Nu", 0x039d }, { "OElig", 0x0152 }, { "Oacute", 0x00d3 }, { "Ocirc", 0x00d4 }, { "Ograve", 0x00d2 }, { "Omega", 0x03a9 }, { "Omicron", 0x039f }, { "Oslash", 0x00d8 }, { "Otilde", 0x00d5 }, { "Ouml", 0x00d6 }, { "Phi", 0x03a6 }, { "Pi", 0x03a0 }, { "Prime", 0x2033 }, { "Psi", 0x03a8 }, { "TQUOT", 34 }, { "Rho", 0x03a1 }, { "Scaron", 0x0160 }, { "Sigma", 0x03a3 }, { "THORN", 0x00de }, { "Tau", 0x03a4 }, { "Theta", 0x0398 }, { "Uacute", 0x00da }, { "Ucirc", 0x00db }, { "Ugrave", 0x00d9 }, { "Upsilon", 0x03a5 }, { "Uuml", 0x00dc }, { "Xi", 0x039e }, { "Yacute", 0x00dd }, { "Yuml", 0x0178 }, { "Zeta", 0x0396 }, { "aacute", 0x00e1 }, { "acirc", 0x00e2 }, { "acute", 0x00b4 }, { "aelig", 0x00e6 }, { "agrave", 0x00e0 }, { "alefsym", 0x2135 }, { "alpha", 0x03b1 }, { "amp", 38 }, { "and", 0x22a5 }, { "ang", 0x2220 }, { "apos", 0x0027 }, { "aring", 0x00e5 }, { "asymp", 0x2248 }, { "atilde", 0x00e3 }, { "auml", 0x00e4 }, { "bdquo", 0x201e }, { "beta", 0x03b2 }, { "brvbar", 0x00a6 }, { "bull", 0x2022 }, { "cap", 0x2229 }, { "ccedil", 0x00e7 }, { "cedil", 0x00b8 }, { "cent", 0x00a2 }, { "chi", 0x03c7 }, { "circ", 0x02c6 }, { "clubs", 0x2663 }, { "cong", 0x2245 }, { "copy", 0x00a9 }, { "crarr", 0x21b5 }, { "cup", 0x222a }, { "curren", 0x00a4 }, { "dArr", 0x21d3 }, { "dagger", 0x2020 }, { "darr", 0x2193 }, { "deg", 0x00b0 }, { "delta", 0x03b4 }, { "diams", 0x2666 }, { "divide", 0x00f7 }, { "eacute", 0x00e9 }, { "ecirc", 0x00ea }, { "egrave", 0x00e8 }, { "empty", 0x2205 }, { "emsp", 0x2003 }, { "ensp", 0x2002 }, { "epsilon", 0x03b5 }, { "equiv", 0x2261 }, { "eta", 0x03b7 }, { "eth", 0x00f0 }, { "euml", 0x00eb }, { "euro", 0x20ac }, { "exist", 0x2203 }, { "fnof", 0x0192 }, { "forall", 0x2200 }, { "frac12", 0x00bd }, { "frac14", 0x00bc }, { "frac34", 0x00be }, { "frasl", 0x2044 }, { "gamma", 0x03b3 }, { "ge", 0x2265 }, { "gt", 62 }, { "hArr", 0x21d4 }, { "harr", 0x2194 }, { "hearts", 0x2665 }, { "hellip", 0x2026 }, { "iacute", 0x00ed }, { "icirc", 0x00ee }, { "iexcl", 0x00a1 }, { "igrave", 0x00ec }, { "image", 0x2111 }, { "infin", 0x221e }, { "int", 0x222b }, { "iota", 0x03b9 }, { "iquest", 0x00bf }, { "isin", 0x2208 }, { "iuml", 0x00ef }, { "kappa", 0x03ba }, { "lArr", 0x21d0 }, { "lambda", 0x03bb }, { "lang", 0x2329 }, { "laquo", 0x00ab }, { "larr", 0x2190 }, { "lceil", 0x2308 }, { "ldquo", 0x201c }, { "le", 0x2264 }, { "lfloor", 0x230a }, { "lowast", 0x2217 }, { "loz", 0x25ca }, { "lrm", 0x200e }, { "lsaquo", 0x2039 }, { "lsquo", 0x2018 }, { "lt", 60 }, { "macr", 0x00af }, { "mdash", 0x2014 }, { "micro", 0x00b5 }, { "middot", 0x00b7 }, { "minus", 0x2212 }, { "mu", 0x03bc }, { "nabla", 0x2207 }, { "nbsp", 0x00a0 }, { "ndash", 0x2013 }, { "ne", 0x2260 }, { "ni", 0x220b }, { "not", 0x00ac }, { "notin", 0x2209 }, { "nsub", 0x2284 }, { "ntilde", 0x00f1 }, { "nu", 0x03bd }, { "oacute", 0x00f3 }, { "ocirc", 0x00f4 }, { "oelig", 0x0153 }, { "ograve", 0x00f2 }, { "oline", 0x203e }, { "omega", 0x03c9 }, { "omicron", 0x03bf }, { "oplus", 0x2295 }, { "or", 0x22a6 }, { "ordf", 0x00aa }, { "ordm", 0x00ba }, { "oslash", 0x00f8 }, { "otilde", 0x00f5 }, { "otimes", 0x2297 }, { "ouml", 0x00f6 }, { "para", 0x00b6 }, { "part", 0x2202 }, { "percnt", 0x0025 }, { "permil", 0x2030 }, { "perp", 0x22a5 }, { "phi", 0x03c6 }, { "pi", 0x03c0 }, { "piv", 0x03d6 }, { "plusmn", 0x00b1 }, { "pound", 0x00a3 }, { "prime", 0x2032 }, { "prod", 0x220f }, { "prop", 0x221d }, { "psi", 0x03c8 }, { "quot", 34 }, { "rArr", 0x21d2 }, { "radic", 0x221a }, { "rang", 0x232a }, { "raquo", 0x00bb }, { "rarr", 0x2192 }, { "rceil", 0x2309 }, { "rdquo", 0x201d }, { "real", 0x211c }, { "reg", 0x00ae }, { "rfloor", 0x230b }, { "rho", 0x03c1 }, { "rlm", 0x200f }, { "rsaquo", 0x203a }, { "rsquo", 0x2019 }, { "sbquo", 0x201a }, { "scaron", 0x0161 }, { "sdot", 0x22c5 }, { "sect", 0x00a7 }, { "shy", 0x00ad }, { "sigma", 0x03c3 }, { "sigmaf", 0x03c2 }, { "sim", 0x223c }, { "spades", 0x2660 }, { "sub", 0x2282 }, { "sube", 0x2286 }, { "sum", 0x2211 }, { "sup1", 0x00b9 }, { "sup2", 0x00b2 }, { "sup3", 0x00b3 }, { "sup", 0x2283 }, { "supe", 0x2287 }, { "szlig", 0x00df }, { "tau", 0x03c4 }, { "there4", 0x2234 }, { "theta", 0x03b8 }, { "thetasym", 0x03d1 }, { "thinsp", 0x2009 }, { "thorn", 0x00fe }, { "tilde", 0x02dc }, { "times", 0x00d7 }, { "trade", 0x2122 }, { "uArr", 0x21d1 }, { "uacute", 0x00fa }, { "uarr", 0x2191 }, { "ucirc", 0x00fb }, { "ugrave", 0x00f9 }, { "uml", 0x00a8 }, { "upsih", 0x03d2 }, { "upsilon", 0x03c5 }, { "uuml", 0x00fc }, { "weierp", 0x2118 }, { "xi", 0x03be }, { "yacute", 0x00fd }, { "yen", 0x00a5 }, { "yuml", 0x00ff }, { "zeta", 0x03b6 }, { "zwj", 0x200d }, { "zwnj", 0x200c }, { "", 0x0000 } }; static QMap *html_map = 0; static void qt_cleanup_html_map() { delete html_map; html_map = 0; } static QMap *htmlMap() { if ( !html_map ) { html_map = new QMap; qAddPostRoutine( qt_cleanup_html_map ); const Entity *ent = entitylist; while( ent->code ) { html_map->insert( ent->name, TQChar(ent->code) ); ent++; } } return html_map; } TQChar TQTextDocument::parseHTMLSpecialChar(const TQChar* doc, int length, int& pos) { TQString s; pos++; int recoverpos = pos; while ( pos < length && doc[pos] != ';' && !doc[pos].isSpace() && pos < recoverpos + 8 ) { s += doc[pos]; pos++; } if (doc[pos] != ';' && !doc[pos].isSpace() ) { pos = recoverpos; return '&'; } pos++; if ( s.length() > 1 && s[0] == '#') { int off = 1; int base = 10; if (s[1] == 'x') { off = 2; base = 16; } bool ok; int num = s.mid(off).toInt(&ok, base); if ( num == 151 ) // ### hack for designer manual return '-'; if (ok) return num; } else { QMap::Iterator it = htmlMap()->find(s); if ( it != htmlMap()->end() ) { return *it; } } pos = recoverpos; return '&'; } TQString TQTextDocument::parseWord(const TQChar* doc, int length, int& pos, bool lower) { TQString s; if (doc[pos] == '"') { pos++; while ( pos < length && doc[pos] != '"' ) { if ( doc[pos] == '&' ) { s += parseHTMLSpecialChar( doc, length, pos ); } else { s += doc[pos]; pos++; } } eat(doc, length, pos, '"'); } else if (doc[pos] == '\'') { pos++; while ( pos < length && doc[pos] != '\'' ) { s += doc[pos]; pos++; } eat(doc, length, pos, '\''); } else { static TQString term = TQString::tqfromLatin1("/>"); while ( pos < length && doc[pos] != '>' && !hasPrefix(doc, length, pos, term) && doc[pos] != '<' && doc[pos] != '=' && !doc[pos].isSpace() ) { if ( doc[pos] == '&' ) { s += parseHTMLSpecialChar( doc, length, pos ); } else { s += doc[pos]; pos++; } } if (lower) s = s.lower(); } return s; } TQChar TQTextDocument::parseChar(const TQChar* doc, int length, int& pos, TQStyleSheetItem::WhiteSpaceMode wsm ) { if ( pos >= length ) return TQChar::null; TQChar c = doc[pos++]; if (c == '<' ) return TQChar::null; if ( c.isSpace() && c != TQChar::nbsp ) { if ( wsm == TQStyleSheetItem::WhiteSpacePre ) { if ( c == '\n' ) return TQChar_linesep; else return c; } else { // non-pre mode: collapse whitespace except nbsp while ( pos< length && doc[pos].isSpace() && doc[pos] != TQChar::nbsp ) pos++; return ' '; } } else if ( c == '&' ) return parseHTMLSpecialChar( doc, length, --pos ); else return c; } TQString TQTextDocument::parseOpenTag(const TQChar* doc, int length, int& pos, QMap &attr, bool& emptyTag) { emptyTag = FALSE; pos++; if ( hasPrefix(doc, length, pos, '!') ) { if ( hasPrefix( doc, length, pos+1, "--")) { pos += 3; // eat comments TQString pref = TQString::tqfromLatin1("-->"); while ( !hasPrefix(doc, length, pos, pref ) && pos < length ) pos++; if ( hasPrefix(doc, length, pos, pref ) ) { pos += 3; eatSpace(doc, length, pos, TRUE); } emptyTag = TRUE; return TQString::null; } else { // eat strange internal tags while ( !hasPrefix(doc, length, pos, '>') && pos < length ) pos++; if ( hasPrefix(doc, length, pos, '>') ) { pos++; eatSpace(doc, length, pos, TRUE); } return TQString::null; } } TQString tag = parseWord(doc, length, pos ); eatSpace(doc, length, pos, TRUE); static TQString term = TQString::tqfromLatin1("/>"); static TQString s_TRUE = TQString::tqfromLatin1("TRUE"); while (doc[pos] != '>' && ! (emptyTag = hasPrefix(doc, length, pos, term) )) { TQString key = parseWord(doc, length, pos ); eatSpace(doc, length, pos, TRUE); if ( key.isEmpty()) { // error recovery while ( pos < length && doc[pos] != '>' ) pos++; break; } TQString value; if (hasPrefix(doc, length, pos, '=') ){ pos++; eatSpace(doc, length, pos); value = parseWord(doc, length, pos, FALSE); } else value = s_TRUE; attr.insert(key.lower(), value ); eatSpace(doc, length, pos, TRUE); } if (emptyTag) { eat(doc, length, pos, '/'); eat(doc, length, pos, '>'); } else eat(doc, length, pos, '>'); return tag; } TQString TQTextDocument::parseCloseTag( const TQChar* doc, int length, int& pos ) { pos++; pos++; TQString tag = parseWord(doc, length, pos ); eatSpace(doc, length, pos, TRUE); eat(doc, length, pos, '>'); return tag; } TQTextFlow::TQTextFlow() { w = pagesize = 0; } TQTextFlow::~TQTextFlow() { clear(); } void TQTextFlow::clear() { #ifndef TQT_NO_TEXTCUSTOMITEM leftItems.setAutoDelete( TRUE ); rightItems.setAutoDelete( TRUE ); leftItems.clear(); rightItems.clear(); leftItems.setAutoDelete( FALSE ); rightItems.setAutoDelete( FALSE ); #endif } void TQTextFlow::setWidth( int width ) { w = width; } int TQTextFlow::adjustLMargin( int yp, int, int margin, int space ) { #ifndef TQT_NO_TEXTCUSTOMITEM for ( TQTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) { if ( item->ypos == -1 ) continue; if ( yp >= item->ypos && yp < item->ypos + item->height ) margin = TQMAX( margin, item->xpos + item->width + space ); } #endif return margin; } int TQTextFlow::adjustRMargin( int yp, int, int margin, int space ) { #ifndef TQT_NO_TEXTCUSTOMITEM for ( TQTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) { if ( item->ypos == -1 ) continue; if ( yp >= item->ypos && yp < item->ypos + item->height ) margin = TQMAX( margin, w - item->xpos - space ); } #endif return margin; } int TQTextFlow::adjustFlow( int y, int /*w*/, int h ) { if ( pagesize > 0 ) { // check pages int yinpage = y % pagesize; if ( yinpage <= border_tolerance ) return border_tolerance - yinpage; else if ( yinpage + h > pagesize - border_tolerance ) return ( pagesize - yinpage ) + border_tolerance; } return 0; } #ifndef TQT_NO_TEXTCUSTOMITEM void TQTextFlow::unregisterFloatingItem( TQTextCustomItem* item ) { leftItems.removeRef( item ); rightItems.removeRef( item ); } void TQTextFlow::registerFloatingItem( TQTextCustomItem* item ) { if ( item->placement() == TQTextCustomItem::PlaceRight ) { if ( !rightItems.contains( item ) ) rightItems.append( item ); } else if ( item->placement() == TQTextCustomItem::PlaceLeft && !leftItems.contains( item ) ) { leftItems.append( item ); } } #endif // TQT_NO_TEXTCUSTOMITEM TQRect TQTextFlow::boundingRect() const { TQRect br; #ifndef TQT_NO_TEXTCUSTOMITEM TQPtrListIterator l( leftItems ); while( l.current() ) { br = br.unite( l.current()->tqgeometry() ); ++l; } TQPtrListIterator r( rightItems ); while( r.current() ) { br = br.unite( r.current()->tqgeometry() ); ++r; } #endif return br; } void TQTextFlow::drawFloatingItems( TQPainter* p, int cx, int cy, int cw, int ch, const TQColorGroup& cg, bool selected ) { #ifndef TQT_NO_TEXTCUSTOMITEM TQTextCustomItem *item; for ( item = leftItems.first(); item; item = leftItems.next() ) { if ( item->xpos == -1 || item->ypos == -1 ) continue; item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected ); } for ( item = rightItems.first(); item; item = rightItems.next() ) { if ( item->xpos == -1 || item->ypos == -1 ) continue; item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected ); } #endif } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #ifndef TQT_NO_TEXTCUSTOMITEM void TQTextCustomItem::pageBreak( int /*y*/ , TQTextFlow* /*flow*/ ) { } #endif #ifndef TQT_NO_TEXTCUSTOMITEM TQTextTable::TQTextTable( TQTextDocument *p, const QMap & attr ) : TQTextCustomItem( p ) { cells.setAutoDelete( FALSE ); cellspacing = 2; if ( attr.contains("cellspacing") ) cellspacing = attr["cellspacing"].toInt(); cellpadding = 1; if ( attr.contains("cellpadding") ) cellpadding = attr["cellpadding"].toInt(); border = innerborder = 0; if ( attr.contains("border" ) ) { TQString s( attr["border"] ); if ( s == "TRUE" ) border = 1; else border = attr["border"].toInt(); } us_b = border; innerborder = us_ib = border ? 1 : 0; if ( border ) cellspacing += 2; us_ib = innerborder; us_cs = cellspacing; us_cp = cellpadding; outerborder = cellspacing + border; us_ob = outerborder; tqlayout = new TQGridLayout( 1, 1, cellspacing ); fixwidth = 0; stretch = 0; if ( attr.contains("width") ) { bool b; TQString s( attr["width"] ); int w = s.toInt( &b ); if ( b ) { fixwidth = w; } else { s = s.stripWhiteSpace(); if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' ) stretch = s.left( s.length()-1).toInt(); } } us_fixwidth = fixwidth; place = PlaceInline; if ( attr["align"] == "left" ) place = PlaceLeft; else if ( attr["align"] == "right" ) place = PlaceRight; cachewidth = 0; attributes = attr; pageBreakFor = -1; } TQTextTable::~TQTextTable() { delete tqlayout; } TQString TQTextTable::richText() const { TQString s; s = "::ConstIterator it = attributes.begin(); for ( ; it != attributes.end(); ++it ) s += it.key() + "=" + *it + " "; s += ">\n"; int lastRow = -1; bool needEnd = FALSE; TQPtrListIterator it2( cells ); while ( it2.current() ) { TQTextTableCell *cell = it2.current(); ++it2; if ( lastRow != cell->row() ) { if ( lastRow != -1 ) s += "\n"; s += ""; lastRow = cell->row(); needEnd = TRUE; } s += "attributes.begin(); for ( ; it != cell->attributes.end(); ++it ) s += " " + it.key() + "=" + *it; s += ">"; s += cell->richText()->richText(); s += ""; } if ( needEnd ) s += "\n"; s += "
\n"; return s; } void TQTextTable::setParagraph(TQTextParagraph *p) { for ( TQTextTableCell* cell = cells.first(); cell; cell = cells.next() ) cell->richText()->parentPar = p; TQTextCustomItem::setParagraph(p); } void TQTextTable::adjustToPainter( TQPainter* p ) { cellspacing = scale( us_cs, p ); cellpadding = scale( us_cp, p ); border = scale( us_b , p ); innerborder = scale( us_ib, p ); outerborder = scale( us_ob ,p ); fixwidth = scale( us_fixwidth, p); width = 0; cachewidth = 0; for ( TQTextTableCell* cell = cells.first(); cell; cell = cells.next() ) cell->adjustToPainter( p ); } void TQTextTable::adjustCells( int y , int shift ) { TQPtrListIterator it( cells ); TQTextTableCell* cell; bool enlarge = FALSE; while ( ( cell = it.current() ) ) { ++it; TQRect r = cell->tqgeometry(); if ( y <= r.top() ) { r.moveBy(0, shift ); cell->setGeometry( r ); enlarge = TRUE; } else if ( y <= r.bottom() ) { r.rBottom() += shift; cell->setGeometry( r ); enlarge = TRUE; } } if ( enlarge ) height += shift; } void TQTextTable::pageBreak( int yt, TQTextFlow* flow ) { if ( flow->pageSize() <= 0 ) return; if ( tqlayout && pageBreakFor > 0 && pageBreakFor != yt ) { tqlayout->tqinvalidate(); int h = tqlayout->heightForWidth( width-2*outerborder ); tqlayout->setGeometry( TQRect(0, 0, width-2*outerborder, h) ); height = tqlayout->tqgeometry().height()+2*outerborder; } pageBreakFor = yt; TQPtrListIterator it( cells ); TQTextTableCell* cell; while ( ( cell = it.current() ) ) { ++it; int y = yt + outerborder + cell->tqgeometry().y(); int shift = flow->adjustFlow( y - cellspacing, width, cell->richText()->height() + 2*cellspacing ); adjustCells( y - outerborder - yt, shift ); } } void TQTextTable::draw(TQPainter* p, int x, int y, int cx, int cy, int cw, int ch, const TQColorGroup& cg, bool selected ) { if ( placement() != PlaceInline ) { x = xpos; y = ypos; } for (TQTextTableCell* cell = cells.first(); cell; cell = cells.next() ) { if ( (cx < 0 && cy) < 0 || TQRect( cx, cy, cw, ch ).intersects( TQRect( x + outerborder + cell->tqgeometry().x(), y + outerborder + cell->tqgeometry().y(), cell->tqgeometry().width(), cell->tqgeometry().height() ) ) ) { cell->draw( p, x+outerborder, y+outerborder, cx, cy, cw, ch, cg, selected ); if ( border ) { TQRect r( x+outerborder+cell->tqgeometry().x() - innerborder, y+outerborder+cell->tqgeometry().y() - innerborder, cell->tqgeometry().width() + 2 * innerborder, cell->tqgeometry().height() + 2 * innerborder ); if ( is_printer( p ) ) { TQPen oldPen = p->pen(); TQRect r2 = r; r2.addCoords( innerborder/2, innerborder/2, -innerborder/2, -innerborder/2 ); p->setPen( TQPen( cg.text(), innerborder ) ); p->drawRect( r2 ); p->setPen( oldPen ); } else { int s = TQMAX( cellspacing-2*innerborder, 0); if ( s ) { p->fillRect( r.left()-s, r.top(), s+1, r.height(), cg.button() ); p->fillRect( r.right(), r.top(), s+1, r.height(), cg.button() ); p->fillRect( r.left()-s, r.top()-s, r.width()+2*s, s, cg.button() ); p->fillRect( r.left()-s, r.bottom(), r.width()+2*s, s, cg.button() ); } qDrawShadePanel( p, r, cg, TRUE, innerborder ); } } } } if ( border ) { TQRect r ( x, y, width, height ); if ( is_printer( p ) ) { TQRect r2 = r; r2.addCoords( border/2, border/2, -border/2, -border/2 ); TQPen oldPen = p->pen(); p->setPen( TQPen( cg.text(), border ) ); p->drawRect( r2 ); p->setPen( oldPen ); } else { int s = border+TQMAX( cellspacing-2*innerborder, 0); if ( s ) { p->fillRect( r.left(), r.top(), s, r.height(), cg.button() ); p->fillRect( r.right()-s, r.top(), s, r.height(), cg.button() ); p->fillRect( r.left(), r.top(), r.width(), s, cg.button() ); p->fillRect( r.left(), r.bottom()-s, r.width(), s, cg.button() ); } qDrawShadePanel( p, r, cg, FALSE, border ); } } } int TQTextTable::minimumWidth() const { return fixwidth ? fixwidth : ((tqlayout ? tqlayout->tqminimumSize().width() : 0) + 2 * outerborder); } void TQTextTable::resize( int nwidth ) { if ( fixwidth && cachewidth != 0 ) return; if ( nwidth == cachewidth ) return; cachewidth = nwidth; int w = nwidth; format( w ); if ( stretch ) nwidth = nwidth * stretch / 100; width = nwidth; tqlayout->tqinvalidate(); int shw = tqlayout->tqsizeHint().width() + 2*outerborder; int mw = tqlayout->tqminimumSize().width() + 2*outerborder; if ( stretch ) width = TQMAX( mw, nwidth ); else width = TQMAX( mw, TQMIN( nwidth, shw ) ); if ( fixwidth ) width = fixwidth; tqlayout->tqinvalidate(); mw = tqlayout->tqminimumSize().width() + 2*outerborder; width = TQMAX( width, mw ); int h = tqlayout->heightForWidth( width-2*outerborder ); tqlayout->setGeometry( TQRect(0, 0, width-2*outerborder, h) ); height = tqlayout->tqgeometry().height()+2*outerborder; } void TQTextTable::format( int w ) { for ( int i = 0; i < (int)cells.count(); ++i ) { TQTextTableCell *cell = cells.at( i ); TQRect r = cell->tqgeometry(); r.setWidth( w - 2*outerborder ); cell->setGeometry( r ); } } void TQTextTable::addCell( TQTextTableCell* cell ) { cells.append( cell ); tqlayout->addMultiCell( cell, cell->row(), cell->row() + cell->rowspan()-1, cell->column(), cell->column() + cell->colspan()-1 ); } bool TQTextTable::enter( TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy, bool atEnd ) { currCell.remove( c ); if ( !atEnd ) return next( c, doc, parag, idx, ox, oy ); currCell.insert( c, cells.count() ); return prev( c, doc, parag, idx, ox, oy ); } bool TQTextTable::enterAt( TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy, const TQPoint &pos ) { currCell.remove( c ); int lastCell = -1; int lastY = -1; int i; for ( i = 0; i < (int)cells.count(); ++i ) { TQTextTableCell *cell = cells.at( i ); if ( !cell ) continue; TQRect r( cell->tqgeometry().x(), cell->tqgeometry().y(), cell->tqgeometry().width() + 2 * innerborder + 2 * outerborder, cell->tqgeometry().height() + 2 * innerborder + 2 * outerborder ); if ( r.left() <= pos.x() && r.right() >= pos.x() ) { if ( cell->tqgeometry().y() > lastY ) { lastCell = i; lastY = cell->tqgeometry().y(); } if ( r.top() <= pos.y() && r.bottom() >= pos.y() ) { currCell.insert( c, i ); break; } } } if ( i == (int) cells.count() ) return FALSE; // no cell found if ( currCell.find( c ) == currCell.end() ) { if ( lastY != -1 ) currCell.insert( c, lastCell ); else return FALSE; } TQTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( !cell ) return FALSE; doc = cell->richText(); parag = doc->firstParagraph(); idx = 0; ox += cell->tqgeometry().x() + cell->horizontalAlignmentOffset() + outerborder + tqparent->x(); oy += cell->tqgeometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } bool TQTextTable::next( TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy ) { int cc = -1; if ( currCell.find( c ) != currCell.end() ) cc = *currCell.find( c ); if ( cc > (int)cells.count() - 1 || cc < 0 ) cc = -1; currCell.remove( c ); currCell.insert( c, ++cc ); if ( cc >= (int)cells.count() ) { currCell.insert( c, 0 ); TQTextCustomItem::next( c, doc, parag, idx, ox, oy ); TQTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } if ( currCell.find( c ) == currCell.end() ) return FALSE; TQTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( !cell ) return FALSE; doc = cell->richText(); parag = doc->firstParagraph(); idx = 0; ox += cell->tqgeometry().x() + cell->horizontalAlignmentOffset() + outerborder + tqparent->x(); oy += cell->tqgeometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } bool TQTextTable::prev( TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy ) { int cc = -1; if ( currCell.find( c ) != currCell.end() ) cc = *currCell.find( c ); if ( cc > (int)cells.count() - 1 || cc < 0 ) cc = cells.count(); currCell.remove( c ); currCell.insert( c, --cc ); if ( cc < 0 ) { currCell.insert( c, 0 ); TQTextCustomItem::prev( c, doc, parag, idx, ox, oy ); TQTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } if ( currCell.find( c ) == currCell.end() ) return FALSE; TQTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( !cell ) return FALSE; doc = cell->richText(); parag = doc->lastParagraph(); idx = parag->length() - 1; ox += cell->tqgeometry().x() + cell->horizontalAlignmentOffset() + outerborder + tqparent->x(); oy += cell->tqgeometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } bool TQTextTable::down( TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy ) { if ( currCell.find( c ) == currCell.end() ) return FALSE; TQTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( cell->row_ == tqlayout->numRows() - 1 ) { currCell.insert( c, 0 ); TQTextCustomItem::down( c, doc, parag, idx, ox, oy ); TQTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } int oldRow = cell->row_; int oldCol = cell->col_; if ( currCell.find( c ) == currCell.end() ) return FALSE; int cc = *currCell.find( c ); for ( int i = cc; i < (int)cells.count(); ++i ) { cell = cells.at( i ); if ( cell->row_ > oldRow && cell->col_ == oldCol ) { currCell.insert( c, i ); break; } } doc = cell->richText(); if ( !cell ) return FALSE; parag = doc->firstParagraph(); idx = 0; ox += cell->tqgeometry().x() + cell->horizontalAlignmentOffset() + outerborder + tqparent->x(); oy += cell->tqgeometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } bool TQTextTable::up( TQTextCursor *c, TQTextDocument *&doc, TQTextParagraph *¶g, int &idx, int &ox, int &oy ) { if ( currCell.find( c ) == currCell.end() ) return FALSE; TQTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( cell->row_ == 0 ) { currCell.insert( c, 0 ); TQTextCustomItem::up( c, doc, parag, idx, ox, oy ); TQTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } int oldRow = cell->row_; int oldCol = cell->col_; if ( currCell.find( c ) == currCell.end() ) return FALSE; int cc = *currCell.find( c ); for ( int i = cc; i >= 0; --i ) { cell = cells.at( i ); if ( cell->row_ < oldRow && cell->col_ == oldCol ) { currCell.insert( c, i ); break; } } doc = cell->richText(); if ( !cell ) return FALSE; parag = doc->lastParagraph(); idx = parag->length() - 1; ox += cell->tqgeometry().x() + cell->horizontalAlignmentOffset() + outerborder + tqparent->x(); oy += cell->tqgeometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } TQTextTableCell::TQTextTableCell( TQTextTable* table, int row, int column, const QMap &attr, const TQStyleSheetItem* /*style*/, // ### use them const TQTextFormat& fmt, const TQString& context, TQMimeSourceFactory &factory, TQStyleSheet *sheet, const TQString& doc) { cached_width = -1; cached_sizehint = -1; maxw = TQWIDGETSIZE_MAX; minw = 0; tqparent = table; row_ = row; col_ = column; stretch_ = 0; richtext = new TQTextDocument( table->tqparent ); richtext->formatCollection()->setPaintDevice( table->tqparent->formatCollection()->paintDevice() ); richtext->bodyText = fmt.color(); richtext->setTableCell( this ); TQString a = *attr.find( "align" ); if ( !a.isEmpty() ) { a = a.lower(); if ( a == "left" ) richtext->tqsetAlignment( TQt::AlignLeft ); else if ( a == "center" ) richtext->tqsetAlignment( TQt::AlignHCenter ); else if ( a == "right" ) richtext->tqsetAlignment( TQt::AlignRight ); } align = 0; TQString va = *attr.find( "valign" ); if ( !va.isEmpty() ) { va = va.lower(); if ( va == "top" ) align |= TQt::AlignTop; else if ( va == "center" || va == "middle" ) align |= TQt::AlignVCenter; else if ( va == "bottom" ) align |= TQt::AlignBottom; } richtext->setFormatter( table->tqparent->formatter() ); richtext->setUseFormatCollection( table->tqparent->useFormatCollection() ); richtext->setMimeSourceFactory( &factory ); richtext->setStyleSheet( sheet ); richtext->setRichText( doc, context, &fmt ); rowspan_ = 1; colspan_ = 1; if ( attr.contains("colspan") ) colspan_ = attr["colspan"].toInt(); if ( attr.contains("rowspan") ) rowspan_ = attr["rowspan"].toInt(); background = 0; if ( attr.contains("bgcolor") ) { background = new TQBrush(TQColor( attr["bgcolor"] )); } hasFixedWidth = FALSE; if ( attr.contains("width") ) { bool b; TQString s( attr["width"] ); int w = s.toInt( &b ); if ( b ) { maxw = w; minw = maxw; hasFixedWidth = TRUE; } else { s = s.stripWhiteSpace(); if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' ) stretch_ = s.left( s.length()-1).toInt(); } } attributes = attr; tqparent->addCell( this ); } TQTextTableCell::~TQTextTableCell() { delete background; background = 0; delete richtext; richtext = 0; } TQSize TQTextTableCell::tqsizeHint() const { int extra = 2 * ( tqparent->innerborder + tqparent->cellpadding + border_tolerance); int used = richtext->widthUsed() + extra; if (stretch_ ) { int w = tqparent->width * stretch_ / 100 - 2*tqparent->cellspacing - 2*tqparent->cellpadding; return TQSize( TQMIN( w, maxw ), 0 ).expandedTo( tqminimumSize() ); } return TQSize( used, 0 ).expandedTo( tqminimumSize() ); } TQSize TQTextTableCell::tqminimumSize() const { int extra = 2 * ( tqparent->innerborder + tqparent->cellpadding + border_tolerance); return TQSize( TQMAX( richtext->minimumWidth() + extra, minw), 0 ); } TQSize TQTextTableCell::tqmaximumSize() const { return TQSize( maxw, TQWIDGETSIZE_MAX ); } TQ_SPExpandData TQTextTableCell::expandingDirections() const { return (TQ_SPExpandData)TQSizePolicy::BothDirections; } bool TQTextTableCell::isEmpty() const { return FALSE; } void TQTextTableCell::setGeometry( const TQRect& r ) { int extra = 2 * ( tqparent->innerborder + tqparent->cellpadding ); if ( r.width() != cached_width ) richtext->doLayout( TQTextFormat::painter(), r.width() - extra ); cached_width = r.width(); geom = r; } TQRect TQTextTableCell::tqgeometry() const { return geom; } bool TQTextTableCell::hasHeightForWidth() const { return TRUE; } int TQTextTableCell::heightForWidth( int w ) const { int extra = 2 * ( tqparent->innerborder + tqparent->cellpadding ); w = TQMAX( minw, w ); if ( cached_width != w ) { TQTextTableCell* that = (TQTextTableCell*) this; that->richtext->doLayout( TQTextFormat::painter(), w - extra ); that->cached_width = w; } return richtext->height() + extra; } void TQTextTableCell::adjustToPainter( TQPainter* p ) { TQTextParagraph *parag = richtext->firstParagraph(); while ( parag ) { parag->adjustToPainter( p ); parag = parag->next(); } } int TQTextTableCell::horizontalAlignmentOffset() const { return tqparent->cellpadding; } int TQTextTableCell::verticalAlignmentOffset() const { if ( (align & TQt::AlignVCenter ) == TQt::AlignVCenter ) return ( geom.height() - richtext->height() ) / 2; else if ( ( align & TQt::AlignBottom ) == TQt::AlignBottom ) return geom.height() - tqparent->cellpadding - richtext->height() ; return tqparent->cellpadding; } void TQTextTableCell::draw( TQPainter* p, int x, int y, int cx, int cy, int cw, int ch, const TQColorGroup& cg, bool ) { if ( cached_width != geom.width() ) { int extra = 2 * ( tqparent->innerborder + tqparent->cellpadding ); richtext->doLayout( p, geom.width() - extra ); cached_width = geom.width(); } TQColorGroup g( cg ); if ( background ) g.setBrush( TQColorGroup::Base, *background ); else if ( richtext->paper() ) g.setBrush( TQColorGroup::Base, *richtext->paper() ); p->save(); p->translate( x + geom.x(), y + geom.y() ); if ( background ) p->fillRect( 0, 0, geom.width(), geom.height(), *background ); else if ( richtext->paper() ) p->fillRect( 0, 0, geom.width(), geom.height(), *richtext->paper() ); p->translate( horizontalAlignmentOffset(), verticalAlignmentOffset() ); TQRegion r; if ( cx >= 0 && cy >= 0 ) richtext->draw( p, cx - ( x + horizontalAlignmentOffset() + geom.x() ), cy - ( y + geom.y() + verticalAlignmentOffset() ), cw, ch, g, FALSE, FALSE, 0 ); else richtext->draw( p, -1, -1, -1, -1, g, FALSE, FALSE, 0 ); p->restore(); } #endif #endif //TQT_NO_RICHTEXT #endif // USE_QT4