src/qt3support/text/q3richtext.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt3Support module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "q3richtext_p.h"
       
    43 
       
    44 #ifndef QT_NO_RICHTEXT
       
    45 
       
    46 #include "qbitmap.h"
       
    47 #include "qapplication.h"
       
    48 #include "q3cleanuphandler.h"
       
    49 #include "qcursor.h"
       
    50 #include "qdatastream.h"
       
    51 #include "q3dragobject.h"
       
    52 #include "qdrawutil.h"
       
    53 #include "qfile.h"
       
    54 #include "qfileinfo.h"
       
    55 #include "qfont.h"
       
    56 #include "qimage.h"
       
    57 #include "qmap.h"
       
    58 #include "qmime.h"
       
    59 #include "q3paintdevicemetrics.h"
       
    60 #include "qpainter.h"
       
    61 #include "qstringlist.h"
       
    62 #include "qstyle.h"
       
    63 #include "qstyleoption.h"
       
    64 #include "q3stylesheet.h"
       
    65 #include "qtextstream.h"
       
    66 #include <private/qtextengine_p.h>
       
    67 #include <private/qunicodetables_p.h>
       
    68 
       
    69 #include <stdlib.h>
       
    70 
       
    71 #if defined(Q_WS_X11)
       
    72 #include "qx11info_x11.h"
       
    73 #endif
       
    74 
       
    75 QT_BEGIN_NAMESPACE
       
    76 
       
    77 static Q3TextCursor* richTextExportStart = 0;
       
    78 static Q3TextCursor* richTextExportEnd = 0;
       
    79 
       
    80 class Q3TextFormatCollection;
       
    81 
       
    82 const int border_tolerance = 2;
       
    83 
       
    84 #ifdef Q_WS_WIN
       
    85 QT_BEGIN_INCLUDE_NAMESPACE
       
    86 #include "qt_windows.h"
       
    87 QT_END_INCLUDE_NAMESPACE
       
    88 #endif
       
    89 
       
    90 static inline bool is_printer(QPainter *p)
       
    91 {
       
    92     if (!p || !p->device())
       
    93         return false;
       
    94     return p->device()->devType() == QInternal::Printer;
       
    95 }
       
    96 
       
    97 static inline int scale(int value, QPainter *painter)
       
    98 {
       
    99     if (is_printer(painter)) {
       
   100         Q3PaintDeviceMetrics metrics(painter->device());
       
   101 #if defined(Q_WS_X11)
       
   102         value = value * metrics.logicalDpiY() /
       
   103                 QX11Info::appDpiY(painter->device()->x11Screen());
       
   104 #elif defined (Q_WS_WIN)
       
   105         HDC hdc = GetDC(0);
       
   106         int gdc = GetDeviceCaps(hdc, LOGPIXELSY);
       
   107         if (gdc)
       
   108             value = value * metrics.logicalDpiY() / gdc;
       
   109         ReleaseDC(0, hdc);
       
   110 #elif defined (Q_WS_MAC)
       
   111         value = value * metrics.logicalDpiY() / 75; // ##### FIXME
       
   112 #elif defined (Q_WS_QWS)
       
   113         value = value * metrics.logicalDpiY() / 75;
       
   114 #endif
       
   115     }
       
   116     return value;
       
   117 }
       
   118 
       
   119 
       
   120 static inline bool isBreakable(Q3TextString *string, int pos)
       
   121 {
       
   122     if (string->at(pos).nobreak)
       
   123         return false;
       
   124     return (pos < string->length()-1 && string->at(pos+1).softBreak);
       
   125 }
       
   126 
       
   127 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
   128 
       
   129 void Q3TextCommandHistory::addCommand(Q3TextCommand *cmd)
       
   130 {
       
   131     if (current < history.count() - 1) {
       
   132         QList<Q3TextCommand *> commands;
       
   133 
       
   134         for (int i = 0; i <= current; ++i)
       
   135             commands.insert(i, history.takeFirst());
       
   136 
       
   137         commands.append(cmd);
       
   138         while (!history.isEmpty())
       
   139             delete history.takeFirst();
       
   140         history = commands;
       
   141     } else {
       
   142         history.append(cmd);
       
   143     }
       
   144 
       
   145     if (history.count() > steps)
       
   146         delete history.takeFirst();
       
   147     else
       
   148         ++current;
       
   149 }
       
   150 
       
   151 Q3TextCursor *Q3TextCommandHistory::undo(Q3TextCursor *c)
       
   152 {
       
   153     if (current > -1) {
       
   154         Q3TextCursor *c2 = history.at(current)->unexecute(c);
       
   155         --current;
       
   156         return c2;
       
   157     }
       
   158     return 0;
       
   159 }
       
   160 
       
   161 Q3TextCursor *Q3TextCommandHistory::redo(Q3TextCursor *c)
       
   162 {
       
   163     if (current > -1) {
       
   164         if (current < history.count() - 1) {
       
   165             ++current;
       
   166             return history.at(current)->execute(c);
       
   167         }
       
   168     } else {
       
   169         if (history.count() > 0) {
       
   170             ++current;
       
   171             return history.at(current)->execute(c);
       
   172         }
       
   173     }
       
   174     return 0;
       
   175 }
       
   176 
       
   177 bool Q3TextCommandHistory::isUndoAvailable()
       
   178 {
       
   179     return current > -1;
       
   180 }
       
   181 
       
   182 bool Q3TextCommandHistory::isRedoAvailable()
       
   183 {
       
   184    return (current > -1 && current < history.count() - 1) || (current == -1 && history.count() > 0);
       
   185 }
       
   186 
       
   187 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
   188 
       
   189 Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
       
   190                                         const QByteArray& oldStyleInfo)
       
   191     : Q3TextCommand(dc), id(i), index(idx), parag(0), text(str), styleInformation(oldStyleInfo)
       
   192 {
       
   193     for (int j = 0; j < (int)text.size(); ++j) {
       
   194         if (text[j].format())
       
   195             text[j].format()->addRef();
       
   196     }
       
   197 }
       
   198 
       
   199 Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str)
       
   200     : Q3TextCommand(0), id(-1), index(idx), parag(p), text(str)
       
   201 {
       
   202     for (int i = 0; i < (int)text.size(); ++i) {
       
   203         if (text[i].format())
       
   204             text[i].format()->addRef();
       
   205     }
       
   206 }
       
   207 
       
   208 Q3TextDeleteCommand::~Q3TextDeleteCommand()
       
   209 {
       
   210     for (int i = 0; i < (int)text.size(); ++i) {
       
   211         if (text[i].format())
       
   212             text[i].format()->removeRef();
       
   213     }
       
   214     text.resize(0);
       
   215 }
       
   216 
       
   217 Q3TextCursor *Q3TextDeleteCommand::execute(Q3TextCursor *c)
       
   218 {
       
   219     Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
       
   220     if (!s) {
       
   221         qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
       
   222         return 0;
       
   223     }
       
   224 
       
   225     cursor.setParagraph(s);
       
   226     cursor.setIndex(index);
       
   227     int len = text.size();
       
   228     if (c)
       
   229         *c = cursor;
       
   230     if (doc) {
       
   231         doc->setSelectionStart(Q3TextDocument::Temp, cursor);
       
   232         for (int i = 0; i < len; ++i)
       
   233             cursor.gotoNextLetter();
       
   234         doc->setSelectionEnd(Q3TextDocument::Temp, cursor);
       
   235         doc->removeSelectedText(Q3TextDocument::Temp, &cursor);
       
   236         if (c)
       
   237             *c = cursor;
       
   238     } else {
       
   239         s->remove(index, len);
       
   240     }
       
   241 
       
   242     return c;
       
   243 }
       
   244 
       
   245 Q3TextCursor *Q3TextDeleteCommand::unexecute(Q3TextCursor *c)
       
   246 {
       
   247     Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
       
   248     if (!s) {
       
   249         qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
       
   250         return 0;
       
   251     }
       
   252 
       
   253     cursor.setParagraph(s);
       
   254     cursor.setIndex(index);
       
   255     QString str = Q3TextString::toString(text);
       
   256     cursor.insert(str, true, &text);
       
   257     if (c)
       
   258         *c = cursor;
       
   259     cursor.setParagraph(s);
       
   260     cursor.setIndex(index);
       
   261 
       
   262 #ifndef QT_NO_DATASTREAM
       
   263     if (!styleInformation.isEmpty()) {
       
   264         QDataStream styleStream(&styleInformation, IO_ReadOnly);
       
   265         int num;
       
   266         styleStream >> num;
       
   267         Q3TextParagraph *p = s;
       
   268         while (num-- && p) {
       
   269             p->readStyleInformation(styleStream);
       
   270             p = p->next();
       
   271         }
       
   272     }
       
   273 #endif
       
   274     s = cursor.paragraph();
       
   275     while (s) {
       
   276         s->format();
       
   277         s->setChanged(true);
       
   278         if (s == c->paragraph())
       
   279             break;
       
   280         s = s->next();
       
   281     }
       
   282 
       
   283     return &cursor;
       
   284 }
       
   285 
       
   286 Q3TextFormatCommand::Q3TextFormatCommand(Q3TextDocument *dc, int sid, int sidx, int eid, int eidx,
       
   287                                         const QVector<Q3TextStringChar> &old, Q3TextFormat *f, int fl)
       
   288     : Q3TextCommand(dc), startId(sid), startIndex(sidx), endId(eid), endIndex(eidx), format(f), oldFormats(old), flags(fl)
       
   289 {
       
   290     format = dc->formatCollection()->format(f);
       
   291     for (int j = 0; j < (int)oldFormats.size(); ++j) {
       
   292         if (oldFormats[j].format())
       
   293             oldFormats[j].format()->addRef();
       
   294     }
       
   295 }
       
   296 
       
   297 Q3TextFormatCommand::~Q3TextFormatCommand()
       
   298 {
       
   299     format->removeRef();
       
   300     for (int j = 0; j < (int)oldFormats.size(); ++j) {
       
   301         if (oldFormats[j].format())
       
   302             oldFormats[j].format()->removeRef();
       
   303     }
       
   304 }
       
   305 
       
   306 Q3TextCursor *Q3TextFormatCommand::execute(Q3TextCursor *c)
       
   307 {
       
   308     Q3TextParagraph *sp = doc->paragAt(startId);
       
   309     Q3TextParagraph *ep = doc->paragAt(endId);
       
   310     if (!sp || !ep)
       
   311         return c;
       
   312 
       
   313     Q3TextCursor start(doc);
       
   314     start.setParagraph(sp);
       
   315     start.setIndex(startIndex);
       
   316     Q3TextCursor end(doc);
       
   317     end.setParagraph(ep);
       
   318     end.setIndex(endIndex);
       
   319 
       
   320     doc->setSelectionStart(Q3TextDocument::Temp, start);
       
   321     doc->setSelectionEnd(Q3TextDocument::Temp, end);
       
   322     doc->setFormat(Q3TextDocument::Temp, format, flags);
       
   323     doc->removeSelection(Q3TextDocument::Temp);
       
   324     if (endIndex == ep->length())
       
   325         end.gotoLeft();
       
   326     *c = end;
       
   327     return c;
       
   328 }
       
   329 
       
   330 Q3TextCursor *Q3TextFormatCommand::unexecute(Q3TextCursor *c)
       
   331 {
       
   332     Q3TextParagraph *sp = doc->paragAt(startId);
       
   333     Q3TextParagraph *ep = doc->paragAt(endId);
       
   334     if (!sp || !ep)
       
   335         return 0;
       
   336 
       
   337     int idx = startIndex;
       
   338     int fIndex = 0;
       
   339     while ( fIndex < int(oldFormats.size()) ) {
       
   340         if (oldFormats.at(fIndex).c == QLatin1Char('\n')) {
       
   341             if (idx > 0) {
       
   342                 if (idx < sp->length() && fIndex > 0)
       
   343                     sp->setFormat(idx, 1, oldFormats.at(fIndex - 1).format());
       
   344                 if (sp == ep)
       
   345                     break;
       
   346                 sp = sp->next();
       
   347                 idx = 0;
       
   348             }
       
   349             fIndex++;
       
   350         }
       
   351         if (oldFormats.at(fIndex).format())
       
   352             sp->setFormat(idx, 1, oldFormats.at(fIndex).format());
       
   353         idx++;
       
   354         fIndex++;
       
   355         if (fIndex >= (int)oldFormats.size())
       
   356             break;
       
   357         if (idx >= sp->length()) {
       
   358             if (sp == ep)
       
   359                 break;
       
   360             sp = sp->next();
       
   361             idx = 0;
       
   362         }
       
   363     }
       
   364 
       
   365     Q3TextCursor end(doc);
       
   366     end.setParagraph(ep);
       
   367     end.setIndex(endIndex);
       
   368     if (endIndex == ep->length())
       
   369         end.gotoLeft();
       
   370     *c = end;
       
   371     return c;
       
   372 }
       
   373 
       
   374 Q3TextStyleCommand::Q3TextStyleCommand(Q3TextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange)
       
   375     : Q3TextCommand(dc), firstParag(fParag), lastParag(lParag), before(beforeChange)
       
   376 {
       
   377     after = readStyleInformation( dc, fParag, lParag);
       
   378 }
       
   379 
       
   380 
       
   381 QByteArray Q3TextStyleCommand::readStyleInformation( Q3TextDocument* doc, int fParag, int lParag)
       
   382 {
       
   383     QByteArray style;
       
   384 #ifndef QT_NO_DATASTREAM
       
   385     Q3TextParagraph *p = doc->paragAt(fParag);
       
   386     if (!p)
       
   387         return style;
       
   388     QDataStream styleStream(&style, IO_WriteOnly);
       
   389     int num = lParag - fParag + 1;
       
   390     styleStream << num;
       
   391     while (num -- && p) {
       
   392         p->writeStyleInformation(styleStream);
       
   393         p = p->next();
       
   394     }
       
   395 #endif
       
   396     return style;
       
   397 }
       
   398 
       
   399 void Q3TextStyleCommand::writeStyleInformation( Q3TextDocument* doc, int fParag, const QByteArray& style)
       
   400 {
       
   401 #ifndef QT_NO_DATASTREAM
       
   402     Q3TextParagraph *p = doc->paragAt(fParag);
       
   403     if (!p)
       
   404         return;
       
   405     QByteArray copy = style;
       
   406     QDataStream styleStream(&copy, IO_ReadOnly);
       
   407     int num;
       
   408     styleStream >> num;
       
   409     while (num-- && p) {
       
   410         p->readStyleInformation(styleStream);
       
   411         p = p->next();
       
   412     }
       
   413 #endif
       
   414 }
       
   415 
       
   416 Q3TextCursor *Q3TextStyleCommand::execute(Q3TextCursor *c)
       
   417 {
       
   418     writeStyleInformation(doc, firstParag, after);
       
   419     return c;
       
   420 }
       
   421 
       
   422 Q3TextCursor *Q3TextStyleCommand::unexecute(Q3TextCursor *c)
       
   423 {
       
   424     writeStyleInformation(doc, firstParag, before);
       
   425     return c;
       
   426 }
       
   427 
       
   428 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
   429 
       
   430 Q3TextCursor::Q3TextCursor(Q3TextDocument *dc)
       
   431     : idx(0), tmpX(-1), ox(0), oy(0),
       
   432       valid(true)
       
   433 {
       
   434     para = dc ? dc->firstParagraph() : 0;
       
   435 }
       
   436 
       
   437 Q3TextCursor::Q3TextCursor(const Q3TextCursor &c)
       
   438 {
       
   439     ox = c.ox;
       
   440     oy = c.oy;
       
   441     idx = c.idx;
       
   442     para = c.para;
       
   443     tmpX = c.tmpX;
       
   444     indices = c.indices;
       
   445     paras = c.paras;
       
   446     xOffsets = c.xOffsets;
       
   447     yOffsets = c.yOffsets;
       
   448     valid = c.valid;
       
   449 }
       
   450 
       
   451 Q3TextCursor::~Q3TextCursor()
       
   452 {
       
   453 }
       
   454 
       
   455 Q3TextCursor &Q3TextCursor::operator=(const Q3TextCursor &c)
       
   456 {
       
   457     ox = c.ox;
       
   458     oy = c.oy;
       
   459     idx = c.idx;
       
   460     para = c.para;
       
   461     tmpX = c.tmpX;
       
   462     indices = c.indices;
       
   463     paras = c.paras;
       
   464     xOffsets = c.xOffsets;
       
   465     yOffsets = c.yOffsets;
       
   466     valid = c.valid;
       
   467 
       
   468     return *this;
       
   469 }
       
   470 
       
   471 bool Q3TextCursor::operator==(const Q3TextCursor &c) const
       
   472 {
       
   473     return para == c.para && idx == c.idx;
       
   474 }
       
   475 
       
   476 int Q3TextCursor::totalOffsetX() const
       
   477 {
       
   478     int xoff = ox;
       
   479     for (QStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit)
       
   480         xoff += *xit;
       
   481     return xoff;
       
   482 }
       
   483 
       
   484 int Q3TextCursor::totalOffsetY() const
       
   485 {
       
   486     int yoff = oy;
       
   487     for (QStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit)
       
   488         yoff += *yit;
       
   489     return yoff;
       
   490 }
       
   491 
       
   492 #ifndef QT_NO_TEXTCUSTOMITEM
       
   493 void Q3TextCursor::gotoIntoNested(const QPoint &globalPos)
       
   494 {
       
   495     if (!para)
       
   496         return;
       
   497     Q_ASSERT(para->at(idx)->isCustom());
       
   498     push();
       
   499     ox = 0;
       
   500     int bl, y;
       
   501     para->lineHeightOfChar(idx, &bl, &y);
       
   502     oy = y + para->rect().y();
       
   503     ox = para->at(idx)->x;
       
   504     Q3TextDocument* doc = document();
       
   505     para->at(idx)->customItem()->enterAt(this, doc, para, idx, ox, oy, globalPos-QPoint(ox,oy));
       
   506 }
       
   507 #endif
       
   508 
       
   509 void Q3TextCursor::invalidateNested()
       
   510 {
       
   511     if (nestedDepth()) {
       
   512         QStack<Q3TextParagraph*>::Iterator it = paras.begin();
       
   513         QStack<int>::Iterator it2 = indices.begin();
       
   514         for (; it != paras.end(); ++it, ++it2) {
       
   515             if (*it == para)
       
   516                 continue;
       
   517             (*it)->invalidate(0);
       
   518 #ifndef QT_NO_TEXTCUSTOMITEM
       
   519             if ((*it)->at(*it2)->isCustom())
       
   520                 (*it)->at(*it2)->customItem()->invalidate();
       
   521 #endif
       
   522         }
       
   523     }
       
   524 }
       
   525 
       
   526 void Q3TextCursor::insert(const QString &str, bool checkNewLine, QVector<Q3TextStringChar> *formatting)
       
   527 {
       
   528     tmpX = -1;
       
   529     bool justInsert = true;
       
   530     QString s(str);
       
   531 #if defined(Q_WS_WIN)
       
   532     if (checkNewLine) {
       
   533         int i = 0;
       
   534         while ((i = s.indexOf(QLatin1Char('\r'), i)) != -1)
       
   535             s.remove(i ,1);
       
   536     }
       
   537 #endif
       
   538     if (checkNewLine)
       
   539         justInsert = s.indexOf(QLatin1Char('\n')) == -1;
       
   540     if (justInsert) { // we ignore new lines and insert all in the current para at the current index
       
   541         para->insert(idx, s.unicode(), s.length());
       
   542         if (formatting) {
       
   543             for (int i = 0; i < (int)s.length(); ++i) {
       
   544                 if (formatting->at(i).format()) {
       
   545                     formatting->at(i).format()->addRef();
       
   546                     para->string()->setFormat(idx + i, formatting->at(i).format(), true);
       
   547                 }
       
   548             }
       
   549         }
       
   550         idx += s.length();
       
   551     } else { // we split at new lines
       
   552         int start = -1;
       
   553         int end;
       
   554         int y = para->rect().y() + para->rect().height();
       
   555         int lastIndex = 0;
       
   556         do {
       
   557             end = s.indexOf(QLatin1Char('\n'), start + 1); // find line break
       
   558             if (end == -1) // didn't find one, so end of line is end of string
       
   559                 end = s.length();
       
   560             int len = (start == -1 ? end : end - start - 1);
       
   561             if (len > 0) // insert the line
       
   562                 para->insert(idx, s.unicode() + start + 1, len);
       
   563             else
       
   564                 para->invalidate(0);
       
   565             if (formatting) { // set formats to the chars of the line
       
   566                 for (int i = 0; i < len; ++i) {
       
   567                     if (formatting->at(i + lastIndex).format()) {
       
   568                         formatting->at(i + lastIndex).format()->addRef();
       
   569                         para->string()->setFormat(i + idx, formatting->at(i + lastIndex).format(), true);
       
   570                     }
       
   571                 }
       
   572                 lastIndex += len;
       
   573             }
       
   574             start = end; // next start is at the end of this line
       
   575             idx += len; // increase the index of the cursor to the end of the inserted text
       
   576             if (s[end] == QLatin1Char('\n')) { // if at the end was a line break, break the line
       
   577                 splitAndInsertEmptyParagraph(false, true);
       
   578                 para->setEndState(-1);
       
   579                 para->prev()->format(-1, false);
       
   580                 lastIndex++;
       
   581             }
       
   582 
       
   583         } while (end < (int)s.length());
       
   584 
       
   585         para->format(-1, false);
       
   586         int dy = para->rect().y() + para->rect().height() - y;
       
   587         Q3TextParagraph *p = para;
       
   588         p->setParagId(p->prev() ? p->prev()->paragId() + 1 : 0);
       
   589         p = p->next();
       
   590         while (p) {
       
   591             p->setParagId(p->prev()->paragId() + 1);
       
   592             p->move(dy);
       
   593             p->invalidate(0);
       
   594             p->setEndState(-1);
       
   595             p = p->next();
       
   596         }
       
   597     }
       
   598 
       
   599     int h = para->rect().height();
       
   600     para->format(-1, true);
       
   601     if (h != para->rect().height())
       
   602         invalidateNested();
       
   603     else if (para->document() && para->document()->parent())
       
   604         para->document()->nextDoubleBuffered = true;
       
   605 
       
   606     fixCursorPosition();
       
   607 }
       
   608 
       
   609 void Q3TextCursor::gotoLeft()
       
   610 {
       
   611     if (para->string()->isRightToLeft())
       
   612         gotoNextLetter();
       
   613     else
       
   614         gotoPreviousLetter();
       
   615 }
       
   616 
       
   617 void Q3TextCursor::gotoPreviousLetter()
       
   618 {
       
   619     tmpX = -1;
       
   620 
       
   621     if (idx > 0) {
       
   622         idx = para->string()->previousCursorPosition(idx);
       
   623 #ifndef QT_NO_TEXTCUSTOMITEM
       
   624         const Q3TextStringChar *tsc = para->at(idx);
       
   625         if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
       
   626             processNesting(EnterEnd);
       
   627 #endif
       
   628     } else if (para->prev()) {
       
   629         para = para->prev();
       
   630         while (!para->isVisible() && para->prev())
       
   631             para = para->prev();
       
   632         idx = para->length() - 1;
       
   633     } else if (nestedDepth()) {
       
   634         pop();
       
   635         processNesting(Prev);
       
   636         if (idx == -1) {
       
   637             pop();
       
   638             if (idx > 0) {
       
   639                 idx = para->string()->previousCursorPosition(idx);
       
   640 #ifndef QT_NO_TEXTCUSTOMITEM
       
   641                 const Q3TextStringChar *tsc = para->at(idx);
       
   642                 if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
       
   643                     processNesting(EnterEnd);
       
   644 #endif
       
   645             } else if (para->prev()) {
       
   646                 para = para->prev();
       
   647                 idx = para->length() - 1;
       
   648             }
       
   649         }
       
   650     }
       
   651 }
       
   652 
       
   653 void Q3TextCursor::push()
       
   654 {
       
   655     indices.push(idx);
       
   656     paras.push(para);
       
   657     xOffsets.push(ox);
       
   658     yOffsets.push(oy);
       
   659 }
       
   660 
       
   661 void Q3TextCursor::pop()
       
   662 {
       
   663     if (indices.isEmpty())
       
   664         return;
       
   665     idx = indices.pop();
       
   666     para = paras.pop();
       
   667     ox = xOffsets.pop();
       
   668     oy = yOffsets.pop();
       
   669 }
       
   670 
       
   671 void Q3TextCursor::restoreState()
       
   672 {
       
   673     while (!indices.isEmpty())
       
   674         pop();
       
   675 }
       
   676 
       
   677 bool Q3TextCursor::place(const QPoint &p, Q3TextParagraph *s, bool link)
       
   678 {
       
   679     QPoint pos(p);
       
   680     QRect r;
       
   681     Q3TextParagraph *str = s;
       
   682     if (pos.y() < s->rect().y()) {
       
   683         pos.setY(s->rect().y());
       
   684 #ifdef Q_WS_MAC
       
   685         pos.setX(s->rect().x());
       
   686 #endif
       
   687     }
       
   688     while (s) {
       
   689         r = s->rect();
       
   690         r.setWidth(document() ? document()->width() : QWIDGETSIZE_MAX);
       
   691         if (s->isVisible())
       
   692             str = s;
       
   693         if (pos.y() >= r.y() && pos.y() <= r.y() + r.height())
       
   694             break;
       
   695         if (!s->next()) {
       
   696 #ifdef Q_WS_MAC
       
   697             pos.setX(s->rect().x() + s->rect().width());
       
   698 #endif
       
   699             break;
       
   700         }
       
   701         s = s->next();
       
   702     }
       
   703 
       
   704     if (!s || !str)
       
   705         return false;
       
   706 
       
   707     s = str;
       
   708 
       
   709     setParagraph(s);
       
   710     int y = s->rect().y();
       
   711     int lines = s->lines();
       
   712     Q3TextStringChar *chr = 0;
       
   713     int index = 0;
       
   714     int i = 0;
       
   715     int cy = 0;
       
   716     int ch = 0;
       
   717     for (; i < lines; ++i) {
       
   718         chr = s->lineStartOfLine(i, &index);
       
   719         cy = s->lineY(i);
       
   720         ch = s->lineHeight(i);
       
   721         if (!chr)
       
   722             return false;
       
   723         if (pos.y() <= y + cy + ch)
       
   724             break;
       
   725     }
       
   726     int nextLine;
       
   727     if (i < lines - 1)
       
   728         s->lineStartOfLine(i+1, &nextLine);
       
   729     else
       
   730         nextLine = s->length();
       
   731     i = index;
       
   732     int x = s->rect().x();
       
   733     if (pos.x() < x)
       
   734         pos.setX(x + 1);
       
   735     int cw;
       
   736     int curpos = s->length()-1;
       
   737     int dist = 10000000;
       
   738     bool inCustom = false;
       
   739     while (i < nextLine) {
       
   740         chr = s->at(i);
       
   741         int cpos = x + chr->x;
       
   742         cw = s->string()->width(i);
       
   743 #ifndef QT_NO_TEXTCUSTOMITEM
       
   744         if (chr->isCustom() && chr->customItem()->isNested()) {
       
   745             if (pos.x() >= cpos && pos.x() <= cpos + cw &&
       
   746                  pos.y() >= y + cy && pos.y() <= y + cy + chr->height()) {
       
   747                 inCustom = true;
       
   748                 curpos = i;
       
   749                 break;
       
   750             }
       
   751         } else
       
   752 #endif
       
   753         {
       
   754             if(chr->rightToLeft)
       
   755                 cpos += cw;
       
   756             int diff = cpos - pos.x();
       
   757             bool dm = diff < 0 ? !chr->rightToLeft : chr->rightToLeft;
       
   758             if ((QABS(diff) < dist || (dist == diff && dm == true)) && para->string()->validCursorPosition(i)) {
       
   759                 dist = QABS(diff);
       
   760                 if (!link || pos.x() >= x + chr->x)
       
   761                     curpos = i;
       
   762             }
       
   763         }
       
   764         i++;
       
   765     }
       
   766     setIndex(curpos);
       
   767 
       
   768 #ifndef QT_NO_TEXTCUSTOMITEM
       
   769     if (inCustom && para->document() && para->at(curpos)->isCustom() && para->at(curpos)->customItem()->isNested()) {
       
   770         Q3TextDocument *oldDoc = para->document();
       
   771         gotoIntoNested(pos);
       
   772         if (oldDoc == para->document())
       
   773             return true;
       
   774         QPoint p(pos.x() - offsetX(), pos.y() - offsetY());
       
   775         if (!place(p, document()->firstParagraph(), link))
       
   776             pop();
       
   777     }
       
   778 #endif
       
   779     return true;
       
   780 }
       
   781 
       
   782 bool Q3TextCursor::processNesting(Operation op)
       
   783 {
       
   784     if (!para->document())
       
   785         return false;
       
   786     Q3TextDocument* doc = para->document();
       
   787     push();
       
   788     ox = para->at(idx)->x;
       
   789     int bl, y;
       
   790     para->lineHeightOfChar(idx, &bl, &y);
       
   791     oy = y + para->rect().y();
       
   792     bool ok = false;
       
   793 
       
   794 #ifndef QT_NO_TEXTCUSTOMITEM
       
   795     switch (op) {
       
   796     case EnterBegin:
       
   797         ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy);
       
   798         break;
       
   799     case EnterEnd:
       
   800         ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy, true);
       
   801         break;
       
   802     case Next:
       
   803         ok = para->at(idx)->customItem()->next(this, doc, para, idx, ox, oy);
       
   804         break;
       
   805     case Prev:
       
   806         ok = para->at(idx)->customItem()->prev(this, doc, para, idx, ox, oy);
       
   807         break;
       
   808     case Down:
       
   809         ok = para->at(idx)->customItem()->down(this, doc, para, idx, ox, oy);
       
   810         break;
       
   811     case Up:
       
   812         ok = para->at(idx)->customItem()->up(this, doc, para, idx, ox, oy);
       
   813         break;
       
   814     }
       
   815     if (!ok)
       
   816 #endif
       
   817         pop();
       
   818     return ok;
       
   819 }
       
   820 
       
   821 void Q3TextCursor::gotoRight()
       
   822 {
       
   823     if (para->string()->isRightToLeft())
       
   824         gotoPreviousLetter();
       
   825     else
       
   826         gotoNextLetter();
       
   827 }
       
   828 
       
   829 void Q3TextCursor::gotoNextLetter()
       
   830 {
       
   831    tmpX = -1;
       
   832 
       
   833 #ifndef QT_NO_TEXTCUSTOMITEM
       
   834     const Q3TextStringChar *tsc = para->at(idx);
       
   835     if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) {
       
   836         if (processNesting(EnterBegin))
       
   837             return;
       
   838     }
       
   839 #endif
       
   840 
       
   841     if (idx < para->length() - 1) {
       
   842         idx = para->string()->nextCursorPosition(idx);
       
   843     } else if (para->next()) {
       
   844         para = para->next();
       
   845         while (!para->isVisible() && para->next())
       
   846             para = para->next();
       
   847         idx = 0;
       
   848     } else if (nestedDepth()) {
       
   849         pop();
       
   850         processNesting(Next);
       
   851         if (idx == -1) {
       
   852             pop();
       
   853             if (idx < para->length() - 1) {
       
   854                 idx = para->string()->nextCursorPosition(idx);
       
   855             } else if (para->next()) {
       
   856                 para = para->next();
       
   857                 idx = 0;
       
   858             }
       
   859         }
       
   860     }
       
   861 }
       
   862 
       
   863 void Q3TextCursor::gotoUp()
       
   864 {
       
   865     int indexOfLineStart;
       
   866     int line;
       
   867     Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
       
   868     if (!c)
       
   869         return;
       
   870 
       
   871     if (tmpX < 0)
       
   872         tmpX = x();
       
   873 
       
   874     if (indexOfLineStart == 0) {
       
   875         if (!para->prev()) {
       
   876             if (!nestedDepth())
       
   877                 return;
       
   878             pop();
       
   879             processNesting(Up);
       
   880             if (idx == -1) {
       
   881                 pop();
       
   882                 if (!para->prev())
       
   883                     return;
       
   884                 idx = tmpX = 0;
       
   885             } else {
       
   886                 tmpX = -1;
       
   887                 return;
       
   888             }
       
   889         }
       
   890         Q3TextParagraph *p = para->prev();
       
   891         while (p && !p->isVisible())
       
   892             p = p->prev();
       
   893         if (p)
       
   894             para = p;
       
   895         int lastLine = para->lines() - 1;
       
   896         if (!para->lineStartOfLine(lastLine, &indexOfLineStart))
       
   897             return;
       
   898         idx = indexOfLineStart;
       
   899         while (idx < para->length()-1 && para->at(idx)->x < tmpX)
       
   900             ++idx;
       
   901         if (idx > indexOfLineStart &&
       
   902             para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
       
   903             --idx;
       
   904     } else {
       
   905         --line;
       
   906         int oldIndexOfLineStart = indexOfLineStart;
       
   907         if (!para->lineStartOfLine(line, &indexOfLineStart))
       
   908             return;
       
   909         idx = indexOfLineStart;
       
   910         while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX)
       
   911             ++idx;
       
   912         if (idx > indexOfLineStart &&
       
   913             para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
       
   914             --idx;
       
   915     }
       
   916     fixCursorPosition();
       
   917 }
       
   918 
       
   919 void Q3TextCursor::gotoDown()
       
   920 {
       
   921     int indexOfLineStart;
       
   922     int line;
       
   923     Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
       
   924     if (!c)
       
   925         return;
       
   926 
       
   927     if (tmpX < 0)
       
   928         tmpX = x();
       
   929 
       
   930     if (line == para->lines() - 1) {
       
   931         if (!para->next()) {
       
   932             if (!nestedDepth())
       
   933                 return;
       
   934             pop();
       
   935             processNesting(Down);
       
   936             if (idx == -1) {
       
   937                 pop();
       
   938                 if (!para->next())
       
   939                     return;
       
   940                 idx = tmpX = 0;
       
   941             } else {
       
   942                 tmpX = -1;
       
   943                 return;
       
   944             }
       
   945         }
       
   946         Q3TextParagraph *s = para->next();
       
   947         while (s && !s->isVisible())
       
   948             s = s->next();
       
   949         if (s)
       
   950             para = s;
       
   951         if (!para->lineStartOfLine(0, &indexOfLineStart))
       
   952             return;
       
   953         int end;
       
   954         if (para->lines() == 1)
       
   955             end = para->length();
       
   956         else
       
   957             para->lineStartOfLine(1, &end);
       
   958 
       
   959         idx = indexOfLineStart;
       
   960         while (idx < end-1 && para->at(idx)->x < tmpX)
       
   961             ++idx;
       
   962         if (idx > indexOfLineStart &&
       
   963             para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
       
   964             --idx;
       
   965     } else {
       
   966         ++line;
       
   967         int end;
       
   968         if (line == para->lines() - 1)
       
   969             end = para->length();
       
   970         else
       
   971             para->lineStartOfLine(line + 1, &end);
       
   972         if (!para->lineStartOfLine(line, &indexOfLineStart))
       
   973             return;
       
   974         idx = indexOfLineStart;
       
   975         while (idx < end-1 && para->at(idx)->x < tmpX)
       
   976             ++idx;
       
   977         if (idx > indexOfLineStart &&
       
   978             para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
       
   979             --idx;
       
   980     }
       
   981     fixCursorPosition();
       
   982 }
       
   983 
       
   984 void Q3TextCursor::gotoLineEnd()
       
   985 {
       
   986     tmpX = -1;
       
   987     int indexOfLineStart;
       
   988     int line;
       
   989     Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
       
   990     if (!c)
       
   991         return;
       
   992 
       
   993     if (line == para->lines() - 1) {
       
   994         idx = para->length() - 1;
       
   995     } else {
       
   996         c = para->lineStartOfLine(++line, &indexOfLineStart);
       
   997         indexOfLineStart--;
       
   998         idx = indexOfLineStart;
       
   999     }
       
  1000 }
       
  1001 
       
  1002 void Q3TextCursor::gotoLineStart()
       
  1003 {
       
  1004     tmpX = -1;
       
  1005     int indexOfLineStart;
       
  1006     int line;
       
  1007     Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
       
  1008     if (!c)
       
  1009         return;
       
  1010 
       
  1011     idx = indexOfLineStart;
       
  1012 }
       
  1013 
       
  1014 void Q3TextCursor::gotoHome()
       
  1015 {
       
  1016     if (topParagraph()->document())
       
  1017         gotoPosition(topParagraph()->document()->firstParagraph());
       
  1018     else
       
  1019         gotoLineStart();
       
  1020 }
       
  1021 
       
  1022 void Q3TextCursor::gotoEnd()
       
  1023 {
       
  1024     if (topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid())
       
  1025         gotoPosition(topParagraph()->document()->lastParagraph(),
       
  1026                       topParagraph()->document()->lastParagraph()->length() - 1);
       
  1027     else
       
  1028         gotoLineEnd();
       
  1029 }
       
  1030 
       
  1031 void Q3TextCursor::gotoPageUp(int visibleHeight)
       
  1032 {
       
  1033     int targetY  = globalY() - visibleHeight;
       
  1034     Q3TextParagraph* old; int index;
       
  1035     do {
       
  1036         old = para; index = idx;
       
  1037         gotoUp();
       
  1038     } while ((old != para || index != idx)  && globalY() > targetY);
       
  1039 }
       
  1040 
       
  1041 void Q3TextCursor::gotoPageDown(int visibleHeight)
       
  1042 {
       
  1043     int targetY  = globalY() + visibleHeight;
       
  1044     Q3TextParagraph* old; int index;
       
  1045     do {
       
  1046         old = para; index = idx;
       
  1047         gotoDown();
       
  1048     } while ((old != para || index != idx) && globalY() < targetY);
       
  1049 }
       
  1050 
       
  1051 void Q3TextCursor::gotoWordRight()
       
  1052 {
       
  1053     if (para->string()->isRightToLeft())
       
  1054         gotoPreviousWord();
       
  1055     else
       
  1056         gotoNextWord();
       
  1057 }
       
  1058 
       
  1059 void Q3TextCursor::gotoWordLeft()
       
  1060 {
       
  1061     if (para->string()->isRightToLeft())
       
  1062         gotoNextWord();
       
  1063     else
       
  1064         gotoPreviousWord();
       
  1065 }
       
  1066 
       
  1067 static bool is_seperator(const QChar &c, bool onlySpace)
       
  1068 {
       
  1069     if (onlySpace)
       
  1070         return c.isSpace();
       
  1071     return c.isSpace() ||
       
  1072            c == QLatin1Char('\t') ||
       
  1073            c == QLatin1Char('.') ||
       
  1074            c == QLatin1Char(',') ||
       
  1075            c == QLatin1Char(':') ||
       
  1076            c == QLatin1Char(';') ||
       
  1077            c == QLatin1Char('-') ||
       
  1078            c == QLatin1Char('<') ||
       
  1079            c == QLatin1Char('>') ||
       
  1080            c == QLatin1Char('[') ||
       
  1081            c == QLatin1Char(']') ||
       
  1082            c == QLatin1Char('(') ||
       
  1083            c == QLatin1Char(')') ||
       
  1084            c == QLatin1Char('{') ||
       
  1085            c == QLatin1Char('}');
       
  1086 }
       
  1087 
       
  1088 void Q3TextCursor::gotoPreviousWord(bool onlySpace)
       
  1089 {
       
  1090     gotoPreviousLetter();
       
  1091     tmpX = -1;
       
  1092     Q3TextString *s = para->string();
       
  1093     bool allowSame = false;
       
  1094     if (idx == ((int)s->length()-1))
       
  1095         return;
       
  1096     for (int i = idx; i >= 0; --i) {
       
  1097         if (is_seperator(s->at(i).c, onlySpace)) {
       
  1098             if (!allowSame)
       
  1099                 continue;
       
  1100             idx = i + 1;
       
  1101             return;
       
  1102         }
       
  1103         if (!allowSame && !is_seperator(s->at(i).c, onlySpace))
       
  1104             allowSame = true;
       
  1105     }
       
  1106     idx = 0;
       
  1107 }
       
  1108 
       
  1109 void Q3TextCursor::gotoNextWord(bool onlySpace)
       
  1110 {
       
  1111     tmpX = -1;
       
  1112     Q3TextString *s = para->string();
       
  1113     bool allowSame = false;
       
  1114     for (int i = idx; i < (int)s->length(); ++i) {
       
  1115         if (!is_seperator(s->at(i).c, onlySpace)) {
       
  1116             if (!allowSame)
       
  1117                 continue;
       
  1118             idx = i;
       
  1119             return;
       
  1120         }
       
  1121         if (!allowSame && is_seperator(s->at(i).c, onlySpace))
       
  1122             allowSame = true;
       
  1123 
       
  1124     }
       
  1125 
       
  1126     if (idx < ((int)s->length()-1)) {
       
  1127         gotoLineEnd();
       
  1128     } else if (para->next()) {
       
  1129         Q3TextParagraph *p = para->next();
       
  1130         while (p  && !p->isVisible())
       
  1131             p = p->next();
       
  1132         if (s) {
       
  1133             para = p;
       
  1134             idx = 0;
       
  1135         }
       
  1136     } else {
       
  1137         gotoLineEnd();
       
  1138     }
       
  1139 }
       
  1140 
       
  1141 bool Q3TextCursor::atParagStart()
       
  1142 {
       
  1143     return idx == 0;
       
  1144 }
       
  1145 
       
  1146 bool Q3TextCursor::atParagEnd()
       
  1147 {
       
  1148     return idx == para->length() - 1;
       
  1149 }
       
  1150 
       
  1151 void Q3TextCursor::splitAndInsertEmptyParagraph(bool ind, bool updateIds)
       
  1152 {
       
  1153     if (!para->document())
       
  1154         return;
       
  1155     tmpX = -1;
       
  1156     Q3TextFormat *f = 0;
       
  1157     if (para->document()->useFormatCollection()) {
       
  1158         f = para->at(idx)->format();
       
  1159         if (idx == para->length() - 1 && idx > 0)
       
  1160             f = para->at(idx - 1)->format();
       
  1161         if (f->isMisspelled()) {
       
  1162             f->removeRef();
       
  1163             f = para->document()->formatCollection()->format(f->font(), f->color());
       
  1164         }
       
  1165     }
       
  1166 
       
  1167     if (atParagEnd()) {
       
  1168         Q3TextParagraph *n = para->next();
       
  1169         Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
       
  1170         if (f)
       
  1171             s->setFormat(0, 1, f, true);
       
  1172         s->copyParagData(para);
       
  1173         if (ind) {
       
  1174             int oi, ni;
       
  1175             s->indent(&oi, &ni);
       
  1176             para = s;
       
  1177             idx = ni;
       
  1178         } else {
       
  1179             para = s;
       
  1180             idx = 0;
       
  1181         }
       
  1182     } else if (atParagStart()) {
       
  1183         Q3TextParagraph *p = para->prev();
       
  1184         Q3TextParagraph *s = para->document()->createParagraph(para->document(), p, para, updateIds);
       
  1185         if (f)
       
  1186             s->setFormat(0, 1, f, true);
       
  1187         s->copyParagData(para);
       
  1188         if (ind) {
       
  1189             s->indent();
       
  1190             s->format();
       
  1191             indent();
       
  1192             para->format();
       
  1193         }
       
  1194     } else {
       
  1195         QString str = para->string()->toString().mid(idx, 0xFFFFFF);
       
  1196         Q3TextParagraph *n = para->next();
       
  1197         Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
       
  1198         s->copyParagData(para);
       
  1199         s->remove(0, 1);
       
  1200         s->append(str, true);
       
  1201         for (int i = 0; i < str.length(); ++i) {
       
  1202             Q3TextStringChar* tsc = para->at(idx + i);
       
  1203             s->setFormat(i, 1, tsc->format(), true);
       
  1204 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1205             if (tsc->isCustom()) {
       
  1206                 Q3TextCustomItem * item = tsc->customItem();
       
  1207                 s->at(i)->setCustomItem(item);
       
  1208                 tsc->loseCustomItem();
       
  1209             }
       
  1210 #endif
       
  1211             if (tsc->isAnchor())
       
  1212                 s->at(i)->setAnchor(tsc->anchorName(),
       
  1213                                        tsc->anchorHref());
       
  1214         }
       
  1215         para->truncate(idx);
       
  1216         if (ind) {
       
  1217             int oi, ni;
       
  1218             s->indent(&oi, &ni);
       
  1219             para = s;
       
  1220             idx = ni;
       
  1221         } else {
       
  1222             para = s;
       
  1223             idx = 0;
       
  1224         }
       
  1225     }
       
  1226 
       
  1227     invalidateNested();
       
  1228 }
       
  1229 
       
  1230 bool Q3TextCursor::remove()
       
  1231 {
       
  1232     tmpX = -1;
       
  1233     if (!atParagEnd()) {
       
  1234         int next = para->string()->nextCursorPosition(idx);
       
  1235         para->remove(idx, next-idx);
       
  1236         int h = para->rect().height();
       
  1237         para->format(-1, true);
       
  1238         if (h != para->rect().height())
       
  1239             invalidateNested();
       
  1240         else if (para->document() && para->document()->parent())
       
  1241             para->document()->nextDoubleBuffered = true;
       
  1242         return false;
       
  1243     } else if (para->next()) {
       
  1244         para->join(para->next());
       
  1245         invalidateNested();
       
  1246         return true;
       
  1247     }
       
  1248     return false;
       
  1249 }
       
  1250 
       
  1251 /* needed to implement backspace the correct way */
       
  1252 bool Q3TextCursor::removePreviousChar()
       
  1253 {
       
  1254     tmpX = -1;
       
  1255     if (!atParagStart()) {
       
  1256         para->remove(idx-1, 1);
       
  1257         int h = para->rect().height();
       
  1258         idx--;
       
  1259         // shouldn't be needed, just to make sure.
       
  1260         fixCursorPosition();
       
  1261         para->format(-1, true);
       
  1262         if (h != para->rect().height())
       
  1263             invalidateNested();
       
  1264         else if (para->document() && para->document()->parent())
       
  1265             para->document()->nextDoubleBuffered = true;
       
  1266         return false;
       
  1267     } else if (para->prev()) {
       
  1268         para = para->prev();
       
  1269         para->join(para->next());
       
  1270         invalidateNested();
       
  1271         return true;
       
  1272     }
       
  1273     return false;
       
  1274 }
       
  1275 
       
  1276 void Q3TextCursor::indent()
       
  1277 {
       
  1278     int oi = 0, ni = 0;
       
  1279     para->indent(&oi, &ni);
       
  1280     if (oi == ni)
       
  1281         return;
       
  1282 
       
  1283     if (idx >= oi)
       
  1284         idx += ni - oi;
       
  1285     else
       
  1286         idx = ni;
       
  1287 }
       
  1288 
       
  1289 void Q3TextCursor::fixCursorPosition()
       
  1290 {
       
  1291     // searches for the closest valid cursor position
       
  1292     if (para->string()->validCursorPosition(idx))
       
  1293         return;
       
  1294 
       
  1295     int lineIdx;
       
  1296     Q3TextStringChar *start = para->lineStartOfChar(idx, &lineIdx, 0);
       
  1297     int x = para->string()->at(idx).x;
       
  1298     int diff = QABS(start->x - x);
       
  1299     int best = lineIdx;
       
  1300 
       
  1301     Q3TextStringChar *c = start;
       
  1302     ++c;
       
  1303 
       
  1304     Q3TextStringChar *end = &para->string()->at(para->length()-1);
       
  1305     while (c <= end && !c->lineStart) {
       
  1306         int xp = c->x;
       
  1307         if (c->rightToLeft)
       
  1308             xp += para->string()->width(lineIdx + (c-start));
       
  1309         int ndiff = QABS(xp - x);
       
  1310         if (ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start))) {
       
  1311             diff = ndiff;
       
  1312             best = lineIdx + (c-start);
       
  1313         }
       
  1314         ++c;
       
  1315     }
       
  1316     idx = best;
       
  1317 }
       
  1318 
       
  1319 
       
  1320 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  1321 
       
  1322 Q3TextDocument::Q3TextDocument(Q3TextDocument *p)
       
  1323     : par(p), parentPar(0)
       
  1324 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1325     , tc(0)
       
  1326 #endif
       
  1327     , tArray(0), tStopWidth(0)
       
  1328 {
       
  1329     fCollection = par ? par->fCollection : new Q3TextFormatCollection;
       
  1330     init();
       
  1331 }
       
  1332 
       
  1333 void Q3TextDocument::init()
       
  1334 {
       
  1335     oTextValid = true;
       
  1336     mightHaveCustomItems = false;
       
  1337     if (par)
       
  1338         par->insertChild(this);
       
  1339     pProcessor = 0;
       
  1340     useFC = true;
       
  1341     pFormatter = 0;
       
  1342     indenter = 0;
       
  1343     fParag = 0;
       
  1344     txtFormat = Qt::AutoText;
       
  1345     preferRichText = false;
       
  1346     pages = false;
       
  1347     focusIndicator.parag = 0;
       
  1348     minw = 0;
       
  1349     wused = 0;
       
  1350     minwParag = curParag = 0;
       
  1351     align = Qt::AlignAuto;
       
  1352     nSelections = 1;
       
  1353 
       
  1354     setStyleSheet(Q3StyleSheet::defaultSheet());
       
  1355 #ifndef QT_NO_MIME
       
  1356     factory_ = Q3MimeSourceFactory::defaultFactory();
       
  1357 #endif
       
  1358     contxt.clear();
       
  1359 
       
  1360     underlLinks = par ? par->underlLinks : true;
       
  1361     backBrush = 0;
       
  1362     buf_pixmap = 0;
       
  1363     nextDoubleBuffered = false;
       
  1364 
       
  1365     if (par)
       
  1366         withoutDoubleBuffer = par->withoutDoubleBuffer;
       
  1367     else
       
  1368         withoutDoubleBuffer = false;
       
  1369 
       
  1370     lParag = fParag = createParagraph(this, 0, 0);
       
  1371 
       
  1372     cx = 0;
       
  1373     cy = 2;
       
  1374     if (par)
       
  1375         cx = cy = 0;
       
  1376     cw = 600;
       
  1377     vw = 0;
       
  1378     flow_ = new Q3TextFlow;
       
  1379     flow_->setWidth(cw);
       
  1380 
       
  1381     leftmargin = rightmargin = 4;
       
  1382     scaleFontsFactor = 1;
       
  1383 
       
  1384     commandHistory = new Q3TextCommandHistory(100);
       
  1385     tStopWidth = formatCollection()->defaultFormat()->width(QLatin1Char('x')) * 8;
       
  1386 }
       
  1387 
       
  1388 Q3TextDocument::~Q3TextDocument()
       
  1389 {
       
  1390     delete commandHistory;
       
  1391     if (par)
       
  1392         par->removeChild(this);
       
  1393     clear();
       
  1394     delete flow_;
       
  1395     if (!par) {
       
  1396         delete pFormatter;
       
  1397         delete fCollection;
       
  1398     }
       
  1399     delete pProcessor;
       
  1400     delete buf_pixmap;
       
  1401     delete indenter;
       
  1402     delete backBrush;
       
  1403     delete [] tArray;
       
  1404 }
       
  1405 
       
  1406 void Q3TextDocument::clear(bool createEmptyParag)
       
  1407 {
       
  1408     while (fParag) {
       
  1409         Q3TextParagraph *p = fParag->next();
       
  1410         delete fParag;
       
  1411         fParag = p;
       
  1412     }
       
  1413     if (flow_)
       
  1414         flow_->clear();
       
  1415     fParag = lParag = 0;
       
  1416     if (createEmptyParag)
       
  1417         fParag = lParag = createParagraph(this);
       
  1418     selections.clear();
       
  1419     oText.clear();
       
  1420     oTextValid = false;
       
  1421 }
       
  1422 
       
  1423 int Q3TextDocument::widthUsed() const
       
  1424 {
       
  1425     return wused + 2*border_tolerance;
       
  1426 }
       
  1427 
       
  1428 int Q3TextDocument::height() const
       
  1429 {
       
  1430     int h = 0;
       
  1431     if (lParag)
       
  1432         h = lParag->rect().top() + lParag->rect().height() + 1;
       
  1433     int fh = flow_->boundingRect().bottom();
       
  1434     return qMax(h, fh);
       
  1435 }
       
  1436 
       
  1437 
       
  1438 
       
  1439 Q3TextParagraph *Q3TextDocument::createParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
       
  1440 {
       
  1441     return new Q3TextParagraph(dc, pr, nx, updateIds);
       
  1442 }
       
  1443 
       
  1444 bool Q3TextDocument::setMinimumWidth(int needed, int used, Q3TextParagraph *p)
       
  1445 {
       
  1446     if (needed == -1) {
       
  1447         minw = 0;
       
  1448         wused = 0;
       
  1449         p = 0;
       
  1450     }
       
  1451     if (p == minwParag) {
       
  1452         if (minw > needed) {
       
  1453             Q3TextParagraph *tp = fParag;
       
  1454             while (tp) {
       
  1455                 if (tp != p && tp->minwidth > needed) {
       
  1456                     needed = tp->minwidth;
       
  1457                     minwParag = tp;
       
  1458                 }
       
  1459                 tp = tp->n;
       
  1460             }
       
  1461         }
       
  1462         minw = needed;
       
  1463         emit minimumWidthChanged(minw);
       
  1464     } else if (needed > minw) {
       
  1465         minw = needed;
       
  1466         minwParag = p;
       
  1467         emit minimumWidthChanged(minw);
       
  1468     }
       
  1469     wused = qMax(wused, used);
       
  1470     wused = qMax(wused, minw);
       
  1471     cw = qMax(minw, cw);
       
  1472     return true;
       
  1473 }
       
  1474 
       
  1475 void Q3TextDocument::setPlainText(const QString &text)
       
  1476 {
       
  1477     preferRichText = false;
       
  1478     clear();
       
  1479     oTextValid = true;
       
  1480     oText = text;
       
  1481 
       
  1482     int lastNl = 0;
       
  1483     int nl = text.indexOf(QLatin1Char('\n'));
       
  1484     if (nl == -1) {
       
  1485         lParag = createParagraph(this, lParag, 0);
       
  1486         if (!fParag)
       
  1487             fParag = lParag;
       
  1488         QString s = text;
       
  1489         if (!s.isEmpty()) {
       
  1490             if (s[(int)s.length() - 1] == QLatin1Char('\r'))
       
  1491                 s.remove(s.length() - 1, 1);
       
  1492             lParag->append(s);
       
  1493         }
       
  1494     } else {
       
  1495         for (;;) {
       
  1496             lParag = createParagraph(this, lParag, 0);
       
  1497             if (!fParag)
       
  1498                 fParag = lParag;
       
  1499             int l = nl - lastNl;
       
  1500             if (l > 0) {
       
  1501                 if (text.unicode()[nl-1] == QLatin1Char('\r'))
       
  1502                     l--;
       
  1503                 QString cs = QString::fromRawData(text.unicode()+lastNl, l);
       
  1504                 lParag->append(cs);
       
  1505             }
       
  1506             if (nl == (int)text.length())
       
  1507                 break;
       
  1508             lastNl = nl + 1;
       
  1509             nl = text.indexOf(QLatin1Char('\n'), nl + 1);
       
  1510             if (nl == -1)
       
  1511                 nl = text.length();
       
  1512         }
       
  1513     }
       
  1514     if (!lParag)
       
  1515         lParag = fParag = createParagraph(this, 0, 0);
       
  1516 }
       
  1517 
       
  1518 struct Q3TextDocumentTag {
       
  1519     Q3TextDocumentTag(){}
       
  1520     Q3TextDocumentTag(const QString&n, const Q3StyleSheetItem* s, const Q3TextFormat& f)
       
  1521         :name(n),style(s), format(f), alignment(Qt::AlignAuto), direction(QChar::DirON),liststyle(Q3StyleSheetItem::ListDisc) {
       
  1522             wsm = Q3StyleSheetItem::WhiteSpaceNormal;
       
  1523     }
       
  1524     QString name;
       
  1525     const Q3StyleSheetItem* style;
       
  1526     QString anchorHref;
       
  1527     Q3StyleSheetItem::WhiteSpaceMode wsm;
       
  1528     Q3TextFormat format;
       
  1529     signed int alignment : 16;
       
  1530     signed int direction : 5;
       
  1531     Q3StyleSheetItem::ListStyle liststyle;
       
  1532 
       
  1533     Q3TextDocumentTag( const Q3TextDocumentTag& t) {
       
  1534         name = t.name;
       
  1535         style = t.style;
       
  1536         anchorHref = t.anchorHref;
       
  1537         wsm = t.wsm;
       
  1538         format = t.format;
       
  1539         alignment = t.alignment;
       
  1540         direction = t.direction;
       
  1541         liststyle = t.liststyle;
       
  1542     }
       
  1543     Q3TextDocumentTag& operator=(const Q3TextDocumentTag& t) {
       
  1544         name = t.name;
       
  1545         style = t.style;
       
  1546         anchorHref = t.anchorHref;
       
  1547         wsm = t.wsm;
       
  1548         format = t.format;
       
  1549         alignment = t.alignment;
       
  1550         direction = t.direction;
       
  1551         liststyle = t.liststyle;
       
  1552         return *this;
       
  1553     }
       
  1554 
       
  1555     Q_DUMMY_COMPARISON_OPERATOR(Q3TextDocumentTag)
       
  1556 };
       
  1557 
       
  1558 
       
  1559 #define NEWPAR \
       
  1560     do{ \
       
  1561         if (!hasNewPar) { \
       
  1562             if (!textEditMode && curpar && curpar->length()>1 \
       
  1563                  && curpar->at(curpar->length()-2)->c == QChar::LineSeparator) \
       
  1564                 curpar->remove(curpar->length()-2, 1); \
       
  1565             curpar = createParagraph(this, curpar, curpar->next()); \
       
  1566             styles.append(vec); \
       
  1567             vec = 0; \
       
  1568         } \
       
  1569         hasNewPar = true; \
       
  1570         curpar->rtext = true;  \
       
  1571         curpar->align = curtag.alignment; \
       
  1572         curpar->lstyle = curtag.liststyle; \
       
  1573         curpar->litem = (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem); \
       
  1574         curpar->str->setDirection((QChar::Direction)curtag.direction); \
       
  1575         space = true; \
       
  1576         tabExpansionColumn = 0; \
       
  1577         delete vec; \
       
  1578         vec = new QVector<Q3StyleSheetItem *>(); \
       
  1579         for (QStack<Q3TextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it) \
       
  1580             vec->append(const_cast<Q3StyleSheetItem *>((*it).style)); \
       
  1581         vec->append(const_cast<Q3StyleSheetItem *>(curtag.style)); \
       
  1582     } while(false);
       
  1583 
       
  1584 
       
  1585 void Q3TextDocument::setRichText(const QString &text, const QString &context, const Q3TextFormat *initialFormat)
       
  1586 {
       
  1587     preferRichText = true;
       
  1588     if (!context.isEmpty())
       
  1589         setContext(context);
       
  1590     clear();
       
  1591     fParag = lParag = createParagraph(this);
       
  1592     oTextValid = true;
       
  1593     oText = text;
       
  1594     setRichTextInternal(text, 0, initialFormat);
       
  1595     fParag->rtext = true;
       
  1596 }
       
  1597 
       
  1598 void Q3TextDocument::setRichTextInternal(const QString &text, Q3TextCursor* cursor, const Q3TextFormat *initialFormat)
       
  1599 {
       
  1600     Q3TextParagraph* curpar = lParag;
       
  1601     int pos = 0;
       
  1602     QStack<Q3TextDocumentTag> tags;
       
  1603     if (!initialFormat)
       
  1604         initialFormat = formatCollection()->defaultFormat();
       
  1605     Q3TextDocumentTag initag(QLatin1String(""), sheet_->item(QLatin1String("")), *initialFormat);
       
  1606     if (bodyText.isValid())
       
  1607         initag.format.setColor(bodyText);
       
  1608     Q3TextDocumentTag curtag = initag;
       
  1609     bool space = true;
       
  1610     bool canMergeLi = false;
       
  1611 
       
  1612     bool textEditMode = false;
       
  1613     int tabExpansionColumn = 0;
       
  1614 
       
  1615     const QChar* doc = text.unicode();
       
  1616     int length = text.length();
       
  1617     bool hasNewPar = curpar->length() <= 1;
       
  1618     QString anchorName;
       
  1619 
       
  1620     // style sheet handling for margin and line spacing calculation below
       
  1621     Q3TextParagraph* stylesPar = curpar;
       
  1622     QVector<Q3StyleSheetItem *>* vec = 0;
       
  1623     QList< QVector<Q3StyleSheetItem *> *> styles;
       
  1624 
       
  1625     if (cursor) {
       
  1626         cursor->splitAndInsertEmptyParagraph();
       
  1627         Q3TextCursor tmp = *cursor;
       
  1628         tmp.gotoPreviousLetter();
       
  1629         stylesPar = curpar = tmp.paragraph();
       
  1630         hasNewPar = true;
       
  1631         textEditMode = true;
       
  1632     } else {
       
  1633         NEWPAR;
       
  1634     }
       
  1635 
       
  1636     // set rtext spacing to false for the initial paragraph.
       
  1637     curpar->rtext = false;
       
  1638 
       
  1639     QString wellKnownTags = QLatin1String("br hr wsp table qt body meta title");
       
  1640 
       
  1641     while (pos < length) {
       
  1642         if (hasPrefix(doc, length, pos, QLatin1Char('<'))){
       
  1643             if (!hasPrefix(doc, length, pos+1, QLatin1Char('/'))) {
       
  1644                 // open tag
       
  1645                 QMap<QString, QString> attr;
       
  1646                 QMap<QString, QString>::Iterator it, end = attr.end();
       
  1647                 bool emptyTag = false;
       
  1648                 QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
       
  1649                 if (tagname.isEmpty())
       
  1650                     continue; // nothing we could do with this, probably parse error
       
  1651 
       
  1652                 const Q3StyleSheetItem* nstyle = sheet_->item(tagname);
       
  1653 
       
  1654                 if (nstyle) {
       
  1655                     // we might have to close some 'forgotten' tags
       
  1656                     while (!nstyle->allowedInContext(curtag.style)) {
       
  1657                         QString msg;
       
  1658                         msg.sprintf("QText Warning: Document not valid ('%s' not allowed in '%s' #%d)",
       
  1659                                      tagname.ascii(), curtag.style->name().ascii(), pos);
       
  1660                         sheet_->error(msg);
       
  1661                         if (tags.isEmpty())
       
  1662                             break;
       
  1663                         curtag = tags.pop();
       
  1664                     }
       
  1665 
       
  1666                     /* special handling for p and li for HTML
       
  1667                        compatibility. We do not want to embed blocks in
       
  1668                        p, and we do not want new blocks inside non-empty
       
  1669                        lis. Plus we want to merge empty lis sometimes. */
       
  1670                     if(nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
       
  1671                         canMergeLi = true;
       
  1672                     } else if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock) {
       
  1673                         while (curtag.style->name() == QLatin1String("p")) {
       
  1674                             if (tags.isEmpty())
       
  1675                                 break;
       
  1676                             curtag = tags.pop();
       
  1677                         }
       
  1678 
       
  1679                         if (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
       
  1680                             // we are in a li and a new block comes along
       
  1681                             if (nstyle->name() == QLatin1String("ul") || nstyle->name() == QLatin1String("ol"))
       
  1682                                 hasNewPar = false; // we want an empty li (like most browsers)
       
  1683                             if (!hasNewPar) {
       
  1684                                 /* do not add new blocks inside
       
  1685                                    non-empty lis */
       
  1686                                 while (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
       
  1687                                     if (tags.isEmpty())
       
  1688                                         break;
       
  1689                                     curtag = tags.pop();
       
  1690                                 }
       
  1691                             } else if (canMergeLi) {
       
  1692                                 /* we have an empty li and a block
       
  1693                                    comes along, merge them */
       
  1694                                 nstyle = curtag.style;
       
  1695                             }
       
  1696                             canMergeLi = false;
       
  1697                         }
       
  1698                     }
       
  1699                 }
       
  1700 
       
  1701 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1702                 Q3TextCustomItem* custom =  0;
       
  1703 #else
       
  1704                 bool custom = false;
       
  1705 #endif
       
  1706 
       
  1707                 // some well-known tags, some have a nstyle, some not
       
  1708                 if (wellKnownTags.contains(tagname)) {
       
  1709                     if (tagname == QLatin1String("br")) {
       
  1710                         emptyTag = space = true;
       
  1711                         int index = qMax(curpar->length(),1) - 1;
       
  1712                         Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
       
  1713                         curpar->append(QString(QChar(QChar::LineSeparator)));
       
  1714                         curpar->setFormat(index, 1, &format);
       
  1715                         hasNewPar = false;
       
  1716                     }  else if (tagname == QLatin1String("hr")) {
       
  1717                         emptyTag = space = true;
       
  1718 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1719                         custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
       
  1720 #endif
       
  1721                     } else if (tagname == QLatin1String("table")) {
       
  1722                         emptyTag = space = true;
       
  1723 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1724                         Q3TextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor);
       
  1725                         curpar->setAlignment(curtag.alignment);
       
  1726                         custom = parseTable(attr, format, doc, length, pos, curpar);
       
  1727 #endif
       
  1728                     } else if (tagname == QLatin1String("qt") || tagname == QLatin1String("body")) {
       
  1729                         it = attr.find(QLatin1String("bgcolor"));
       
  1730                         if (it != end) {
       
  1731                             QBrush *b = new QBrush(QColor(*it));
       
  1732                             setPaper(b);
       
  1733                         }
       
  1734                         it = attr.find(QLatin1String("background"));
       
  1735                         if (it != end) {
       
  1736 #ifndef QT_NO_MIME
       
  1737                             QImage img;
       
  1738                             QString bg = *it;
       
  1739                             const QMimeSource* m = factory_->data(bg, contxt);
       
  1740                             if (!m) {
       
  1741                                 qCritical("QRichText: no mimesource for %s",
       
  1742                                           QFile::encodeName(bg).data());
       
  1743                             } else {
       
  1744                                 if (!Q3ImageDrag::decode(m, img)) {
       
  1745                                     qCritical("Q3TextImage: cannot decode %s",
       
  1746                                               QFile::encodeName(bg).data());
       
  1747                                 }
       
  1748                             }
       
  1749                             if (!img.isNull()) {
       
  1750                                 QBrush *b = new QBrush(QColor(), QPixmap(img));
       
  1751                                 setPaper(b);
       
  1752                             }
       
  1753 #endif
       
  1754                         }
       
  1755                         it = attr.find(QLatin1String("text"));
       
  1756                         if (it != end) {
       
  1757                             QColor c(*it);
       
  1758                             initag.format.setColor(c);
       
  1759                             curtag.format.setColor(c);
       
  1760                             bodyText = c;
       
  1761                         }
       
  1762                         it = attr.find(QLatin1String("link"));
       
  1763                         if (it != end)
       
  1764                             linkColor = QColor(*it);
       
  1765                         it = attr.find(QLatin1String("title"));
       
  1766                         if (it != end)
       
  1767                             attribs.insert(QLatin1String("title"), *it);
       
  1768 
       
  1769                         if (textEditMode) {
       
  1770                             it = attr.find(QLatin1String("style"));
       
  1771                             if (it != end) {
       
  1772                                 QString a = *it;
       
  1773                                 int count = a.count(QLatin1Char(';')) + 1;
       
  1774                                 for (int s = 0; s < count; s++) {
       
  1775                                     QString style = a.section(QLatin1Char(';'), s, s);
       
  1776                                     if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
       
  1777                                         scaleFontsFactor = double(formatCollection()->defaultFormat()->fn.pointSize()) /
       
  1778                                                            style.mid(10, style.length() - 12).toInt();
       
  1779                                     }
       
  1780                                 }
       
  1781                             }
       
  1782                             nstyle = 0; // ignore body in textEditMode
       
  1783                         }
       
  1784                         // end qt- and body-tag handling
       
  1785                     } else if (tagname == QLatin1String("meta")) {
       
  1786                         if (attr[QLatin1String("name")] == QLatin1String("qrichtext") && attr[QLatin1String("content")] == QLatin1String("1"))
       
  1787                             textEditMode = true;
       
  1788                     } else if (tagname == QLatin1String("title")) {
       
  1789                         QString title;
       
  1790                         while (pos < length) {
       
  1791                             if (hasPrefix(doc, length, pos, QLatin1Char('<')) && hasPrefix(doc, length, pos+1, QLatin1Char('/')) &&
       
  1792                                  parseCloseTag(doc, length, pos) == QLatin1String("title"))
       
  1793                                 break;
       
  1794                             title += doc[pos];
       
  1795                             ++pos;
       
  1796                         }
       
  1797                         attribs.insert(QLatin1String("title"), title);
       
  1798                     }
       
  1799                 } // end of well-known tag handling
       
  1800 
       
  1801 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1802                 if (!custom) // try generic custom item
       
  1803                     custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
       
  1804 #endif
       
  1805                 if (!nstyle && !custom) // we have no clue what this tag could be, ignore it
       
  1806                     continue;
       
  1807 
       
  1808                 if (custom) {
       
  1809 #ifndef QT_NO_TEXTCUSTOMITEM
       
  1810                     int index = qMax(curpar->length(),1) - 1;
       
  1811                     Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
       
  1812                     curpar->append(QString(QLatin1Char('*')));
       
  1813                     Q3TextFormat* f = formatCollection()->format(&format);
       
  1814                     curpar->setFormat(index, 1, f);
       
  1815                     curpar->at(index)->setCustomItem(custom);
       
  1816                     if (!curtag.anchorHref.isEmpty())
       
  1817                         curpar->at(index)->setAnchor(QString(), curtag.anchorHref);
       
  1818                     if (!anchorName.isEmpty() ) {
       
  1819                         curpar->at(index)->setAnchor(anchorName, curpar->at(index)->anchorHref());
       
  1820                         anchorName.clear();
       
  1821                     }
       
  1822                     registerCustomItem(custom, curpar);
       
  1823                     hasNewPar = false;
       
  1824 #endif
       
  1825                 } else if (!emptyTag) {
       
  1826                     /* if we do nesting, push curtag on the stack,
       
  1827                        otherwise reinint curag. */
       
  1828                     if (curtag.style->name() != tagname || nstyle->selfNesting()) {
       
  1829                         tags.push(curtag);
       
  1830                     } else {
       
  1831                         if (!tags.isEmpty())
       
  1832                             curtag = tags.top();
       
  1833                         else
       
  1834                             curtag = initag;
       
  1835                     }
       
  1836 
       
  1837                     curtag.name = tagname;
       
  1838                     curtag.style = nstyle;
       
  1839                     curtag.name = tagname;
       
  1840                     curtag.style = nstyle;
       
  1841                     if (nstyle->whiteSpaceMode()  != Q3StyleSheetItem::WhiteSpaceModeUndefined)
       
  1842                         curtag.wsm = nstyle->whiteSpaceMode();
       
  1843 
       
  1844                     /* netscape compatibility: eat a newline and only a newline if a pre block starts */
       
  1845                     if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre &&
       
  1846                          nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock)
       
  1847                         eat(doc, length, pos, QLatin1Char('\n'));
       
  1848 
       
  1849                     /* ignore whitespace for inline elements if there
       
  1850                        was already one*/
       
  1851                     if (!textEditMode &&
       
  1852                          (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal
       
  1853                           || curtag.wsm == Q3StyleSheetItem::WhiteSpaceNoWrap)
       
  1854                          && (space || nstyle->displayMode() != Q3StyleSheetItem::DisplayInline))
       
  1855                         eatSpace(doc, length, pos);
       
  1856 
       
  1857                     curtag.format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
       
  1858                     if (nstyle->isAnchor()) {
       
  1859                         if (!anchorName.isEmpty())
       
  1860                             anchorName += QLatin1Char('#') + attr[QLatin1String("name")];
       
  1861                         else
       
  1862                             anchorName = attr[QLatin1String("name")];
       
  1863                         curtag.anchorHref = attr[QLatin1String("href")];
       
  1864                     }
       
  1865 
       
  1866                     if (nstyle->alignment() != Q3StyleSheetItem::Undefined)
       
  1867                         curtag.alignment = nstyle->alignment();
       
  1868 
       
  1869                     if (nstyle->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
       
  1870                         curtag.liststyle = nstyle->listStyle();
       
  1871 
       
  1872                     if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock
       
  1873                          || nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
       
  1874 
       
  1875                         if (nstyle->name() == QLatin1String("ol") ||
       
  1876                             nstyle->name() == QLatin1String("ul") ||
       
  1877                             nstyle->name() == QLatin1String("li")) {
       
  1878                             QString type = attr[QLatin1String("type")];
       
  1879                             if (!type.isEmpty()) {
       
  1880                                 if (type == QLatin1String("1")) {
       
  1881                                     curtag.liststyle = Q3StyleSheetItem::ListDecimal;
       
  1882                                 } else if (type == QLatin1String("a")) {
       
  1883                                     curtag.liststyle = Q3StyleSheetItem::ListLowerAlpha;
       
  1884                                 } else if (type == QLatin1String("A")) {
       
  1885                                     curtag.liststyle = Q3StyleSheetItem::ListUpperAlpha;
       
  1886                                 } else {
       
  1887                                     type = type.toLower();
       
  1888                                     if (type == QLatin1String("square"))
       
  1889                                         curtag.liststyle = Q3StyleSheetItem::ListSquare;
       
  1890                                     else if (type == QLatin1String("disc"))
       
  1891                                         curtag.liststyle = Q3StyleSheetItem::ListDisc;
       
  1892                                     else if (type == QLatin1String("circle"))
       
  1893                                         curtag.liststyle = Q3StyleSheetItem::ListCircle;
       
  1894                                 }
       
  1895                             }
       
  1896                         }
       
  1897 
       
  1898 
       
  1899                         /* Internally we treat ordered and bullet
       
  1900                           lists the same for margin calculations. In
       
  1901                           order to have fast pointer compares in the
       
  1902                           xMargin() functions we restrict ourselves to
       
  1903                           <ol>. Once we calculate the margins in the
       
  1904                           parser rathern than later, the unelegance of
       
  1905                           this approach goes awy
       
  1906                          */
       
  1907                         if (nstyle->name() == QLatin1String("ul"))
       
  1908                             curtag.style = sheet_->item(QLatin1String("ol"));
       
  1909 
       
  1910                         it = attr.find(QLatin1String("align"));
       
  1911                         if (it != end) {
       
  1912                             QString align = (*it).toLower();
       
  1913                             if (align == QLatin1String("center"))
       
  1914                                 curtag.alignment = Qt::AlignCenter;
       
  1915                             else if (align == QLatin1String("right"))
       
  1916                                 curtag.alignment = Qt::AlignRight;
       
  1917                             else if (align == QLatin1String("justify"))
       
  1918                                 curtag.alignment = Qt::AlignJustify;
       
  1919                         }
       
  1920                         it = attr.find(QLatin1String("dir"));
       
  1921                         if (it != end) {
       
  1922                             QString dir = (*it).toLower();
       
  1923                             if (dir == QLatin1String("rtl"))
       
  1924                                 curtag.direction = QChar::DirR;
       
  1925                             else if (dir == QLatin1String("ltr"))
       
  1926                                 curtag.direction = QChar::DirL;
       
  1927                         }
       
  1928 
       
  1929                         NEWPAR;
       
  1930 
       
  1931                         if (curtag.style && curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
       
  1932                             it = attr.find(QLatin1String("value"));
       
  1933                             if (it != end)
       
  1934                                 curpar->setListValue((*it).toInt());
       
  1935                         }
       
  1936 
       
  1937                         it = attr.find(QLatin1String("style"));
       
  1938                         if (it != end) {
       
  1939                             QString a = *it;
       
  1940                             bool ok = true;
       
  1941                             int count = a.count(QLatin1Char(';'))+1;
       
  1942                             for (int s = 0; ok && s < count; s++) {
       
  1943                                 QString style = a.section(QLatin1Char(';'), s, s);
       
  1944                                 if (style.startsWith(QLatin1String("margin-top:")) && style.endsWith(QLatin1String("px")))
       
  1945                                     curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok);
       
  1946                                 else if (style.startsWith(QLatin1String("margin-bottom:")) && style.endsWith(QLatin1String("px")))
       
  1947                                     curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok);
       
  1948                                 else if (style.startsWith(QLatin1String("margin-left:")) && style.endsWith(QLatin1String("px")))
       
  1949                                     curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok);
       
  1950                                 else if (style.startsWith(QLatin1String("margin-right:")) && style.endsWith(QLatin1String("px")))
       
  1951                                     curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok);
       
  1952                                 else if (style.startsWith(QLatin1String("text-indent:")) && style.endsWith(QLatin1String("px")))
       
  1953                                     curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok);
       
  1954                             }
       
  1955                             if (!ok) // be pressmistic
       
  1956                                 curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0;
       
  1957                         }
       
  1958                     } else if (nstyle->name() == QLatin1String("html")) {
       
  1959                         it = attr.find(QLatin1String("dir"));
       
  1960                         if (it != end) {
       
  1961                             QString dir = (*it).toLower();
       
  1962                             if (dir == QLatin1String("rtl"))
       
  1963                                 curtag.direction = QChar::DirR;
       
  1964                             else if (dir == QLatin1String("ltr"))
       
  1965                                 curtag.direction = QChar::DirL;
       
  1966                         }
       
  1967                     }
       
  1968                 }
       
  1969             } else {
       
  1970                 QString tagname = parseCloseTag(doc, length, pos);
       
  1971                 if (tagname.isEmpty())
       
  1972                     continue; // nothing we could do with this, probably parse error
       
  1973                 if (!sheet_->item(tagname)) // ignore unknown tags
       
  1974                     continue;
       
  1975                 if (tagname == QLatin1String("li"))
       
  1976                     continue;
       
  1977 
       
  1978                 // we close a block item. Since the text may continue, we need to have a new paragraph
       
  1979                 bool needNewPar = curtag.style->displayMode() == Q3StyleSheetItem::DisplayBlock
       
  1980                                  || curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem;
       
  1981 
       
  1982 
       
  1983                 // html slopiness: handle unbalanched tag closing
       
  1984                 while (curtag.name != tagname) {
       
  1985                     QString msg;
       
  1986                     msg.sprintf("QText Warning: Document not valid ('%s' not closed before '%s' #%d)",
       
  1987                                  curtag.name.ascii(), tagname.ascii(), pos);
       
  1988                     sheet_->error(msg);
       
  1989                     if (tags.isEmpty())
       
  1990                         break;
       
  1991                     curtag = tags.pop();
       
  1992                 }
       
  1993 
       
  1994 
       
  1995                 // close the tag
       
  1996                 if (!tags.isEmpty())
       
  1997                     curtag = tags.pop();
       
  1998                 else
       
  1999                     curtag = initag;
       
  2000 
       
  2001                 if (needNewPar) {
       
  2002                     if (textEditMode && (tagname == QLatin1String("p") || tagname == QLatin1String("div"))) // preserve empty paragraphs
       
  2003                         hasNewPar = false;
       
  2004                     NEWPAR;
       
  2005                 }
       
  2006             }
       
  2007         } else {
       
  2008             // normal contents
       
  2009             QString s;
       
  2010             QChar c;
       
  2011             while (pos < length && !hasPrefix(doc, length, pos, QLatin1Char('<'))){
       
  2012                 if (textEditMode) {
       
  2013                     // text edit mode: we handle all white space but ignore newlines
       
  2014                     c = parseChar(doc, length, pos, Q3StyleSheetItem::WhiteSpacePre);
       
  2015                     if (c == QChar::LineSeparator)
       
  2016                         break;
       
  2017                 } else {
       
  2018                     int l = pos;
       
  2019                     c = parseChar(doc, length, pos, curtag.wsm);
       
  2020 
       
  2021                     // in white space pre mode: treat any space as non breakable
       
  2022                     // and expand tabs to eight character wide columns.
       
  2023                     if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre) {
       
  2024                         if  (c == QLatin1Char('\t')) {
       
  2025                             c = QLatin1Char(' ');
       
  2026                             while((++tabExpansionColumn)%8)
       
  2027                                 s += c;
       
  2028                         }
       
  2029                         if (c == QChar::LineSeparator)
       
  2030                             tabExpansionColumn = 0;
       
  2031                         else
       
  2032                             tabExpansionColumn++;
       
  2033 
       
  2034                     }
       
  2035                     if (c == QLatin1Char(' ') || c == QChar::LineSeparator) {
       
  2036                         /* avoid overlong paragraphs by forcing a new
       
  2037                                paragraph after 4096 characters. This case can
       
  2038                                occur when loading undiscovered plain text
       
  2039                                documents in rich text mode. Instead of hanging
       
  2040                                forever, we do the trick.
       
  2041                             */
       
  2042                         if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && s.length() > 4096) do {
       
  2043                             if (doc[l] == QLatin1Char('\n')) {
       
  2044                                 hasNewPar = false; // for a new paragraph ...
       
  2045                                 NEWPAR;
       
  2046                                 hasNewPar = false; // ... and make it non-reusable
       
  2047                                 c = QLatin1Char('\n');  // make sure we break below
       
  2048                                 break;
       
  2049                             }
       
  2050                         } while (++l < pos);
       
  2051                     }
       
  2052                 }
       
  2053 
       
  2054                 if (c == QLatin1Char('\n'))
       
  2055                     break;  // break on  newlines, pre delievers a QChar::LineSeparator
       
  2056 
       
  2057                 bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode;
       
  2058 
       
  2059                 if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && c_isSpace && space)
       
  2060                     continue;
       
  2061                 if (c == QLatin1Char('\r'))
       
  2062                     continue;
       
  2063                 space = c_isSpace;
       
  2064                 s += c;
       
  2065             }
       
  2066             if (!s.isEmpty() && curtag.style->displayMode() != Q3StyleSheetItem::DisplayNone) {
       
  2067                 hasNewPar = false;
       
  2068                 int index = qMax(curpar->length(),1) - 1;
       
  2069                 curpar->append(s);
       
  2070                 if (curtag.wsm != Q3StyleSheetItem::WhiteSpaceNormal) {
       
  2071                     Q3TextString *str = curpar->string();
       
  2072                     for (int i = index; i < index + s.length(); ++i)
       
  2073                         str->at(i).nobreak = true;
       
  2074                 }
       
  2075 
       
  2076                 Q3TextFormat* f = formatCollection()->format(&curtag.format);
       
  2077                 curpar->setFormat(index, s.length(), f, false); // do not use collection because we have done that already
       
  2078                 f->ref += s.length() -1; // that what friends are for...
       
  2079                 if (!curtag.anchorHref.isEmpty()) {
       
  2080                     for (int i = 0; i < int(s.length()); i++)
       
  2081                         curpar->at(index + i)->setAnchor(QString(), curtag.anchorHref);
       
  2082                 }
       
  2083                 if (!anchorName.isEmpty() ) {
       
  2084                     for (int i = 0; i < int(s.length()); i++)
       
  2085                         curpar->at(index + i)->setAnchor(anchorName, curpar->at(index + i)->anchorHref());
       
  2086                     anchorName.clear();
       
  2087                 }
       
  2088             }
       
  2089         }
       
  2090     }
       
  2091 
       
  2092     if (hasNewPar && curpar != fParag && !cursor && stylesPar != curpar) {
       
  2093         // cleanup unused last paragraphs
       
  2094         curpar = curpar->p;
       
  2095         delete curpar->n;
       
  2096     }
       
  2097 
       
  2098     if (!anchorName.isEmpty() ) {
       
  2099         curpar->at(curpar->length() - 1)->setAnchor(anchorName, curpar->at(curpar->length() - 1)->anchorHref());
       
  2100         anchorName.clear();
       
  2101     }
       
  2102 
       
  2103     setRichTextMarginsInternal(styles, stylesPar);
       
  2104 
       
  2105     if (cursor) {
       
  2106         cursor->gotoPreviousLetter();
       
  2107         cursor->remove();
       
  2108     }
       
  2109     while (!styles.isEmpty())
       
  2110         delete styles.takeFirst();
       
  2111     delete vec;
       
  2112 }
       
  2113 
       
  2114 void Q3TextDocument::setRichTextMarginsInternal(QList< QVector<Q3StyleSheetItem *> *>& styles, Q3TextParagraph* stylesPar)
       
  2115 {
       
  2116     // margin and line spacing calculation
       
  2117     // qDebug("setRichTextMarginsInternal: styles.size() = %d", styles.size());
       
  2118     QVector<Q3StyleSheetItem *>* prevStyle = 0;
       
  2119     int stylesIndex = 0;
       
  2120     QVector<Q3StyleSheetItem *>* curStyle = styles.size() ? styles.first() : 0;
       
  2121     QVector<Q3StyleSheetItem *>* nextStyle =
       
  2122         (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
       
  2123     while (stylesPar) {
       
  2124         if (!curStyle) {
       
  2125             stylesPar = stylesPar->next();
       
  2126             prevStyle = curStyle;
       
  2127             curStyle = nextStyle;
       
  2128             nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
       
  2129             continue;
       
  2130         }
       
  2131 
       
  2132         int i, mar;
       
  2133         Q3StyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0;
       
  2134         if (mainStyle && mainStyle->displayMode() == Q3StyleSheetItem::DisplayListItem)
       
  2135             stylesPar->setListItem(true);
       
  2136         int numLists = 0;
       
  2137         for (i = 0; i < (int)curStyle->size(); ++i) {
       
  2138             if ((*curStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
       
  2139                  && (*curStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
       
  2140                 numLists++;
       
  2141         }
       
  2142         stylesPar->ldepth = numLists;
       
  2143         if (stylesPar->next() && nextStyle) {
       
  2144             // also set the depth of the next paragraph, required for the margin calculation
       
  2145             numLists = 0;
       
  2146             for (i = 0; i < (int)nextStyle->size(); ++i) {
       
  2147                 if ((*nextStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
       
  2148                      && (*nextStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
       
  2149                     numLists++;
       
  2150             }
       
  2151             stylesPar->next()->ldepth = numLists;
       
  2152         }
       
  2153 
       
  2154         // do the top margin
       
  2155         Q3StyleSheetItem* item = mainStyle;
       
  2156         int m;
       
  2157         if (stylesPar->utm > 0) {
       
  2158             m = stylesPar->utm-1;
       
  2159             stylesPar->utm = 0;
       
  2160         } else {
       
  2161             m = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
       
  2162             if (stylesPar->ldepth) {
       
  2163                 if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
       
  2164                     m /= stylesPar->ldepth * stylesPar->ldepth;
       
  2165                 else
       
  2166                     m = 0;
       
  2167             }
       
  2168         }
       
  2169         for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
       
  2170             item = (*curStyle)[i];
       
  2171             if (prevStyle && i < (int) prevStyle->size() &&
       
  2172                  ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
       
  2173                     (*prevStyle)[i] == item))
       
  2174                 break;
       
  2175             // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
       
  2176             if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined  &&
       
  2177                  (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
       
  2178                 continue;
       
  2179             mar = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
       
  2180             m = qMax(m, mar);
       
  2181         }
       
  2182         stylesPar->utm = m - stylesPar->topMargin();
       
  2183 
       
  2184         // do the bottom margin
       
  2185         item = mainStyle;
       
  2186         if (stylesPar->ubm > 0) {
       
  2187             m = stylesPar->ubm-1;
       
  2188             stylesPar->ubm = 0;
       
  2189         } else {
       
  2190             m = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
       
  2191             if (stylesPar->ldepth) {
       
  2192                 if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
       
  2193                     m /= stylesPar->ldepth * stylesPar->ldepth;
       
  2194                 else
       
  2195                     m = 0;
       
  2196             }
       
  2197         }
       
  2198         for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
       
  2199             item = (*curStyle)[i];
       
  2200             if (nextStyle && i < (int) nextStyle->size() &&
       
  2201                  ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
       
  2202                     (*nextStyle)[i] == item))
       
  2203                 break;
       
  2204             // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
       
  2205             if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined  &&
       
  2206                  (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
       
  2207                 continue;
       
  2208             mar = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
       
  2209             m = qMax(m, mar);
       
  2210         }
       
  2211         stylesPar->ubm = m - stylesPar->bottomMargin();
       
  2212 
       
  2213         // do the left margin, simplyfied
       
  2214         item = mainStyle;
       
  2215         if (stylesPar->ulm > 0) {
       
  2216             m = stylesPar->ulm-1;
       
  2217             stylesPar->ulm = 0;
       
  2218         } else {
       
  2219             m = qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
       
  2220         }
       
  2221         for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
       
  2222             item = (*curStyle)[i];
       
  2223             m += qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
       
  2224         }
       
  2225         stylesPar->ulm = m - stylesPar->leftMargin();
       
  2226 
       
  2227         // do the right margin, simplyfied
       
  2228         item = mainStyle;
       
  2229         if (stylesPar->urm > 0) {
       
  2230             m = stylesPar->urm-1;
       
  2231             stylesPar->urm = 0;
       
  2232         } else {
       
  2233             m = qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
       
  2234         }
       
  2235         for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
       
  2236             item = (*curStyle)[i];
       
  2237             m += qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
       
  2238         }
       
  2239         stylesPar->urm = m - stylesPar->rightMargin();
       
  2240 
       
  2241         // do the first line margin, which really should be called text-indent
       
  2242         item = mainStyle;
       
  2243         if (stylesPar->uflm > 0) {
       
  2244             m = stylesPar->uflm-1;
       
  2245             stylesPar->uflm = 0;
       
  2246         } else {
       
  2247             m = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
       
  2248         }
       
  2249         for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
       
  2250             item = (*curStyle)[i];
       
  2251             mar = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
       
  2252             m = qMax(m, mar);
       
  2253         }
       
  2254         stylesPar->uflm =m - stylesPar->firstLineMargin();
       
  2255 
       
  2256         // do the bogus line "spacing", which really is just an extra margin
       
  2257         item = mainStyle;
       
  2258         for (i = (int)curStyle->size() - 1 ; i >= 0; --i) {
       
  2259             item = (*curStyle)[i];
       
  2260             if (item->lineSpacing() != Q3StyleSheetItem::Undefined) {
       
  2261                 stylesPar->ulinespacing = item->lineSpacing();
       
  2262                 if (formatCollection() &&
       
  2263                      stylesPar->ulinespacing < formatCollection()->defaultFormat()->height())
       
  2264                     stylesPar->ulinespacing += formatCollection()->defaultFormat()->height();
       
  2265                 break;
       
  2266             }
       
  2267         }
       
  2268 
       
  2269         stylesPar = stylesPar->next();
       
  2270         prevStyle = curStyle;
       
  2271         curStyle = nextStyle;
       
  2272         nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
       
  2273     }
       
  2274 }
       
  2275 
       
  2276 void Q3TextDocument::setText(const QString &text, const QString &context)
       
  2277 {
       
  2278     focusIndicator.parag = 0;
       
  2279     selections.clear();
       
  2280     if ((txtFormat == Qt::AutoText && Q3StyleSheet::mightBeRichText(text))
       
  2281         || txtFormat == Qt::RichText)
       
  2282         setRichText(text, context);
       
  2283     else
       
  2284         setPlainText(text);
       
  2285 }
       
  2286 
       
  2287 QString Q3TextDocument::plainText() const
       
  2288 {
       
  2289     QString buffer;
       
  2290     QString s;
       
  2291     Q3TextParagraph *p = fParag;
       
  2292     while (p) {
       
  2293         if (!p->mightHaveCustomItems) {
       
  2294             const Q3TextString *ts = p->string(); // workaround VC++ and Borland
       
  2295             s = ts->toString(); // with false we don't fix spaces (nbsp)
       
  2296         } else {
       
  2297             for (int i = 0; i < p->length() - 1; ++i) {
       
  2298 #ifndef QT_NO_TEXTCUSTOMITEM
       
  2299                 if (p->at(i)->isCustom()) {
       
  2300                     if (p->at(i)->customItem()->isNested()) {
       
  2301                         s += QLatin1String("\n");
       
  2302                         Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
       
  2303                         QList<Q3TextTableCell *> cells = t->tableCells();
       
  2304                         for (int idx = 0; idx < cells.size(); ++idx) {
       
  2305                             Q3TextTableCell *c = cells.at(idx);
       
  2306                             s += c->richText()->plainText() + QLatin1String("\n");
       
  2307                         }
       
  2308                         s += QLatin1String("\n");
       
  2309                     }
       
  2310                 } else
       
  2311 #endif
       
  2312                 {
       
  2313                     s += p->at(i)->c;
       
  2314                 }
       
  2315             }
       
  2316         }
       
  2317         s.remove(s.length() - 1, 1);
       
  2318         if (p->next())
       
  2319             s += QLatin1String("\n");
       
  2320         buffer += s;
       
  2321         p = p->next();
       
  2322     }
       
  2323     return buffer;
       
  2324 }
       
  2325 
       
  2326 static QString align_to_string(int a)
       
  2327 {
       
  2328     if (a & Qt::AlignRight)
       
  2329         return QLatin1String(" align=\"right\"");
       
  2330     if (a & Qt::AlignHCenter)
       
  2331         return QLatin1String(" align=\"center\"");
       
  2332     if (a & Qt::AlignJustify)
       
  2333         return QLatin1String(" align=\"justify\"");
       
  2334     return QString();
       
  2335 }
       
  2336 
       
  2337 static QString direction_to_string(int dir)
       
  2338 {
       
  2339     if (dir != QChar::DirON)
       
  2340         return (dir == QChar::DirL? QLatin1String(" dir=\"ltr\"") : QLatin1String(" dir=\"rtl\""));
       
  2341     return QString();
       
  2342 }
       
  2343 
       
  2344 static QString list_value_to_string(int v)
       
  2345 {
       
  2346     if (v != -1)
       
  2347         return QLatin1String(" listvalue=\"") + QString::number(v) + QLatin1Char('"');
       
  2348     return QString();
       
  2349 }
       
  2350 
       
  2351 static QString list_style_to_string(int v)
       
  2352 {
       
  2353     switch(v) {
       
  2354     case Q3StyleSheetItem::ListDecimal: return QLatin1String("\"1\"");
       
  2355     case Q3StyleSheetItem::ListLowerAlpha: return QLatin1String("\"a\"");
       
  2356     case Q3StyleSheetItem::ListUpperAlpha: return QLatin1String("\"A\"");
       
  2357     case Q3StyleSheetItem::ListDisc: return QLatin1String("\"disc\"");
       
  2358     case Q3StyleSheetItem::ListSquare: return QLatin1String("\"square\"");
       
  2359     case Q3StyleSheetItem::ListCircle: return QLatin1String("\"circle\"");
       
  2360     default:
       
  2361         return QString();
       
  2362     }
       
  2363 }
       
  2364 
       
  2365 static inline bool list_is_ordered(int v)
       
  2366 {
       
  2367     return v == Q3StyleSheetItem::ListDecimal ||
       
  2368            v == Q3StyleSheetItem::ListLowerAlpha ||
       
  2369            v == Q3StyleSheetItem::ListUpperAlpha;
       
  2370 }
       
  2371 
       
  2372 
       
  2373 static QString margin_to_string(Q3StyleSheetItem* style, int t, int b, int l, int r, int fl)
       
  2374 {
       
  2375     QString s;
       
  2376     if (l > 0)
       
  2377         s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-left:") +
       
  2378              QString::number(l+qMax(0,style->margin(Q3StyleSheetItem::MarginLeft))) + QLatin1String("px");
       
  2379     if (r > 0)
       
  2380         s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-right:") +
       
  2381              QString::number(r+qMax(0,style->margin(Q3StyleSheetItem::MarginRight))) + QLatin1String("px");
       
  2382     if (t > 0)
       
  2383         s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-top:") +
       
  2384              QString::number(t+qMax(0,style->margin(Q3StyleSheetItem::MarginTop))) + QLatin1String("px");
       
  2385     if (b > 0)
       
  2386         s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-bottom:") +
       
  2387              QString::number(b+qMax(0,style->margin(Q3StyleSheetItem::MarginBottom))) + QLatin1String("px");
       
  2388     if (fl > 0)
       
  2389         s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("text-indent:") +
       
  2390              QString::number(fl+qMax(0,style->margin(Q3StyleSheetItem::MarginFirstLine))) + QLatin1String("px");
       
  2391     if (s.size())
       
  2392         return QLatin1String(" style=\"") + s + QLatin1String("\"");
       
  2393     return QString();
       
  2394 }
       
  2395 
       
  2396 QString Q3TextDocument::richText() const
       
  2397 {
       
  2398     QString s = QLatin1String("");
       
  2399     if (!par) {
       
  2400         s += QLatin1String("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:");
       
  2401         s += QString::number(formatCollection()->defaultFormat()->font().pointSize());
       
  2402         s += QLatin1String("pt;font-family:");
       
  2403         s += formatCollection()->defaultFormat()->font().family();
       
  2404         s += QLatin1String("\">");
       
  2405     }
       
  2406     Q3TextParagraph* p = fParag;
       
  2407 
       
  2408     Q3StyleSheetItem* item_p = styleSheet()->item(QLatin1String("p"));
       
  2409     Q3StyleSheetItem* item_div = styleSheet()->item(QLatin1String("div"));
       
  2410     Q3StyleSheetItem* item_ul = styleSheet()->item(QLatin1String("ul"));
       
  2411     Q3StyleSheetItem* item_ol = styleSheet()->item(QLatin1String("ol"));
       
  2412     Q3StyleSheetItem* item_li = styleSheet()->item(QLatin1String("li"));
       
  2413     if (!item_p || !item_div || !item_ul || !item_ol || !item_li) {
       
  2414         qWarning("QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)");
       
  2415         return QString();
       
  2416     }
       
  2417     int pastListDepth = 0;
       
  2418     int listDepth = 0;
       
  2419 #if 0
       
  2420     int futureListDepth = 0;
       
  2421 #endif
       
  2422     QVector<int> listStyles(10);
       
  2423 
       
  2424     while (p) {
       
  2425         listDepth = p->listDepth();
       
  2426         if (listDepth < pastListDepth)  {
       
  2427             for (int i = pastListDepth; i > listDepth; i--)
       
  2428                 s += list_is_ordered(listStyles[i]) ? QLatin1String("</ol>") : QLatin1String("</ul>");
       
  2429             s += QLatin1Char('\n');
       
  2430         } else if (listDepth > pastListDepth) {
       
  2431             s += QLatin1Char('\n');
       
  2432             listStyles.resize(qMax((int)listStyles.size(), listDepth+1));
       
  2433             QString list_type;
       
  2434             listStyles[listDepth] = p->listStyle();
       
  2435             if (!list_is_ordered(p->listStyle()) || item_ol->listStyle() != p->listStyle())
       
  2436                 list_type = QLatin1String(" type=") + list_style_to_string(p->listStyle());
       
  2437             for (int i = pastListDepth; i < listDepth; i++) {
       
  2438                 s += list_is_ordered(p->listStyle()) ? QLatin1String("<ol") : QLatin1String("<ul");
       
  2439                 s += list_type + QLatin1Char('>');
       
  2440             }
       
  2441         } else {
       
  2442             s += QLatin1Char('\n');
       
  2443         }
       
  2444 
       
  2445         QString ps = p->richText();
       
  2446 
       
  2447 #if 0
       
  2448           // for the bottom margin we need to know whether we are at the end of a list
       
  2449         futureListDepth = 0;
       
  2450         if (listDepth > 0 && p->next())
       
  2451             futureListDepth = p->next()->listDepth();
       
  2452 #endif
       
  2453 
       
  2454         if (richTextExportStart && richTextExportStart->paragraph() ==p &&
       
  2455              richTextExportStart->index() == 0)
       
  2456             s += QLatin1String("<!--StartFragment-->");
       
  2457 
       
  2458         if (p->isListItem()) {
       
  2459             s += QLatin1String("<li");
       
  2460             if (p->listStyle() != listStyles[listDepth])
       
  2461                 s += QLatin1String(" type=") + list_style_to_string(p->listStyle());
       
  2462             s += align_to_string(p->alignment());
       
  2463             s += margin_to_string(item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
       
  2464             s += list_value_to_string(p->listValue());
       
  2465             s += direction_to_string(p->direction());
       
  2466             s += QLatin1Char('>');
       
  2467             s += ps;
       
  2468             s += QLatin1String("</li>");
       
  2469         } else if (p->listDepth()) {
       
  2470             s += QLatin1String("<div");
       
  2471             s += align_to_string(p->alignment());
       
  2472             s += margin_to_string(item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
       
  2473             s += direction_to_string(p->direction());
       
  2474             s += QLatin1Char('>');
       
  2475             s += ps;
       
  2476             s += QLatin1String("</div>");
       
  2477         } else {
       
  2478             // normal paragraph item
       
  2479             s += QLatin1String("<p");
       
  2480             s += align_to_string(p->alignment());
       
  2481             s += margin_to_string(item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
       
  2482             s += direction_to_string(p->direction());
       
  2483             s += QLatin1Char('>');
       
  2484             s += ps;
       
  2485             s += QLatin1String("</p>");
       
  2486         }
       
  2487         pastListDepth = listDepth;
       
  2488         p = p->next();
       
  2489     }
       
  2490     while (listDepth > 0) {
       
  2491         s += list_is_ordered(listStyles[listDepth]) ? QLatin1String("</ol>") : QLatin1String("</ul>");
       
  2492         listDepth--;
       
  2493     }
       
  2494 
       
  2495     if (!par)
       
  2496         s += QLatin1String("\n</body></html>\n");
       
  2497 
       
  2498     return s;
       
  2499 }
       
  2500 
       
  2501 QString Q3TextDocument::text() const
       
  2502 {
       
  2503     if ((txtFormat == Qt::AutoText && preferRichText) || txtFormat == Qt::RichText)
       
  2504         return richText();
       
  2505     return plainText();
       
  2506 }
       
  2507 
       
  2508 QString Q3TextDocument::text(int parag) const
       
  2509 {
       
  2510     Q3TextParagraph *p = paragAt(parag);
       
  2511     if (!p)
       
  2512         return QString();
       
  2513 
       
  2514     if ((txtFormat == Qt::AutoText && preferRichText) || txtFormat == Qt::RichText)
       
  2515         return p->richText();
       
  2516     else
       
  2517         return p->string()->toString();
       
  2518 }
       
  2519 
       
  2520 void Q3TextDocument::invalidate()
       
  2521 {
       
  2522     Q3TextParagraph *s = fParag;
       
  2523     while (s) {
       
  2524         s->invalidate(0);
       
  2525         s = s->next();
       
  2526     }
       
  2527 }
       
  2528 
       
  2529 void Q3TextDocument::selectionStart(int id, int &paragId, int &index)
       
  2530 {
       
  2531     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  2532     if (it == selections.end())
       
  2533         return;
       
  2534     Q3TextDocumentSelection &sel = *it;
       
  2535     paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
       
  2536     index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
       
  2537 }
       
  2538 
       
  2539 Q3TextCursor Q3TextDocument::selectionStartCursor(int id)
       
  2540 {
       
  2541     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  2542     if (it == selections.end())
       
  2543         return Q3TextCursor(this);
       
  2544     Q3TextDocumentSelection &sel = *it;
       
  2545     if (sel.swapped)
       
  2546         return sel.endCursor;
       
  2547     return sel.startCursor;
       
  2548 }
       
  2549 
       
  2550 Q3TextCursor Q3TextDocument::selectionEndCursor(int id)
       
  2551 {
       
  2552     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  2553     if (it == selections.end())
       
  2554         return Q3TextCursor(this);
       
  2555     Q3TextDocumentSelection &sel = *it;
       
  2556     if (!sel.swapped)
       
  2557         return sel.endCursor;
       
  2558     return sel.startCursor;
       
  2559 }
       
  2560 
       
  2561 void Q3TextDocument::selectionEnd(int id, int &paragId, int &index)
       
  2562 {
       
  2563     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  2564     if (it == selections.end())
       
  2565         return;
       
  2566     Q3TextDocumentSelection &sel = *it;
       
  2567     paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
       
  2568     index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
       
  2569 }
       
  2570 
       
  2571 void Q3TextDocument::addSelection(int id)
       
  2572 {
       
  2573     nSelections = qMax(nSelections, id + 1);
       
  2574 }
       
  2575 
       
  2576 static void setSelectionEndHelper(int id, Q3TextDocumentSelection &sel, Q3TextCursor &start, Q3TextCursor &end)
       
  2577 {
       
  2578     Q3TextCursor c1 = start;
       
  2579     Q3TextCursor c2 = end;
       
  2580     if (sel.swapped) {
       
  2581         c1 = end;
       
  2582         c2 = start;
       
  2583     }
       
  2584 
       
  2585     c1.paragraph()->removeSelection(id);
       
  2586     c2.paragraph()->removeSelection(id);
       
  2587     if (c1.paragraph() != c2.paragraph()) {
       
  2588         c1.paragraph()->setSelection(id, c1.index(), c1.paragraph()->length() - 1);
       
  2589         c2.paragraph()->setSelection(id, 0, c2.index());
       
  2590     } else {
       
  2591         c1.paragraph()->setSelection(id, qMin(c1.index(), c2.index()), qMax(c1.index(), c2.index()));
       
  2592     }
       
  2593 
       
  2594     sel.startCursor = start;
       
  2595     sel.endCursor = end;
       
  2596     if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
       
  2597         sel.swapped = sel.startCursor.index() > sel.endCursor.index();
       
  2598 }
       
  2599 
       
  2600 bool Q3TextDocument::setSelectionEnd(int id, const Q3TextCursor &cursor)
       
  2601 {
       
  2602     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  2603     if (it == selections.end())
       
  2604         return false;
       
  2605     Q3TextDocumentSelection &sel = *it;
       
  2606 
       
  2607     Q3TextCursor start = sel.startCursor;
       
  2608     Q3TextCursor end = cursor;
       
  2609 
       
  2610     if (start == end) {
       
  2611         removeSelection(id);
       
  2612         setSelectionStart(id, cursor);
       
  2613         return true;
       
  2614     }
       
  2615 
       
  2616     if (sel.endCursor.paragraph() == end.paragraph()) {
       
  2617         setSelectionEndHelper(id, sel, start, end);
       
  2618         return true;
       
  2619     }
       
  2620 
       
  2621     bool inSelection = false;
       
  2622     Q3TextCursor c(this);
       
  2623     Q3TextCursor tmp = sel.startCursor;
       
  2624     if (sel.swapped)
       
  2625         tmp = sel.endCursor;
       
  2626     tmp.restoreState();
       
  2627     Q3TextCursor tmp2 = cursor;
       
  2628     tmp2.restoreState();
       
  2629     c.setParagraph(tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph());
       
  2630     bool hadStart = false;
       
  2631     bool hadEnd = false;
       
  2632     bool hadStartParag = false;
       
  2633     bool hadEndParag = false;
       
  2634     bool hadOldStart = false;
       
  2635     bool hadOldEnd = false;
       
  2636     bool leftSelection = false;
       
  2637     sel.swapped = false;
       
  2638     for (;;) {
       
  2639         if (c == start)
       
  2640             hadStart = true;
       
  2641         if (c == end)
       
  2642             hadEnd = true;
       
  2643         if (c.paragraph() == start.paragraph())
       
  2644             hadStartParag = true;
       
  2645         if (c.paragraph() == end.paragraph())
       
  2646             hadEndParag = true;
       
  2647         if (c == sel.startCursor)
       
  2648             hadOldStart = true;
       
  2649         if (c == sel.endCursor)
       
  2650             hadOldEnd = true;
       
  2651 
       
  2652         if (!sel.swapped &&
       
  2653              ((hadEnd && !hadStart)
       
  2654               || (hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index())))
       
  2655             sel.swapped = true;
       
  2656 
       
  2657         if ((c == end && hadStartParag) || (c == start && hadEndParag)) {
       
  2658             Q3TextCursor tmp = c;
       
  2659             tmp.restoreState();
       
  2660             if (tmp.paragraph() != c.paragraph()) {
       
  2661                 int sstart = tmp.paragraph()->selectionStart(id);
       
  2662                 tmp.paragraph()->removeSelection(id);
       
  2663                 tmp.paragraph()->setSelection(id, sstart, tmp.index());
       
  2664             }
       
  2665         }
       
  2666 
       
  2667         if (inSelection &&
       
  2668              ((c == end && hadStart) || (c == start && hadEnd)))
       
  2669              leftSelection = true;
       
  2670         else if (!leftSelection && !inSelection && (hadStart || hadEnd))
       
  2671             inSelection = true;
       
  2672 
       
  2673         bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection(id) && c.atParagEnd();
       
  2674         c.paragraph()->removeSelection(id);
       
  2675         if (inSelection) {
       
  2676             if (c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph()) {
       
  2677                 c.paragraph()->setSelection(id, qMin(start.index(), end.index()), qMax(start.index(), end.index()));
       
  2678             } else if (c.paragraph() == start.paragraph() && !hadEndParag) {
       
  2679                 c.paragraph()->setSelection(id, start.index(), c.paragraph()->length() - 1);
       
  2680             } else if (c.paragraph() == end.paragraph() && !hadStartParag) {
       
  2681                 c.paragraph()->setSelection(id, end.index(), c.paragraph()->length() - 1);
       
  2682             } else if (c.paragraph() == end.paragraph() && hadEndParag) {
       
  2683                 c.paragraph()->setSelection(id, 0, end.index());
       
  2684             } else if (c.paragraph() == start.paragraph() && hadStartParag) {
       
  2685                 c.paragraph()->setSelection(id, 0, start.index());
       
  2686             } else {
       
  2687                 c.paragraph()->setSelection(id, 0, c.paragraph()->length() - 1);
       
  2688             }
       
  2689         }
       
  2690 
       
  2691         if (leftSelection)
       
  2692             inSelection = false;
       
  2693 
       
  2694         if (noSelectionAnymore)
       
  2695             break;
       
  2696         // *ugle*hack optimization
       
  2697         Q3TextParagraph *p = c.paragraph();
       
  2698         if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph()) {
       
  2699             c.gotoNextLetter();
       
  2700             if (p == lastParagraph() && c.atParagEnd())
       
  2701                 break;
       
  2702         } else {
       
  2703             if (p->document()->parent())
       
  2704                 do {
       
  2705                     c.gotoNextLetter();
       
  2706                 } while (c.paragraph() == p);
       
  2707             else
       
  2708                 c.setParagraph(p->next());
       
  2709         }
       
  2710     }
       
  2711 
       
  2712     if (!sel.swapped)
       
  2713         sel.startCursor.paragraph()->setSelection(id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1);
       
  2714 
       
  2715     sel.startCursor = start;
       
  2716     sel.endCursor = end;
       
  2717     if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
       
  2718         sel.swapped = sel.startCursor.index() > sel.endCursor.index();
       
  2719 
       
  2720     setSelectionEndHelper(id, sel, start, end);
       
  2721 
       
  2722     return true;
       
  2723 }
       
  2724 
       
  2725 void Q3TextDocument::selectAll(int id)
       
  2726 {
       
  2727     removeSelection(id);
       
  2728 
       
  2729     Q3TextDocumentSelection sel;
       
  2730     sel.swapped = false;
       
  2731     Q3TextCursor c(this);
       
  2732 
       
  2733     c.setParagraph(fParag);
       
  2734     c.setIndex(0);
       
  2735     sel.startCursor = c;
       
  2736 
       
  2737     c.setParagraph(lParag);
       
  2738     c.setIndex(lParag->length() - 1);
       
  2739     sel.endCursor = c;
       
  2740 
       
  2741     selections.insert(id, sel);
       
  2742 
       
  2743     Q3TextParagraph *p = fParag;
       
  2744     while (p) {
       
  2745         p->setSelection(id, 0, p->length() - 1);
       
  2746         p = p->next();
       
  2747     }
       
  2748 
       
  2749     for (int idx = 0; idx < childList.size(); ++idx) {
       
  2750         Q3TextDocument *dc = childList.at(idx);
       
  2751         dc->selectAll(id);
       
  2752     }
       
  2753 }
       
  2754 
       
  2755 bool Q3TextDocument::removeSelection(int id)
       
  2756 {
       
  2757     if (!selections.contains(id))
       
  2758         return false;
       
  2759 
       
  2760     Q3TextDocumentSelection &sel = selections[id];
       
  2761 
       
  2762     Q3TextCursor start = sel.swapped ? sel.endCursor : sel.startCursor;
       
  2763     Q3TextCursor end = sel.swapped ? sel.startCursor : sel.endCursor;
       
  2764     Q3TextParagraph* p = 0;
       
  2765     while (start != end) {
       
  2766         if (p != start.paragraph()) {
       
  2767             p = start.paragraph();
       
  2768             p->removeSelection(id);
       
  2769             //### avoid endless loop by all means necessary, did somebody mention refactoring?
       
  2770             if (!parent() && p == lParag)
       
  2771                 break;
       
  2772         }
       
  2773         start.gotoNextLetter();
       
  2774     }
       
  2775     p = start.paragraph();
       
  2776     p->removeSelection(id);
       
  2777     selections.remove(id);
       
  2778     return true;
       
  2779 }
       
  2780 
       
  2781 QString Q3TextDocument::selectedText(int id, bool asRichText) const
       
  2782 {
       
  2783     QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.find(id);
       
  2784     if (it == selections.end())
       
  2785         return QString();
       
  2786 
       
  2787     Q3TextDocumentSelection sel = *it;
       
  2788 
       
  2789 
       
  2790     Q3TextCursor c1 = sel.startCursor;
       
  2791     Q3TextCursor c2 = sel.endCursor;
       
  2792     if (sel.swapped) {
       
  2793         c2 = sel.startCursor;
       
  2794         c1 = sel.endCursor;
       
  2795     }
       
  2796 
       
  2797     /* 3.0.3 improvement: Make it possible to get a reasonable
       
  2798        selection inside a table.  This approach is very conservative:
       
  2799        make sure that both cursors have the same depth level and point
       
  2800        to paragraphs within the same text document.
       
  2801 
       
  2802        Meaning if you select text in two table cells, you will get the
       
  2803        entire table. This is still far better than the 3.0.2, where
       
  2804        you always got the entire table.
       
  2805 
       
  2806        ### Fix this properly when refactoring
       
  2807      */
       
  2808     while (c2.nestedDepth() > c1.nestedDepth())
       
  2809         c2.oneUp();
       
  2810     while (c1.nestedDepth() > c2.nestedDepth())
       
  2811         c1.oneUp();
       
  2812     while (c1.nestedDepth() && c2.nestedDepth() &&
       
  2813             c1.paragraph()->document() != c2.paragraph()->document()) {
       
  2814         c1.oneUp();
       
  2815         c2.oneUp();
       
  2816     }
       
  2817     // do not trust sel_swapped with tables. Fix this properly when refactoring as well
       
  2818     if (c1.paragraph()->paragId() > c2.paragraph()->paragId() ||
       
  2819          (c1.paragraph() == c2.paragraph() && c1.index() > c2.index())) {
       
  2820         Q3TextCursor tmp = c1;
       
  2821         c2 = c1;
       
  2822         c1 = tmp;
       
  2823     }
       
  2824 
       
  2825     // end selection 3.0.3 improvement
       
  2826 
       
  2827     if (asRichText && !parent()) {
       
  2828         richTextExportStart = &c1;
       
  2829         richTextExportEnd = &c2;
       
  2830 
       
  2831         QString sel = richText();
       
  2832         int from = sel.indexOf(QLatin1String("<!--StartFragment-->"));
       
  2833         if (from >= 0) {
       
  2834             from += 20;
       
  2835             // find the previous span and move it into the start fragment before we clip it
       
  2836             QString prevspan;
       
  2837             int pspan = sel.lastIndexOf(QLatin1String("<span"), from-21);
       
  2838             if (pspan > sel.lastIndexOf(QLatin1String("</span"), from-21)) {
       
  2839                 int spanend = sel.indexOf(QLatin1Char('>'), pspan);
       
  2840                 prevspan = sel.mid(pspan, spanend - pspan + 1);
       
  2841             }
       
  2842             int to = sel.lastIndexOf(QLatin1String("<!--EndFragment-->"));
       
  2843             if (from <= to)
       
  2844                 sel = QLatin1String("<!--StartFragment-->") + prevspan + sel.mid(from, to - from);
       
  2845         }
       
  2846         richTextExportStart = richTextExportEnd = 0;
       
  2847         return sel;
       
  2848     }
       
  2849 
       
  2850     QString s;
       
  2851     if (c1.paragraph() == c2.paragraph()) {
       
  2852         Q3TextParagraph *p = c1.paragraph();
       
  2853         int end = c2.index();
       
  2854         if (p->at(qMax(0, end - 1))->isCustom())
       
  2855             ++end;
       
  2856         if (!p->mightHaveCustomItems) {
       
  2857             s += p->string()->toString().mid(c1.index(), end - c1.index());
       
  2858         } else {
       
  2859             for (int i = c1.index(); i < end; ++i) {
       
  2860 #ifndef QT_NO_TEXTCUSTOMITEM
       
  2861                 if (p->at(i)->isCustom()) {
       
  2862                     if (p->at(i)->customItem()->isNested()) {
       
  2863                         s += QLatin1String("\n");
       
  2864                         Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
       
  2865                         QList<Q3TextTableCell *> cells = t->tableCells();
       
  2866                         for (int idx = 0; idx < cells.size(); ++idx) {
       
  2867                             Q3TextTableCell *c = cells.at(idx);
       
  2868                             s += c->richText()->plainText() + QLatin1String("\n");
       
  2869                         }
       
  2870                         s += QLatin1String("\n");
       
  2871                     }
       
  2872                 } else
       
  2873 #endif
       
  2874                 {
       
  2875                     s += p->at(i)->c;
       
  2876                 }
       
  2877             }
       
  2878         }
       
  2879     } else {
       
  2880         Q3TextParagraph *p = c1.paragraph();
       
  2881         int start = c1.index();
       
  2882         while (p) {
       
  2883             int end = p == c2.paragraph() ? c2.index() : p->length() - 1;
       
  2884             if (p == c2.paragraph() && p->at(qMax(0, end - 1))->isCustom())
       
  2885                 ++end;
       
  2886             if (!p->mightHaveCustomItems) {
       
  2887                 s += p->string()->toString().mid(start, end - start);
       
  2888                 if (p != c2.paragraph())
       
  2889                     s += QLatin1String("\n");
       
  2890             } else {
       
  2891                 for (int i = start; i < end; ++i) {
       
  2892 #ifndef QT_NO_TEXTCUSTOMITEM
       
  2893                     if (p->at(i)->isCustom()) {
       
  2894                         if (p->at(i)->customItem()->isNested()) {
       
  2895                             s += QLatin1String(QLatin1String("\n"));
       
  2896                             Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
       
  2897                             QList<Q3TextTableCell *> cells = t->tableCells();
       
  2898                             for (int idx = 0; idx < cells.size(); ++idx) {
       
  2899                                 Q3TextTableCell *c = cells.at(idx);
       
  2900                                 s += c->richText()->plainText() + QLatin1String("\n");
       
  2901                             }
       
  2902                             s += QLatin1String("\n");
       
  2903                         }
       
  2904                     } else
       
  2905 #endif
       
  2906                     {
       
  2907                         s += p->at(i)->c;
       
  2908                     }
       
  2909                 }
       
  2910             }
       
  2911             start = 0;
       
  2912             if (p == c2.paragraph())
       
  2913                 break;
       
  2914             p = p->next();
       
  2915         }
       
  2916     }
       
  2917     // ### workaround for plain text export until we get proper
       
  2918     // mime types: turn unicode line seperators into the more
       
  2919     // widely understood \n. Makes copy and pasting code snipplets
       
  2920     // from within Assistent possible
       
  2921     QChar* uc = (QChar*) s.unicode();
       
  2922     for (int ii = 0; ii < s.length(); ii++) {
       
  2923         if (uc[(int)ii] == QChar::LineSeparator)
       
  2924             uc[(int)ii] = QLatin1Char('\n');
       
  2925         else if ( uc[(int)ii] == QChar::Nbsp )
       
  2926             uc[(int)ii] = QLatin1Char(' ');
       
  2927     }
       
  2928     return s;
       
  2929 }
       
  2930 
       
  2931 void Q3TextDocument::setFormat(int id, Q3TextFormat *f, int flags)
       
  2932 {
       
  2933     QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.constFind(id);
       
  2934     if (it == selections.constEnd())
       
  2935         return;
       
  2936 
       
  2937     Q3TextDocumentSelection sel = *it;
       
  2938 
       
  2939     Q3TextCursor c1 = sel.startCursor;
       
  2940     Q3TextCursor c2 = sel.endCursor;
       
  2941     if (sel.swapped) {
       
  2942         c2 = sel.startCursor;
       
  2943         c1 = sel.endCursor;
       
  2944     }
       
  2945 
       
  2946     c2.restoreState();
       
  2947     c1.restoreState();
       
  2948 
       
  2949     if (c1.paragraph() == c2.paragraph()) {
       
  2950         c1.paragraph()->setFormat(c1.index(), c2.index() - c1.index(), f, true, flags);
       
  2951         return;
       
  2952     }
       
  2953 
       
  2954     c1.paragraph()->setFormat(c1.index(), c1.paragraph()->length() - c1.index(), f, true, flags);
       
  2955     Q3TextParagraph *p = c1.paragraph()->next();
       
  2956     while (p && p != c2.paragraph()) {
       
  2957         p->setFormat(0, p->length(), f, true, flags);
       
  2958         p = p->next();
       
  2959     }
       
  2960     c2.paragraph()->setFormat(0, c2.index(), f, true, flags);
       
  2961 }
       
  2962 
       
  2963 void Q3TextDocument::removeSelectedText(int id, Q3TextCursor *cursor)
       
  2964 {
       
  2965     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  2966     if (it == selections.end())
       
  2967         return;
       
  2968 
       
  2969     Q3TextDocumentSelection sel = *it;
       
  2970     Q3TextCursor c1 = sel.startCursor;
       
  2971     Q3TextCursor c2 = sel.endCursor;
       
  2972     if (sel.swapped) {
       
  2973         c2 = sel.startCursor;
       
  2974         c1 = sel.endCursor;
       
  2975     }
       
  2976 
       
  2977     // ### no support for editing tables yet
       
  2978     if (c1.nestedDepth() || c2.nestedDepth())
       
  2979         return;
       
  2980 
       
  2981     c2.restoreState();
       
  2982     c1.restoreState();
       
  2983 
       
  2984     *cursor = c1;
       
  2985     removeSelection(id);
       
  2986 
       
  2987     if (c1.paragraph() == c2.paragraph()) {
       
  2988         c1.paragraph()->remove(c1.index(), c2.index() - c1.index());
       
  2989         return;
       
  2990     }
       
  2991 
       
  2992     if (c1.paragraph() == fParag && c1.index() == 0 &&
       
  2993          c2.paragraph() == lParag && c2.index() == lParag->length() - 1)
       
  2994         cursor->setValid(false);
       
  2995 
       
  2996     bool didGoLeft = false;
       
  2997     if ( c1.index() == 0 && c1.paragraph() != fParag) {
       
  2998         cursor->gotoPreviousLetter();
       
  2999         didGoLeft = cursor->isValid();
       
  3000     }
       
  3001 
       
  3002     c1.paragraph()->remove(c1.index(), c1.paragraph()->length() - 1 - c1.index());
       
  3003     Q3TextParagraph *p = c1.paragraph()->next();
       
  3004     int dy = 0;
       
  3005     Q3TextParagraph *tmp;
       
  3006     while (p && p != c2.paragraph()) {
       
  3007         tmp = p->next();
       
  3008         dy -= p->rect().height();
       
  3009         delete p;
       
  3010         p = tmp;
       
  3011     }
       
  3012     c2.paragraph()->remove(0, c2.index());
       
  3013     while (p) {
       
  3014         p->move(dy);
       
  3015         p->invalidate(0);
       
  3016         p->setEndState(-1);
       
  3017         p = p->next();
       
  3018     }
       
  3019 
       
  3020 
       
  3021     c1.paragraph()->join(c2.paragraph());
       
  3022 
       
  3023     if (didGoLeft)
       
  3024         cursor->gotoNextLetter();
       
  3025 }
       
  3026 
       
  3027 void Q3TextDocument::indentSelection(int id)
       
  3028 {
       
  3029     QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
       
  3030     if (it == selections.end())
       
  3031         return;
       
  3032 
       
  3033     Q3TextDocumentSelection sel = *it;
       
  3034     Q3TextParagraph *startParag = sel.startCursor.paragraph();
       
  3035     Q3TextParagraph *endParag = sel.endCursor.paragraph();
       
  3036     if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) {
       
  3037         endParag = sel.startCursor.paragraph();
       
  3038         startParag = sel.endCursor.paragraph();
       
  3039     }
       
  3040 
       
  3041     Q3TextParagraph *p = startParag;
       
  3042     while (p && p != endParag) {
       
  3043         p->indent();
       
  3044         p = p->next();
       
  3045     }
       
  3046 }
       
  3047 
       
  3048 void Q3TextCommandHistory::clear()
       
  3049 {
       
  3050     while (!history.isEmpty())
       
  3051         delete history.takeFirst();
       
  3052     current = -1;
       
  3053 }
       
  3054 
       
  3055 void Q3TextDocument::addCommand(Q3TextCommand *cmd)
       
  3056 {
       
  3057     commandHistory->addCommand(cmd);
       
  3058 }
       
  3059 
       
  3060 Q3TextCursor *Q3TextDocument::undo(Q3TextCursor *c)
       
  3061 {
       
  3062     return commandHistory->undo(c);
       
  3063 }
       
  3064 
       
  3065 Q3TextCursor *Q3TextDocument::redo(Q3TextCursor *c)
       
  3066 {
       
  3067     return commandHistory->redo(c);
       
  3068 }
       
  3069 
       
  3070 bool Q3TextDocument::find(Q3TextCursor& cursor, const QString &expr, bool cs, bool wo, bool forward)
       
  3071 {
       
  3072     Qt::CaseSensitivity caseSensitive = cs ? Qt::CaseSensitive : Qt::CaseInsensitive;
       
  3073     removeSelection(Standard);
       
  3074     if (expr.isEmpty())
       
  3075         return false;
       
  3076     for (;;) {
       
  3077 	QString s = cursor.paragraph()->string()->toString();
       
  3078 	int start = cursor.index();
       
  3079 	for (;;) {
       
  3080 	    int res = forward
       
  3081 		? s.indexOf(expr, start, caseSensitive)
       
  3082 		: s.lastIndexOf(expr, start, caseSensitive);
       
  3083 	    int end = res + expr.length();
       
  3084 	    if (res == -1 || (!forward && start <= res))
       
  3085 		break;
       
  3086 	    if (!wo || ((res == 0 || !s[res-1].isLetterOrNumber())
       
  3087 		&& (end == (int)s.length() || !s[end].isLetterOrNumber()))) {
       
  3088 		removeSelection(Standard);
       
  3089 		cursor.setIndex(forward ? end : res);
       
  3090 		setSelectionStart(Standard, cursor);
       
  3091 		cursor.setIndex(forward ? res : end);
       
  3092 		setSelectionEnd(Standard, cursor);
       
  3093 		if (!forward)
       
  3094 		    cursor.setIndex(res);
       
  3095 		return true;
       
  3096 	    }
       
  3097 	    start = res + (forward ? 1 : -1);
       
  3098 	}
       
  3099         if (forward) {
       
  3100             if (cursor.paragraph() == lastParagraph() && cursor.atParagEnd())
       
  3101                  break;
       
  3102             cursor.gotoNextLetter();
       
  3103         } else {
       
  3104             if (cursor.paragraph() == firstParagraph() && cursor.atParagStart())
       
  3105                  break;
       
  3106             cursor.gotoPreviousLetter();
       
  3107         }
       
  3108     }
       
  3109     return false;
       
  3110 }
       
  3111 
       
  3112 void Q3TextDocument::setTextFormat(Qt::TextFormat f)
       
  3113 {
       
  3114     txtFormat = f;
       
  3115     if (fParag == lParag && fParag->length() <= 1)
       
  3116         fParag->rtext = (f == Qt::RichText);
       
  3117 }
       
  3118 
       
  3119 Qt::TextFormat Q3TextDocument::textFormat() const
       
  3120 {
       
  3121     return txtFormat;
       
  3122 }
       
  3123 
       
  3124 bool Q3TextDocument::inSelection(int selId, const QPoint &pos) const
       
  3125 {
       
  3126     QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.find(selId);
       
  3127     if (it == selections.end())
       
  3128         return false;
       
  3129 
       
  3130     Q3TextDocumentSelection sel = *it;
       
  3131     Q3TextParagraph *startParag = sel.startCursor.paragraph();
       
  3132     Q3TextParagraph *endParag = sel.endCursor.paragraph();
       
  3133     if (sel.startCursor.paragraph() == sel.endCursor.paragraph() &&
       
  3134          sel.startCursor.paragraph()->selectionStart(selId) == sel.endCursor.paragraph()->selectionEnd(selId))
       
  3135         return false;
       
  3136     if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) {
       
  3137         endParag = sel.startCursor.paragraph();
       
  3138         startParag = sel.endCursor.paragraph();
       
  3139     }
       
  3140 
       
  3141     Q3TextParagraph *p = startParag;
       
  3142     while (p) {
       
  3143         if (p->rect().contains(pos)) {
       
  3144             bool inSel = false;
       
  3145             int selStart = p->selectionStart(selId);
       
  3146             int selEnd = p->selectionEnd(selId);
       
  3147             int y = 0;
       
  3148             int h = 0;
       
  3149             for (int i = 0; i < p->length(); ++i) {
       
  3150                 if (i == selStart)
       
  3151                     inSel = true;
       
  3152                 if (i == selEnd)
       
  3153                     break;
       
  3154                 if (p->at(i)->lineStart) {
       
  3155                     y = (*p->lineStarts.find(i))->y;
       
  3156                     h = (*p->lineStarts.find(i))->h;
       
  3157                 }
       
  3158                 if (pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h) {
       
  3159                     if (inSel && pos.x() >= p->at(i)->x &&
       
  3160                          pos.x() <= p->at(i)->x + p->at(i)->format()->width(p->at(i)->c))
       
  3161                         return true;
       
  3162                 }
       
  3163             }
       
  3164         }
       
  3165         if (pos.y() < p->rect().y())
       
  3166             break;
       
  3167         if (p == endParag)
       
  3168             break;
       
  3169         p = p->next();
       
  3170     }
       
  3171 
       
  3172     return false;
       
  3173 }
       
  3174 
       
  3175 void Q3TextDocument::doLayout(QPainter *p, int w)
       
  3176 {
       
  3177     minw = wused = 0;
       
  3178     if (!is_printer(p))
       
  3179         p = 0;
       
  3180     withoutDoubleBuffer = (p != 0);
       
  3181     QPainter * oldPainter = Q3TextFormat::painter();
       
  3182     Q3TextFormat::setPainter(p);
       
  3183     tStopWidth = formatCollection()->defaultFormat()->width( QLatin1Char('x') ) * 8;
       
  3184     flow_->setWidth(w);
       
  3185     cw = w;
       
  3186     vw = w;
       
  3187     Q3TextParagraph *parag = fParag;
       
  3188     while (parag) {
       
  3189         parag->invalidate(0);
       
  3190         if (p)
       
  3191             parag->adjustToPainter(p);
       
  3192         parag->format();
       
  3193         parag = parag->next();
       
  3194     }
       
  3195     Q3TextFormat::setPainter(oldPainter);
       
  3196 }
       
  3197 
       
  3198 QPixmap *Q3TextDocument::bufferPixmap(const QSize &s)
       
  3199 {
       
  3200     if (!buf_pixmap)
       
  3201         buf_pixmap = new QPixmap(s.expandedTo(QSize(1,1)));
       
  3202     else if (buf_pixmap->size() != s)
       
  3203         buf_pixmap->resize(s.expandedTo(buf_pixmap->size()));
       
  3204     return buf_pixmap;
       
  3205 }
       
  3206 
       
  3207 void Q3TextDocument::draw(QPainter *p, const QRect &rect, const QPalette &pal,
       
  3208                           const QBrush *paper)
       
  3209 {
       
  3210     if (!firstParagraph())
       
  3211         return;
       
  3212 
       
  3213     if (paper) {
       
  3214         p->setBrushOrigin(-qIntCast(p->translationX()),
       
  3215                            -qIntCast(p->translationY()));
       
  3216 
       
  3217         p->fillRect(rect, *paper);
       
  3218     }
       
  3219 
       
  3220     QPainter * oldPainter = Q3TextFormat::painter();
       
  3221     Q3TextFormat::setPainter(p);
       
  3222 
       
  3223     if (formatCollection()->defaultFormat()->color() != pal.text().color())
       
  3224         setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color());
       
  3225 
       
  3226     Q3TextParagraph *parag = firstParagraph();
       
  3227     while (parag) {
       
  3228         if (!parag->isValid())
       
  3229             parag->format();
       
  3230         int y = parag->rect().y();
       
  3231         QRect pr(parag->rect());
       
  3232         pr.setX(0);
       
  3233         pr.setWidth(QWIDGETSIZE_MAX);
       
  3234         if (!rect.isNull() && !rect.intersects(pr)) {
       
  3235             parag = parag->next();
       
  3236             continue;
       
  3237         }
       
  3238         p->translate(0, y);
       
  3239         if (rect.isValid())
       
  3240             parag->paint(*p, pal, 0, false, rect.x(), rect.y(), rect.width(), rect.height());
       
  3241         else
       
  3242             parag->paint(*p, pal, 0, false);
       
  3243         p->translate(0, -y);
       
  3244         parag = parag->next();
       
  3245         if (!flow()->isEmpty())
       
  3246             flow()->drawFloatingItems(p, rect.x(), rect.y(), rect.width(), rect.height(), pal, false);
       
  3247     }
       
  3248     Q3TextFormat::setPainter(oldPainter);
       
  3249 }
       
  3250 
       
  3251 void Q3TextDocument::drawParagraph(QPainter *painter, Q3TextParagraph *parag, int cx, int cy,
       
  3252                                    int cw, int ch,
       
  3253                                    QPixmap *&/*doubleBuffer*/, const QPalette &pal,
       
  3254                                    bool drawCursor, Q3TextCursor *cursor, bool resetChanged)
       
  3255 {
       
  3256     if (resetChanged)
       
  3257         parag->setChanged(false);
       
  3258     QRect ir(parag->rect());
       
  3259 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3260     if (!parag->tableCell())
       
  3261 #endif
       
  3262         ir.setWidth(width());
       
  3263 
       
  3264     painter->translate(ir.x(), ir.y());
       
  3265 
       
  3266     if (!parag->document()->parent()) {
       
  3267         const QPoint oldOrigin = painter->brushOrigin();
       
  3268         painter->setBrushOrigin(-ir.topLeft());
       
  3269         painter->fillRect(QRect(0, 0, ir.width(), ir.height()), parag->backgroundBrush(pal));
       
  3270         painter->setBrushOrigin(oldOrigin);
       
  3271     }
       
  3272 
       
  3273     painter->translate(-(ir.x() - parag->rect().x()),
       
  3274                        -(ir.y() - parag->rect().y()));
       
  3275     parag->paint(*painter, pal, drawCursor ? cursor : 0, true, cx, cy, cw, ch);
       
  3276 
       
  3277     painter->translate(-ir.x(), -ir.y());
       
  3278 
       
  3279     parag->document()->nextDoubleBuffered = false;
       
  3280 }
       
  3281 
       
  3282 Q3TextParagraph *Q3TextDocument::draw(QPainter *p, int cx, int cy, int cw, int ch,
       
  3283                                      const QPalette &pal, bool onlyChanged, bool drawCursor,
       
  3284                                      Q3TextCursor *cursor, bool resetChanged)
       
  3285 {
       
  3286     if (withoutDoubleBuffer || (par && par->withoutDoubleBuffer)) {
       
  3287         withoutDoubleBuffer = true;
       
  3288         QRect r;
       
  3289         draw(p, r, pal);
       
  3290         return 0;
       
  3291     }
       
  3292     withoutDoubleBuffer = false;
       
  3293 
       
  3294     if (!firstParagraph())
       
  3295         return 0;
       
  3296 
       
  3297     QPainter * oldPainter = Q3TextFormat::painter();
       
  3298     Q3TextFormat::setPainter(p);
       
  3299     if (formatCollection()->defaultFormat()->color() != pal.text().color())
       
  3300         setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color());
       
  3301 
       
  3302     if (cx < 0 && cy < 0) {
       
  3303         cx = 0;
       
  3304         cy = 0;
       
  3305         cw = width();
       
  3306         ch = height();
       
  3307     }
       
  3308 
       
  3309     Q3TextParagraph *lastFormatted = 0;
       
  3310     Q3TextParagraph *parag = firstParagraph();
       
  3311 
       
  3312     QPixmap *doubleBuffer = 0;
       
  3313 
       
  3314     while (parag) {
       
  3315         lastFormatted = parag;
       
  3316         if (!parag->isValid())
       
  3317             parag->format();
       
  3318 
       
  3319         QRect pr = parag->rect();
       
  3320         pr.setWidth(parag->document()->width());
       
  3321         if (pr.y() > cy + ch)
       
  3322             goto floating;
       
  3323         QRect clipr(cx, cy, cw, ch);
       
  3324         if (!pr.intersects(clipr) || (onlyChanged && !parag->hasChanged())) {
       
  3325             pr.setWidth(parag->document()->width());
       
  3326             parag = parag->next();
       
  3327             continue;
       
  3328         }
       
  3329 
       
  3330         drawParagraph(p, parag, cx, cy, cw, ch, doubleBuffer, pal, drawCursor,
       
  3331                        cursor, resetChanged);
       
  3332         parag = parag->next();
       
  3333     }
       
  3334 
       
  3335     parag = lastParagraph();
       
  3336 
       
  3337  floating:
       
  3338     if (parag->rect().y() + parag->rect().height() < parag->document()->height()) {
       
  3339         if (!parag->document()->parent()) {
       
  3340             QRect fillRect = QRect(0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
       
  3341                 parag->document()->height() - (parag->rect().y() + parag->rect().height()));
       
  3342             if (QRect(cx, cy, cw, ch).intersects(fillRect))
       
  3343                 p->fillRect(fillRect, pal.brush(QPalette::Base));
       
  3344         }
       
  3345         if (!flow()->isEmpty()) {
       
  3346             QRect cr(cx, cy, cw, ch);
       
  3347             flow()->drawFloatingItems(p, cr.x(), cr.y(), cr.width(), cr.height(), pal, false);
       
  3348         }
       
  3349     }
       
  3350 
       
  3351     if (buf_pixmap && buf_pixmap->height() > 300) {
       
  3352         delete buf_pixmap;
       
  3353         buf_pixmap = 0;
       
  3354     }
       
  3355 
       
  3356     Q3TextFormat::setPainter(oldPainter);
       
  3357     return lastFormatted;
       
  3358 }
       
  3359 
       
  3360 /*
       
  3361   #### this function only sets the default font size in the format collection
       
  3362  */
       
  3363 void Q3TextDocument::setDefaultFormat(const QFont &font, const QColor &color)
       
  3364 {
       
  3365     bool reformat = font != fCollection->defaultFormat()->font();
       
  3366     for (int idx = 0; idx < childList.size(); ++idx) {
       
  3367         Q3TextDocument *dc = childList.at(idx);
       
  3368         dc->setDefaultFormat(font, color);
       
  3369     }
       
  3370     fCollection->updateDefaultFormat(font, color, sheet_);
       
  3371 
       
  3372     if (!reformat)
       
  3373         return;
       
  3374     tStopWidth = formatCollection()->defaultFormat()->width(QLatin1Char('x')) * 8;
       
  3375 
       
  3376     // invalidate paragraphs and custom items
       
  3377     Q3TextParagraph *p = fParag;
       
  3378     while (p) {
       
  3379         p->invalidate(0);
       
  3380 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3381         for (int i = 0; i < p->length() - 1; ++i)
       
  3382             if (p->at(i)->isCustom())
       
  3383                 p->at(i)->customItem()->invalidate();
       
  3384 #endif
       
  3385         p = p->next();
       
  3386     }
       
  3387 }
       
  3388 
       
  3389 
       
  3390 /*!
       
  3391     \preliminary
       
  3392 
       
  3393     Generates an internal object for the tag called \a name, given the
       
  3394     attributes \a attr, and using additional information provided by
       
  3395     the mime source factory \a factory.
       
  3396 
       
  3397     \a context is the optional context of the document, i.e. the path
       
  3398     to look for relative links. This becomes important if the text
       
  3399     contains relative references, for example within image tags.
       
  3400     QSimpleRichText always uses the default mime source factory (see
       
  3401     \l{Q3MimeSourceFactory::defaultFactory()}) to resolve these
       
  3402     references. The context will then be used to calculate the
       
  3403     absolute path. See Q3MimeSourceFactory::makeAbsolute() for details.
       
  3404 
       
  3405     \a emptyTag and \a doc are for internal use only.
       
  3406 
       
  3407     This function should not be used in application code.
       
  3408 */
       
  3409 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3410 Q3TextCustomItem* Q3TextDocument::tag(Q3StyleSheet *sheet, const QString& name,
       
  3411                                         const QMap<QString, QString> &attr,
       
  3412                                         const QString& context,
       
  3413                                         const Q3MimeSourceFactory& factory,
       
  3414                                         bool /*emptyTag */, Q3TextDocument *doc)
       
  3415 {
       
  3416     const Q3StyleSheetItem* style = sheet->item(name);
       
  3417     // first some known  tags
       
  3418     if (!style)
       
  3419         return 0;
       
  3420     if (style->name() == QLatin1String("img"))
       
  3421         return new Q3TextImage(doc, attr, context, (Q3MimeSourceFactory&)factory);
       
  3422     if (style->name() == QLatin1String("hr"))
       
  3423         return new Q3TextHorizontalLine(doc, attr, context, (Q3MimeSourceFactory&)factory );
       
  3424    return 0;
       
  3425 }
       
  3426 #endif
       
  3427 
       
  3428 
       
  3429 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3430 void Q3TextDocument::registerCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p)
       
  3431 {
       
  3432     if (i && i->placement() != Q3TextCustomItem::PlaceInline) {
       
  3433         flow_->registerFloatingItem(i);
       
  3434         p->registerFloatingItem(i);
       
  3435         i->setParagraph(p);
       
  3436     }
       
  3437     p->mightHaveCustomItems = mightHaveCustomItems = true;
       
  3438 }
       
  3439 
       
  3440 void Q3TextDocument::unregisterCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p)
       
  3441 {
       
  3442     p->unregisterFloatingItem(i);
       
  3443     i->setParagraph(0);
       
  3444     flow_->unregisterFloatingItem(i);
       
  3445 }
       
  3446 #endif
       
  3447 
       
  3448 bool Q3TextDocument::hasFocusParagraph() const
       
  3449 {
       
  3450     return !!focusIndicator.parag;
       
  3451 }
       
  3452 
       
  3453 QString Q3TextDocument::focusHref() const
       
  3454 {
       
  3455     return focusIndicator.href;
       
  3456 }
       
  3457 
       
  3458 QString Q3TextDocument::focusName() const
       
  3459 {
       
  3460     return focusIndicator.name;
       
  3461 }
       
  3462 
       
  3463 bool Q3TextDocument::focusNextPrevChild(bool next)
       
  3464 {
       
  3465     if (!focusIndicator.parag) {
       
  3466         if (next) {
       
  3467             focusIndicator.parag = fParag;
       
  3468             focusIndicator.start = 0;
       
  3469             focusIndicator.len = 0;
       
  3470         } else {
       
  3471             focusIndicator.parag = lParag;
       
  3472             focusIndicator.start = lParag->length();
       
  3473             focusIndicator.len = 0;
       
  3474         }
       
  3475     } else {
       
  3476         focusIndicator.parag->setChanged(true);
       
  3477     }
       
  3478     focusIndicator.href.clear();
       
  3479     focusIndicator.name.clear();
       
  3480 
       
  3481     if (next) {
       
  3482         Q3TextParagraph *p = focusIndicator.parag;
       
  3483         int index = focusIndicator.start + focusIndicator.len;
       
  3484         while (p) {
       
  3485             for (int i = index; i < p->length(); ++i) {
       
  3486                 if (p->at(i)->isAnchor()) {
       
  3487                     p->setChanged(true);
       
  3488                     focusIndicator.parag = p;
       
  3489                     focusIndicator.start = i;
       
  3490                     focusIndicator.len = 0;
       
  3491                     focusIndicator.href = p->at(i)->anchorHref();
       
  3492                     focusIndicator.name = p->at(i)->anchorName();
       
  3493                     while (i < p->length()) {
       
  3494                         if (!p->at(i)->isAnchor())
       
  3495                             return true;
       
  3496                         focusIndicator.len++;
       
  3497                         i++;
       
  3498                     }
       
  3499 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3500                 } else if (p->at(i)->isCustom()) {
       
  3501                     if (p->at(i)->customItem()->isNested()) {
       
  3502                         Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
       
  3503                         QList<Q3TextTableCell *> cells = t->tableCells();
       
  3504                         // first try to continue
       
  3505                         int idx;
       
  3506                         bool resetCells = true;
       
  3507                         for (idx = 0; idx < cells.size(); ++idx) {
       
  3508                             Q3TextTableCell *c = cells.at(idx);
       
  3509                             if (c->richText()->hasFocusParagraph()) {
       
  3510                                 if (c->richText()->focusNextPrevChild(next)) {
       
  3511                                     p->setChanged(true);
       
  3512                                     focusIndicator.parag = p;
       
  3513                                     focusIndicator.start = i;
       
  3514                                     focusIndicator.len = 0;
       
  3515                                     focusIndicator.href = c->richText()->focusHref();
       
  3516                                     focusIndicator.name = c->richText()->focusName();
       
  3517                                     return true;
       
  3518                                 } else {
       
  3519                                     resetCells = false;
       
  3520                                     ++idx;
       
  3521                                     break;
       
  3522                                 }
       
  3523                             }
       
  3524                         }
       
  3525                         // now really try
       
  3526                         if (resetCells)
       
  3527                             idx = 0;
       
  3528                         for (; idx < cells.size(); ++idx) {
       
  3529                             Q3TextTableCell *c = cells.at(idx);
       
  3530                             if (c->richText()->focusNextPrevChild(next)) {
       
  3531                                 p->setChanged(true);
       
  3532                                 focusIndicator.parag = p;
       
  3533                                 focusIndicator.start = i;
       
  3534                                 focusIndicator.len = 0;
       
  3535                                 focusIndicator.href = c->richText()->focusHref();
       
  3536                                 focusIndicator.name = c->richText()->focusName();
       
  3537                                 return true;
       
  3538                             }
       
  3539                         }
       
  3540                     }
       
  3541 #endif
       
  3542                 }
       
  3543             }
       
  3544             index = 0;
       
  3545             p = p->next();
       
  3546         }
       
  3547     } else {
       
  3548         Q3TextParagraph *p = focusIndicator.parag;
       
  3549         int index = focusIndicator.start - 1;
       
  3550         if (focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1)
       
  3551             index++;
       
  3552         while (p) {
       
  3553             for (int i = index; i >= 0; --i) {
       
  3554                 if (p->at(i)->isAnchor()) {
       
  3555                     p->setChanged(true);
       
  3556                     focusIndicator.parag = p;
       
  3557                     focusIndicator.start = i;
       
  3558                     focusIndicator.len = 0;
       
  3559                     focusIndicator.href = p->at(i)->anchorHref();
       
  3560                     focusIndicator.name = p->at(i)->anchorName();
       
  3561                     while (i >= -1) {
       
  3562                         if (i < 0 || !p->at(i)->isAnchor()) {
       
  3563                             focusIndicator.start++;
       
  3564                             return true;
       
  3565                         }
       
  3566                         if (i < 0)
       
  3567                             break;
       
  3568                         focusIndicator.len++;
       
  3569                         focusIndicator.start--;
       
  3570                         i--;
       
  3571                     }
       
  3572 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3573                 } else if (p->at(i)->isCustom()) {
       
  3574                     if (p->at(i)->customItem()->isNested()) {
       
  3575                         Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
       
  3576                         QList<Q3TextTableCell *> cells = t->tableCells();
       
  3577                         // first try to continue
       
  3578                         int idx;
       
  3579                         bool resetCells = true;
       
  3580                         for (idx = cells.size()-1; idx >= 0; --idx) {
       
  3581                             Q3TextTableCell *c = cells.at(idx);
       
  3582                             if (c->richText()->hasFocusParagraph()) {
       
  3583                                 if (c->richText()->focusNextPrevChild(next)) {
       
  3584                                     p->setChanged(true);
       
  3585                                     focusIndicator.parag = p;
       
  3586                                     focusIndicator.start = i;
       
  3587                                     focusIndicator.len = 0;
       
  3588                                     focusIndicator.href = c->richText()->focusHref();
       
  3589                                     focusIndicator.name = c->richText()->focusName();
       
  3590                                     return true;
       
  3591                                 } else {
       
  3592                                     resetCells = false;
       
  3593                                     --idx;
       
  3594                                     break;
       
  3595                                 }
       
  3596                             }
       
  3597                         }
       
  3598                         // now really try
       
  3599                         if (resetCells)
       
  3600                             idx = cells.size()-1;
       
  3601                         for (; idx >= 0; --idx) {
       
  3602                             Q3TextTableCell *c = cells.at(idx);
       
  3603                             if (c->richText()->focusNextPrevChild(next)) {
       
  3604                                 p->setChanged(true);
       
  3605                                 focusIndicator.parag = p;
       
  3606                                 focusIndicator.start = i;
       
  3607                                 focusIndicator.len = 0;
       
  3608                                 focusIndicator.href = c->richText()->focusHref();
       
  3609                                 focusIndicator.name = c->richText()->focusName();
       
  3610                                 return true;
       
  3611                             }
       
  3612                         }
       
  3613                     }
       
  3614 #endif
       
  3615                 }
       
  3616             }
       
  3617             p = p->prev();
       
  3618             if (p)
       
  3619                 index = p->length() - 1;
       
  3620         }
       
  3621     }
       
  3622 
       
  3623     focusIndicator.parag = 0;
       
  3624 
       
  3625     return false;
       
  3626 }
       
  3627 
       
  3628 int Q3TextDocument::length() const
       
  3629 {
       
  3630     int l = -1;
       
  3631     Q3TextParagraph *p = fParag;
       
  3632     while (p) {
       
  3633         l += p->length();
       
  3634         p = p->next();
       
  3635     }
       
  3636     return qMax(0,l);
       
  3637 }
       
  3638 
       
  3639 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  3640 
       
  3641 int Q3TextFormat::width(const QChar &c) const
       
  3642 {
       
  3643     if (c.unicode() == 0xad) // soft hyphen
       
  3644         return 0;
       
  3645     if (!pntr || !pntr->isActive()) {
       
  3646         if (c == QLatin1Char('\t'))
       
  3647             return fm.width(QLatin1Char(' '));
       
  3648         if (ha == AlignNormal) {
       
  3649             int w;
       
  3650             if (c.row())
       
  3651                 w = fm.width(c);
       
  3652             else
       
  3653                 w = widths[c.unicode()];
       
  3654             if (w == 0 && !c.row()) {
       
  3655                 w = fm.width(c);
       
  3656                 ((Q3TextFormat*)this)->widths[c.unicode()] = w;
       
  3657             }
       
  3658             return w;
       
  3659         } else {
       
  3660             QFont f(fn);
       
  3661             if (usePixelSizes)
       
  3662                 f.setPixelSize((f.pixelSize() * 2) / 3);
       
  3663             else
       
  3664                 f.setPointSize((f.pointSize() * 2) / 3);
       
  3665             QFontMetrics fm_(f);
       
  3666             return fm_.width(c);
       
  3667         }
       
  3668     }
       
  3669 
       
  3670     QFont f(fn);
       
  3671     if (ha != AlignNormal) {
       
  3672         if (usePixelSizes)
       
  3673             f.setPixelSize((f.pixelSize() * 2) / 3);
       
  3674         else
       
  3675             f.setPointSize((f.pointSize() * 2) / 3);
       
  3676     }
       
  3677     applyFont(f);
       
  3678 
       
  3679     return pntr_fm->width(c);
       
  3680 }
       
  3681 
       
  3682 int Q3TextFormat::width(const QString &str, int pos) const
       
  3683 {
       
  3684     int w = 0;
       
  3685     if (str.unicode()[pos].unicode() == 0xad)
       
  3686         return w;
       
  3687     if (!pntr || !pntr->isActive()) {
       
  3688         if (ha == AlignNormal) {
       
  3689             w = fm.charWidth(str, pos);
       
  3690         } else {
       
  3691             QFont f(fn);
       
  3692             if (usePixelSizes)
       
  3693                 f.setPixelSize((f.pixelSize() * 2) / 3);
       
  3694             else
       
  3695                 f.setPointSize((f.pointSize() * 2) / 3);
       
  3696             QFontMetrics fm_(f);
       
  3697             w = fm_.charWidth(str, pos);
       
  3698         }
       
  3699     } else {
       
  3700         QFont f(fn);
       
  3701         if (ha != AlignNormal) {
       
  3702             if (usePixelSizes)
       
  3703                 f.setPixelSize((f.pixelSize() * 2) / 3);
       
  3704             else
       
  3705                 f.setPointSize((f.pointSize() * 2) / 3);
       
  3706         }
       
  3707         applyFont(f);
       
  3708         w = pntr_fm->charWidth(str, pos);
       
  3709     }
       
  3710     return w;
       
  3711 }
       
  3712 
       
  3713 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  3714 
       
  3715 Q3TextString::Q3TextString()
       
  3716 {
       
  3717     bidiDirty = true;
       
  3718     bidi = false;
       
  3719     rightToLeft = false;
       
  3720     dir = QChar::DirON;
       
  3721 }
       
  3722 
       
  3723 Q3TextString::Q3TextString(const Q3TextString &s)
       
  3724 {
       
  3725     bidiDirty = true;
       
  3726     bidi = s.bidi;
       
  3727     rightToLeft = s.rightToLeft;
       
  3728     dir = s.dir;
       
  3729     data = s.data;
       
  3730     data.detach();
       
  3731     for (int i = 0; i < (int)data.size(); ++i) {
       
  3732         Q3TextFormat *f = data[i].format();
       
  3733         if (f)
       
  3734             f->addRef();
       
  3735     }
       
  3736 }
       
  3737 
       
  3738 void Q3TextString::insert(int index, const QString &s, Q3TextFormat *f)
       
  3739 {
       
  3740     insert(index, s.unicode(), s.length(), f);
       
  3741 }
       
  3742 
       
  3743 void Q3TextString::insert(int index, const QChar *unicode, int len, Q3TextFormat *f)
       
  3744 {
       
  3745     int os = data.size();
       
  3746     data.resize(data.size() + len);
       
  3747     if (index < os) {
       
  3748         memmove(data.data() + index + len, data.data() + index,
       
  3749                  sizeof(Q3TextStringChar) * (os - index));
       
  3750     }
       
  3751     Q3TextStringChar *ch = data.data() + index;
       
  3752     for (int i = 0; i < len; ++i) {
       
  3753         ch->x = 0;
       
  3754         ch->lineStart = 0;
       
  3755         ch->nobreak = false;
       
  3756         ch->type = Q3TextStringChar::Regular;
       
  3757         ch->p.format = f;
       
  3758         ch->rightToLeft = 0;
       
  3759         ch->c = unicode[i];
       
  3760         ++ch;
       
  3761     }
       
  3762     bidiDirty = true;
       
  3763 }
       
  3764 
       
  3765 Q3TextString::~Q3TextString()
       
  3766 {
       
  3767     clear();
       
  3768 }
       
  3769 
       
  3770 void Q3TextString::insert(int index, Q3TextStringChar *c, bool doAddRefFormat )
       
  3771 {
       
  3772     int os = data.size();
       
  3773     data.resize(data.size() + 1);
       
  3774     if (index < os) {
       
  3775         memmove(data.data() + index + 1, data.data() + index,
       
  3776                  sizeof(Q3TextStringChar) * (os - index));
       
  3777     }
       
  3778     Q3TextStringChar &ch = data[(int)index];
       
  3779     ch.c = c->c;
       
  3780     ch.x = 0;
       
  3781     ch.lineStart = 0;
       
  3782     ch.rightToLeft = 0;
       
  3783     ch.p.format = 0;
       
  3784     ch.type = Q3TextStringChar::Regular;
       
  3785     ch.nobreak = false;
       
  3786     if (doAddRefFormat && c->format())
       
  3787         c->format()->addRef();
       
  3788     ch.setFormat(c->format());
       
  3789     bidiDirty = true;
       
  3790 }
       
  3791 
       
  3792 int Q3TextString::appendParagraphs( Q3TextParagraph *start, Q3TextParagraph *end )
       
  3793 {
       
  3794     int paragCount = 0;
       
  3795     int newLength = data.size();
       
  3796     for (Q3TextParagraph *p = start; p != end; p = p->next()) {
       
  3797         newLength += p->length();
       
  3798         ++paragCount;
       
  3799     }
       
  3800 
       
  3801     const int oldLength = data.size();
       
  3802     data.resize(newLength);
       
  3803 
       
  3804     Q3TextStringChar *d = &data[oldLength];
       
  3805     for (Q3TextParagraph *p = start; p != end; p = p->next()) {
       
  3806         const Q3TextStringChar * const src = p->at(0);
       
  3807         int i = 0;
       
  3808         for (; i < p->length() - 1; ++i) {
       
  3809             d[i].c = src[i].c;
       
  3810             d[i].x = 0;
       
  3811             d[i].lineStart = 0;
       
  3812             d[i].rightToLeft = 0;
       
  3813             d[i].type = Q3TextStringChar::Regular;
       
  3814             d[i].nobreak = false;
       
  3815             d[i].p.format = src[i].format();
       
  3816             if (d[i].p.format)
       
  3817                 d[i].p.format->addRef();
       
  3818         }
       
  3819         d[i].x = 0;
       
  3820         d[i].lineStart = 0;
       
  3821         d[i].nobreak = false;
       
  3822         d[i].type = Q3TextStringChar::Regular;
       
  3823         d[i].p.format = 0;
       
  3824         d[i].rightToLeft = 0;
       
  3825         d[i].c = QLatin1Char('\n');
       
  3826         d += p->length();
       
  3827     }
       
  3828 
       
  3829     bidiDirty = true;
       
  3830     return paragCount;
       
  3831 }
       
  3832 
       
  3833 void Q3TextString::truncate(int index)
       
  3834 {
       
  3835     index = qMax(index, 0);
       
  3836     index = qMin(index, (int)data.size() - 1);
       
  3837     if (index < (int)data.size()) {
       
  3838         for (int i = index + 1; i < (int)data.size(); ++i) {
       
  3839             Q3TextStringChar &ch = data[i];
       
  3840 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3841             if (!(ch.type == Q3TextStringChar::Regular)) {
       
  3842                 delete ch.customItem();
       
  3843                 if (ch.p.custom->format)
       
  3844                     ch.p.custom->format->removeRef();
       
  3845                 delete ch.p.custom;
       
  3846                 ch.p.custom = 0;
       
  3847             } else
       
  3848 #endif
       
  3849                 if (ch.format()) {
       
  3850                     ch.format()->removeRef();
       
  3851                 }
       
  3852         }
       
  3853     }
       
  3854     data.resize(index);
       
  3855     bidiDirty = true;
       
  3856 }
       
  3857 
       
  3858 void Q3TextString::remove(int index, int len)
       
  3859 {
       
  3860     for (int i = index; i < (int)data.size() && i - index < len; ++i) {
       
  3861         Q3TextStringChar &ch = data[i];
       
  3862 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3863         if (!(ch.type == Q3TextStringChar::Regular)) {
       
  3864             delete ch.customItem();
       
  3865             if (ch.p.custom->format)
       
  3866                 ch.p.custom->format->removeRef();
       
  3867             delete ch.p.custom;
       
  3868             ch.p.custom = 0;
       
  3869         } else
       
  3870 #endif
       
  3871             if (ch.format()) {
       
  3872                 ch.format()->removeRef();
       
  3873             }
       
  3874     }
       
  3875     memmove(data.data() + index, data.data() + index + len,
       
  3876              sizeof(Q3TextStringChar) * (data.size() - index - len));
       
  3877     data.resize(data.size() - len);
       
  3878     bidiDirty = true;
       
  3879 }
       
  3880 
       
  3881 void Q3TextString::clear()
       
  3882 {
       
  3883     for (int i = 0; i < (int)data.count(); ++i) {
       
  3884         Q3TextStringChar &ch = data[i];
       
  3885 #ifndef QT_NO_TEXTCUSTOMITEM
       
  3886         if (!(ch.type == Q3TextStringChar::Regular)) {
       
  3887             if (ch.customItem() && ch.customItem()->placement() == Q3TextCustomItem::PlaceInline)
       
  3888                 delete ch.customItem();
       
  3889             if (ch.p.custom->format)
       
  3890                 ch.p.custom->format->removeRef();
       
  3891             delete ch.p.custom;
       
  3892             ch.p.custom = 0;
       
  3893         } else
       
  3894 #endif
       
  3895             if (ch.format()) {
       
  3896                 ch.format()->removeRef();
       
  3897             }
       
  3898     }
       
  3899     data.resize(0);
       
  3900     bidiDirty = true;
       
  3901 }
       
  3902 
       
  3903 void Q3TextString::setFormat(int index, Q3TextFormat *f, bool useCollection)
       
  3904 {
       
  3905     Q3TextStringChar &ch = data[index];
       
  3906     if (useCollection && ch.format())
       
  3907         ch.format()->removeRef();
       
  3908     ch.setFormat(f);
       
  3909 }
       
  3910 
       
  3911 void Q3TextString::checkBidi() const
       
  3912 {
       
  3913     // ############ fix BIDI handling
       
  3914     Q3TextString *that = (Q3TextString *)this;
       
  3915     that->bidiDirty = false;
       
  3916     int length = data.size();
       
  3917     if (!length) {
       
  3918         that->bidi = rightToLeft;
       
  3919         that->rightToLeft = (dir == QChar::DirR);
       
  3920         return;
       
  3921     }
       
  3922 
       
  3923     if (dir == QChar::DirR) {
       
  3924         that->rightToLeft = true;
       
  3925     } else if (dir == QChar::DirL) {
       
  3926         that->rightToLeft = false;
       
  3927     } else {
       
  3928         that->rightToLeft = (QApplication::layoutDirection() == Qt::RightToLeft);
       
  3929     }
       
  3930 
       
  3931     const Q3TextStringChar *start = data.data();
       
  3932     const Q3TextStringChar *end = start + length;
       
  3933 
       
  3934     ((Q3TextString *)this)->stringCache = toString(data);
       
  3935 
       
  3936     // determines the properties we need for layouting
       
  3937     QTextEngine textEngine;
       
  3938     textEngine.text = toString();
       
  3939     textEngine.option.setTextDirection(rightToLeft ? Qt::RightToLeft : Qt::LeftToRight);
       
  3940     textEngine.itemize();
       
  3941     const HB_CharAttributes *ca = textEngine.attributes() + length-1;
       
  3942     Q3TextStringChar *ch = (Q3TextStringChar *)end - 1;
       
  3943     QScriptItem *item = &textEngine.layoutData->items[textEngine.layoutData->items.size()-1];
       
  3944     unsigned char bidiLevel = item->analysis.bidiLevel;
       
  3945     that->bidi = (bidiLevel || rightToLeft);
       
  3946     int pos = length-1;
       
  3947     while (ch >= start) {
       
  3948         if (item->position > pos) {
       
  3949             --item;
       
  3950             Q_ASSERT(item >= &textEngine.layoutData->items[0]);
       
  3951             bidiLevel = item->analysis.bidiLevel;
       
  3952             if (bidiLevel)
       
  3953                 that->bidi = true;
       
  3954         }
       
  3955         ch->softBreak = ca->lineBreakType >= HB_Break;
       
  3956         ch->whiteSpace = ca->whiteSpace;
       
  3957         ch->charStop = ca->charStop;
       
  3958         ch->bidiLevel = bidiLevel;
       
  3959         ch->rightToLeft = (bidiLevel%2);
       
  3960         --ch;
       
  3961         --ca;
       
  3962         --pos;
       
  3963     }
       
  3964 }
       
  3965 
       
  3966 void Q3TextDocument::setStyleSheet(Q3StyleSheet *s)
       
  3967 {
       
  3968     if (!s)
       
  3969         return;
       
  3970     sheet_ = s;
       
  3971     list_tm = list_bm = par_tm = par_bm = 12;
       
  3972     list_lm = 40;
       
  3973     li_tm = li_bm = 0;
       
  3974     Q3StyleSheetItem* item = s->item(QLatin1String("ol"));
       
  3975     if (item) {
       
  3976         list_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
       
  3977         list_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
       
  3978         list_lm = qMax(0,item->margin(Q3StyleSheetItem::MarginLeft));
       
  3979     }
       
  3980     if ((item = s->item(QLatin1String("li")))) {
       
  3981         li_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
       
  3982         li_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
       
  3983     }
       
  3984     if ((item = s->item(QLatin1String("p")))) {
       
  3985         par_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
       
  3986         par_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
       
  3987     }
       
  3988 }
       
  3989 
       
  3990 void Q3TextDocument::setUnderlineLinks(bool b) {
       
  3991     underlLinks = b;
       
  3992     for (int idx = 0; idx < childList.size(); ++idx) {
       
  3993         Q3TextDocument *dc = childList.at(idx);
       
  3994         dc->setUnderlineLinks(b);
       
  3995     }
       
  3996 }
       
  3997 
       
  3998 void Q3TextStringChar::setFormat(Q3TextFormat *f)
       
  3999 {
       
  4000     if (type == Regular) {
       
  4001         p.format = f;
       
  4002     } else {
       
  4003 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4004         if (!p.custom) {
       
  4005             p.custom = new CustomData;
       
  4006             p.custom->custom = 0;
       
  4007         }
       
  4008         p.custom->format = f;
       
  4009 #endif
       
  4010     }
       
  4011 }
       
  4012 
       
  4013 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4014 void Q3TextStringChar::setCustomItem(Q3TextCustomItem *i)
       
  4015 {
       
  4016     if (type == Regular) {
       
  4017         Q3TextFormat *f = format();
       
  4018         p.custom = new CustomData;
       
  4019         p.custom->format = f;
       
  4020     } else {
       
  4021         delete p.custom->custom;
       
  4022     }
       
  4023     p.custom->custom = i;
       
  4024     type = (type == Anchor ? CustomAnchor : Custom);
       
  4025 }
       
  4026 
       
  4027 void Q3TextStringChar::loseCustomItem()
       
  4028 {
       
  4029     if (type == Custom) {
       
  4030         Q3TextFormat *f = p.custom->format;
       
  4031         p.custom->custom = 0;
       
  4032         delete p.custom;
       
  4033         type = Regular;
       
  4034         p.format = f;
       
  4035     } else if (type == CustomAnchor) {
       
  4036         p.custom->custom = 0;
       
  4037         type = Anchor;
       
  4038     }
       
  4039 }
       
  4040 
       
  4041 #endif
       
  4042 
       
  4043 QString Q3TextStringChar::anchorName() const
       
  4044 {
       
  4045     if (type == Regular)
       
  4046         return QString();
       
  4047     else
       
  4048         return p.custom->anchorName;
       
  4049 }
       
  4050 
       
  4051 QString Q3TextStringChar::anchorHref() const
       
  4052 {
       
  4053     if (type == Regular)
       
  4054         return QString();
       
  4055     else
       
  4056         return p.custom->anchorHref;
       
  4057 }
       
  4058 
       
  4059 void Q3TextStringChar::setAnchor(const QString& name, const QString& href)
       
  4060 {
       
  4061     if (type == Regular) {
       
  4062         Q3TextFormat *f = format();
       
  4063         p.custom = new CustomData;
       
  4064 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4065         p.custom->custom = 0;
       
  4066 #endif
       
  4067         p.custom->format = f;
       
  4068         type = Anchor;
       
  4069     } else if (type == Custom) {
       
  4070         type = CustomAnchor;
       
  4071     }
       
  4072     p.custom->anchorName = name;
       
  4073     p.custom->anchorHref = href;
       
  4074 }
       
  4075 
       
  4076 
       
  4077 int Q3TextString::width(int idx) const
       
  4078 {
       
  4079      int w = 0;
       
  4080      Q3TextStringChar *c = &at(idx);
       
  4081      if (!c->charStop || c->c.unicode() == 0xad || c->c.unicode() == 0x2028)
       
  4082          return 0;
       
  4083 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4084      if(c->isCustom()) {
       
  4085          if(c->customItem()->placement() == Q3TextCustomItem::PlaceInline)
       
  4086              w = c->customItem()->width;
       
  4087      } else
       
  4088 #endif
       
  4089      {
       
  4090          int r = c->c.row();
       
  4091          if(r < 0x06
       
  4092 #ifndef Q_WS_WIN
       
  4093              // Uniscribe's handling of Asian makes the condition below fail.
       
  4094              || (r > 0x1f && !(r > 0xd7 && r < 0xe0))
       
  4095 #endif
       
  4096              ) {
       
  4097              w = c->format()->width(c->c);
       
  4098          } else {
       
  4099              // complex text. We need some hacks to get the right metric here
       
  4100              w = c->format()->width(toString(), idx);
       
  4101          }
       
  4102      }
       
  4103      return w;
       
  4104 }
       
  4105 
       
  4106 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  4107 
       
  4108 Q3TextParagraph::Q3TextParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
       
  4109     : p(pr), n(nx), docOrPseudo(dc),
       
  4110       changed(false), firstFormat(true), firstPProcess(true), needPreProcess(false), fullWidth(true),
       
  4111       lastInFrame(false), visible(true), breakable(true), movedDown(false),
       
  4112       mightHaveCustomItems(false), hasdoc(dc != 0), litem(false), rtext(false),
       
  4113       align(0), lstyle(Q3StyleSheetItem::ListDisc), invalid(0), mSelections(0),
       
  4114 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4115       mFloatingItems(0),
       
  4116 #endif
       
  4117       utm(0), ubm(0), ulm(0), urm(0), uflm(0), ulinespacing(0),
       
  4118       tabStopWidth(0), minwidth(0), tArray(0), eData(0), ldepth(0)
       
  4119 {
       
  4120     lstyle = Q3StyleSheetItem::ListDisc;
       
  4121     if (!hasdoc)
       
  4122         docOrPseudo = new Q3TextParagraphPseudoDocument;
       
  4123     bgcol = 0;
       
  4124     list_val = -1;
       
  4125     paintdevice = 0;
       
  4126     Q3TextFormat* defFormat = formatCollection()->defaultFormat();
       
  4127     if (!hasdoc) {
       
  4128         tabStopWidth = defFormat->width(QLatin1Char('x')) * 8;
       
  4129         pseudoDocument()->commandHistory = new Q3TextCommandHistory(100);
       
  4130     }
       
  4131 
       
  4132     if (p)
       
  4133         p->n = this;
       
  4134     if (n)
       
  4135         n->p = this;
       
  4136 
       
  4137     if (!p && hasdoc)
       
  4138         document()->setFirstParagraph(this);
       
  4139     if (!n && hasdoc)
       
  4140         document()->setLastParagraph(this);
       
  4141 
       
  4142     state = -1;
       
  4143 
       
  4144     if (p)
       
  4145         id = p->id + 1;
       
  4146     else
       
  4147         id = 0;
       
  4148     if (n && updateIds) {
       
  4149         Q3TextParagraph *s = n;
       
  4150         while (s) {
       
  4151             s->id = s->p->id + 1;
       
  4152             s->invalidateStyleCache();
       
  4153             s = s->n;
       
  4154         }
       
  4155     }
       
  4156 
       
  4157     str = new Q3TextString();
       
  4158     const QChar ch(QLatin1Char(' '));
       
  4159     str->insert(0, &ch, 1, formatCollection()->defaultFormat());
       
  4160 }
       
  4161 
       
  4162 Q3TextParagraph::~Q3TextParagraph()
       
  4163 {
       
  4164     delete str;
       
  4165     if (hasdoc) {
       
  4166         register Q3TextDocument *doc = document();
       
  4167         if (this == doc->minwParag) {
       
  4168             doc->minwParag = 0;
       
  4169             doc->minw = 0;
       
  4170         }
       
  4171         if (this == doc->curParag)
       
  4172             doc->curParag = 0;
       
  4173     } else {
       
  4174         delete pseudoDocument();
       
  4175     }
       
  4176     delete [] tArray;
       
  4177     delete eData;
       
  4178     QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
       
  4179     for (; it != lineStarts.end(); ++it)
       
  4180         delete *it;
       
  4181     if (mSelections)
       
  4182         delete mSelections;
       
  4183 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4184     if (mFloatingItems)
       
  4185         delete mFloatingItems;
       
  4186 #endif
       
  4187     if (p)
       
  4188         p->setNext(n);
       
  4189     if (n)
       
  4190         n->setPrev(p);
       
  4191     delete bgcol;
       
  4192 }
       
  4193 
       
  4194 void Q3TextParagraph::setNext(Q3TextParagraph *s)
       
  4195 {
       
  4196     n = s;
       
  4197     if (!n && hasdoc)
       
  4198         document()->setLastParagraph(this);
       
  4199 }
       
  4200 
       
  4201 void Q3TextParagraph::setPrev(Q3TextParagraph *s)
       
  4202 {
       
  4203     p = s;
       
  4204     if (!p && hasdoc)
       
  4205         document()->setFirstParagraph(this);
       
  4206 }
       
  4207 
       
  4208 void Q3TextParagraph::invalidate(int chr)
       
  4209 {
       
  4210     if (invalid < 0)
       
  4211         invalid = chr;
       
  4212     else
       
  4213         invalid = qMin(invalid, chr);
       
  4214 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4215     if (mFloatingItems) {
       
  4216         for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
       
  4217             Q3TextCustomItem *i = mFloatingItems->at(idx);
       
  4218             i->ypos = -1;
       
  4219         }
       
  4220     }
       
  4221 #endif
       
  4222     invalidateStyleCache();
       
  4223 }
       
  4224 
       
  4225 void Q3TextParagraph::invalidateStyleCache()
       
  4226 {
       
  4227     if (list_val < 0)
       
  4228         list_val = -1;
       
  4229 }
       
  4230 
       
  4231 
       
  4232 void Q3TextParagraph::insert(int index, const QString &s)
       
  4233 {
       
  4234     insert(index, s.unicode(), s.length());
       
  4235 }
       
  4236 
       
  4237 void Q3TextParagraph::insert(int index, const QChar *unicode, int len)
       
  4238 {
       
  4239     if (hasdoc && !document()->useFormatCollection() && document()->preProcessor())
       
  4240         str->insert(index, unicode, len,
       
  4241                      document()->preProcessor()->format(Q3TextPreProcessor::Standard));
       
  4242     else
       
  4243         str->insert(index, unicode, len, formatCollection()->defaultFormat());
       
  4244     invalidate(index);
       
  4245     needPreProcess = true;
       
  4246 }
       
  4247 
       
  4248 void Q3TextParagraph::truncate(int index)
       
  4249 {
       
  4250     str->truncate(index);
       
  4251     insert(length(), QLatin1String(" "));
       
  4252     needPreProcess = true;
       
  4253 }
       
  4254 
       
  4255 void Q3TextParagraph::remove(int index, int len)
       
  4256 {
       
  4257     if (index + len - str->length() > 0)
       
  4258         return;
       
  4259 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4260     for (int i = index; i < index + len; ++i) {
       
  4261         Q3TextStringChar *c = at(i);
       
  4262         if (hasdoc && c->isCustom()) {
       
  4263             document()->unregisterCustomItem(c->customItem(), this);
       
  4264         }
       
  4265     }
       
  4266 #endif
       
  4267     str->remove(index, len);
       
  4268     invalidate(0);
       
  4269     needPreProcess = true;
       
  4270 }
       
  4271 
       
  4272 void Q3TextParagraph::join(Q3TextParagraph *s)
       
  4273 {
       
  4274     int oh = r.height() + s->r.height();
       
  4275     n = s->n;
       
  4276     if (n)
       
  4277         n->p = this;
       
  4278     else if (hasdoc)
       
  4279         document()->setLastParagraph(this);
       
  4280 
       
  4281     int start = str->length();
       
  4282     if (length() > 0 && at(length() - 1)->c == QLatin1Char(' ')) {
       
  4283         remove(length() - 1, 1);
       
  4284         --start;
       
  4285     }
       
  4286     append(s->str->toString(), true);
       
  4287 
       
  4288     for (int i = 0; i < s->length(); ++i) {
       
  4289         if (!hasdoc || document()->useFormatCollection()) {
       
  4290             s->str->at(i).format()->addRef();
       
  4291             str->setFormat(i + start, s->str->at(i).format(), true);
       
  4292         }
       
  4293 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4294         if (s->str->at(i).isCustom()) {
       
  4295             Q3TextCustomItem * item = s->str->at(i).customItem();
       
  4296             str->at(i + start).setCustomItem(item);
       
  4297             s->str->at(i).loseCustomItem();
       
  4298             if (hasdoc) {
       
  4299                 document()->unregisterCustomItem(item, s);
       
  4300                 document()->registerCustomItem(item, this);
       
  4301             }
       
  4302         }
       
  4303         if (s->str->at(i).isAnchor()) {
       
  4304             str->at(i + start).setAnchor(s->str->at(i).anchorName(),
       
  4305                             s->str->at(i).anchorHref());
       
  4306         }
       
  4307 #endif
       
  4308     }
       
  4309 
       
  4310     if (!extraData() && s->extraData()) {
       
  4311         setExtraData(s->extraData());
       
  4312         s->setExtraData(0);
       
  4313     } else if (extraData() && s->extraData()) {
       
  4314         extraData()->join(s->extraData());
       
  4315     }
       
  4316     delete s;
       
  4317     invalidate(0);
       
  4318     r.setHeight(oh);
       
  4319     needPreProcess = true;
       
  4320     if (n) {
       
  4321         Q3TextParagraph *s = n;
       
  4322         s->invalidate(0);
       
  4323         while (s) {
       
  4324             s->id = s->p->id + 1;
       
  4325             s->state = -1;
       
  4326             s->needPreProcess = true;
       
  4327             s->changed = true;
       
  4328             s->invalidateStyleCache();
       
  4329             s = s->n;
       
  4330         }
       
  4331     }
       
  4332     format();
       
  4333     state = -1;
       
  4334 }
       
  4335 
       
  4336 void Q3TextParagraph::move(int &dy)
       
  4337 {
       
  4338     if (dy == 0)
       
  4339         return;
       
  4340     changed = true;
       
  4341     r.moveBy(0, dy);
       
  4342 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4343     if (mFloatingItems) {
       
  4344         for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
       
  4345             Q3TextCustomItem *i = mFloatingItems->at(idx);
       
  4346             i->ypos += dy;
       
  4347         }
       
  4348     }
       
  4349 #endif
       
  4350     if (p)
       
  4351         p->lastInFrame = true;
       
  4352 
       
  4353     // do page breaks if required
       
  4354     if (hasdoc && document()->isPageBreakEnabled()) {
       
  4355         int shift;
       
  4356         if ((shift = document()->formatter()->formatVertically( document(), this))) {
       
  4357             if (p)
       
  4358                 p->setChanged(true);
       
  4359             dy += shift;
       
  4360         }
       
  4361     }
       
  4362 }
       
  4363 
       
  4364 void Q3TextParagraph::format(int start, bool doMove)
       
  4365 {
       
  4366     if (!str || str->length() == 0 || !formatter())
       
  4367         return;
       
  4368 
       
  4369     if (hasdoc &&
       
  4370          document()->preProcessor() &&
       
  4371          (needPreProcess || state == -1))
       
  4372         document()->preProcessor()->process(document(), this, invalid <= 0 ? 0 : invalid);
       
  4373     needPreProcess = false;
       
  4374 
       
  4375     if (invalid == -1)
       
  4376         return;
       
  4377 
       
  4378     r.moveTopLeft(QPoint(documentX(), p ? p->r.y() + p->r.height() : documentY()));
       
  4379     if (p)
       
  4380         p->lastInFrame = false;
       
  4381 
       
  4382     movedDown = false;
       
  4383     bool formattedAgain = false;
       
  4384 
       
  4385  formatAgain:
       
  4386 
       
  4387     r.setWidth(documentWidth());
       
  4388 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4389     if (hasdoc && mFloatingItems) {
       
  4390         for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
       
  4391             Q3TextCustomItem *i = mFloatingItems->at(idx);
       
  4392             i->ypos = r.y();
       
  4393             if (i->placement() == Q3TextCustomItem::PlaceRight) {
       
  4394                 i->xpos = r.x() + r.width() - i->width;
       
  4395             }
       
  4396         }
       
  4397     }
       
  4398 #endif
       
  4399     QMap<int, QTextLineStart*> oldLineStarts = lineStarts;
       
  4400     lineStarts.clear();
       
  4401     int y = formatter()->format(document(), this, start, oldLineStarts);
       
  4402 
       
  4403 
       
  4404     r.setWidth(qMax(r.width(), formatter()->minimumWidth()));
       
  4405 
       
  4406 
       
  4407     QMap<int, QTextLineStart*>::Iterator it = oldLineStarts.begin();
       
  4408 
       
  4409     for (; it != oldLineStarts.end(); ++it)
       
  4410         delete *it;
       
  4411 
       
  4412     if (!hasdoc) { // qt_format_text bounding rect handling
       
  4413         it = lineStarts.begin();
       
  4414         int usedw = 0;
       
  4415         for (; it != lineStarts.end(); ++it)
       
  4416             usedw = qMax(usedw, (*it)->w);
       
  4417         if (r.width() <= 0) {
       
  4418             // if the user specifies an invalid rect, this means that the
       
  4419             // bounding box should grow to the width that the text actually
       
  4420             // needs
       
  4421             r.setWidth(usedw);
       
  4422         } else {
       
  4423             r.setWidth(qMin(usedw, r.width()));
       
  4424         }
       
  4425     }
       
  4426 
       
  4427     if (y != r.height())
       
  4428         r.setHeight(y);
       
  4429 
       
  4430     if (!visible) {
       
  4431         r.setHeight(0);
       
  4432     } else {
       
  4433         int minw = minwidth = formatter()->minimumWidth();
       
  4434         int wused = formatter()->widthUsed();
       
  4435         wused = qMax(minw, wused);
       
  4436         if (hasdoc) {
       
  4437             document()->setMinimumWidth(minw, wused, this);
       
  4438         }  else {
       
  4439             pseudoDocument()->minw = qMax(pseudoDocument()->minw, minw);
       
  4440             pseudoDocument()->wused = qMax(pseudoDocument()->wused, wused);
       
  4441         }
       
  4442     }
       
  4443 
       
  4444     // do page breaks if required
       
  4445     if (hasdoc && document()->isPageBreakEnabled()) {
       
  4446         int shift = document()->formatter()->formatVertically(document(), this);
       
  4447         if (shift && !formattedAgain) {
       
  4448             formattedAgain = true;
       
  4449             goto formatAgain;
       
  4450         }
       
  4451     }
       
  4452 
       
  4453     if (n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y()) {
       
  4454         int dy = (r.y() + r.height()) - n->r.y();
       
  4455         Q3TextParagraph *s = n;
       
  4456         bool makeInvalid = p && p->lastInFrame;
       
  4457         while (s && dy) {
       
  4458             if (!s->isFullWidth())
       
  4459                 makeInvalid = true;
       
  4460             if (makeInvalid)
       
  4461                 s->invalidate(0);
       
  4462             s->move(dy);
       
  4463             if (s->lastInFrame)
       
  4464                 makeInvalid = true;
       
  4465             s = s->n;
       
  4466         }
       
  4467     }
       
  4468 
       
  4469     firstFormat = false;
       
  4470     changed = true;
       
  4471     invalid = -1;
       
  4472     //#####   string()->setTextChanged(false);
       
  4473 }
       
  4474 
       
  4475 int Q3TextParagraph::lineHeightOfChar(int i, int *bl, int *y) const
       
  4476 {
       
  4477     if (!isValid())
       
  4478         ((Q3TextParagraph*)this)->format();
       
  4479 
       
  4480     QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
       
  4481     --it;
       
  4482     for (;;) {
       
  4483         if (i >= it.key()) {
       
  4484             if (bl)
       
  4485                 *bl = (*it)->baseLine;
       
  4486             if (y)
       
  4487                 *y = (*it)->y;
       
  4488             return (*it)->h;
       
  4489         }
       
  4490         if (it == lineStarts.begin())
       
  4491             break;
       
  4492         --it;
       
  4493     }
       
  4494 
       
  4495     qWarning("Q3TextParagraph::lineHeightOfChar: couldn't find lh for %d", i);
       
  4496     return 15;
       
  4497 }
       
  4498 
       
  4499 Q3TextStringChar *Q3TextParagraph::lineStartOfChar(int i, int *index, int *line) const
       
  4500 {
       
  4501     if (!isValid())
       
  4502         ((Q3TextParagraph*)this)->format();
       
  4503 
       
  4504     int l = (int)lineStarts.count() - 1;
       
  4505     QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
       
  4506     --it;
       
  4507     for (;;) {
       
  4508         if (i >= it.key()) {
       
  4509             if (index)
       
  4510                 *index = it.key();
       
  4511             if (line)
       
  4512                 *line = l;
       
  4513             return &str->at(it.key());
       
  4514         }
       
  4515         if (it == lineStarts.begin())
       
  4516             break;
       
  4517         --it;
       
  4518         --l;
       
  4519     }
       
  4520 
       
  4521     qWarning("Q3TextParagraph::lineStartOfChar: couldn't find %d", i);
       
  4522     return 0;
       
  4523 }
       
  4524 
       
  4525 int Q3TextParagraph::lines() const
       
  4526 {
       
  4527     if (!isValid())
       
  4528         ((Q3TextParagraph*)this)->format();
       
  4529 
       
  4530     return (int)lineStarts.count();
       
  4531 }
       
  4532 
       
  4533 Q3TextStringChar *Q3TextParagraph::lineStartOfLine(int line, int *index) const
       
  4534 {
       
  4535     if (!isValid())
       
  4536         ((Q3TextParagraph*)this)->format();
       
  4537 
       
  4538     if (line >= 0 && line < (int)lineStarts.count()) {
       
  4539         QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
       
  4540         while (line-- > 0)
       
  4541             ++it;
       
  4542         int i = it.key();
       
  4543         if (index)
       
  4544             *index = i;
       
  4545         return &str->at(i);
       
  4546     }
       
  4547 
       
  4548     qWarning("Q3TextParagraph::lineStartOfLine: couldn't find %d", line);
       
  4549     return 0;
       
  4550 }
       
  4551 
       
  4552 int Q3TextParagraph::leftGap() const
       
  4553 {
       
  4554     if (!isValid())
       
  4555         ((Q3TextParagraph*)this)->format();
       
  4556 
       
  4557     if (str->length() == 0)
       
  4558         return 0;
       
  4559 
       
  4560     int line = 0;
       
  4561     int x = str->length() ? str->at(0).x : 0;  /* set x to x of first char */
       
  4562     if (str->isBidi()) {
       
  4563         for (int i = 1; i < str->length()-1; ++i)
       
  4564             x = qMin(x, str->at(i).x);
       
  4565         return x;
       
  4566     }
       
  4567 
       
  4568     QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
       
  4569     while (line < (int)lineStarts.count()) {
       
  4570         int i = it.key(); /* char index */
       
  4571         x = qMin(x, str->at(i).x);
       
  4572         ++it;
       
  4573         ++line;
       
  4574     }
       
  4575     return x;
       
  4576 }
       
  4577 
       
  4578 void Q3TextParagraph::setFormat(int index, int len, Q3TextFormat *f, bool useCollection, int flags)
       
  4579 {
       
  4580     if (!f)
       
  4581         return;
       
  4582     if (index < 0)
       
  4583         index = 0;
       
  4584     if (index > str->length() - 1)
       
  4585         index = str->length() - 1;
       
  4586     if (index + len >= str->length())
       
  4587         len = str->length() - index;
       
  4588 
       
  4589     Q3TextFormatCollection *fc = 0;
       
  4590     if (useCollection)
       
  4591         fc = formatCollection();
       
  4592     Q3TextFormat *of;
       
  4593     for (int i = 0; i < len; ++i) {
       
  4594         of = str->at(i + index).format();
       
  4595         if (!changed && (!of || f->key() != of->key()))
       
  4596             changed = true;
       
  4597         if (invalid == -1 &&
       
  4598              (f->font().family() != of->font().family() ||
       
  4599                f->font().pointSize() != of->font().pointSize() ||
       
  4600                f->font().weight() != of->font().weight() ||
       
  4601                f->font().italic() != of->font().italic() ||
       
  4602                f->vAlign() != of->vAlign())) {
       
  4603             invalidate(0);
       
  4604         }
       
  4605         if (flags == -1 || flags == Q3TextFormat::Format || !fc) {
       
  4606             if (fc)
       
  4607                 f = fc->format(f);
       
  4608             str->setFormat(i + index, f, useCollection);
       
  4609         } else {
       
  4610             Q3TextFormat *fm = fc->format(of, f, flags);
       
  4611             str->setFormat(i + index, fm, useCollection);
       
  4612         }
       
  4613     }
       
  4614 }
       
  4615 
       
  4616 void Q3TextParagraph::indent(int *oldIndent, int *newIndent)
       
  4617 {
       
  4618     if (!hasdoc || !document()->indent() || isListItem()) {
       
  4619         if (oldIndent)
       
  4620             *oldIndent = 0;
       
  4621         if (newIndent)
       
  4622             *newIndent = 0;
       
  4623         if (oldIndent && newIndent)
       
  4624             *newIndent = *oldIndent;
       
  4625         return;
       
  4626     }
       
  4627     document()->indent()->indent(document(), this, oldIndent, newIndent);
       
  4628 }
       
  4629 
       
  4630 void Q3TextParagraph::paint(QPainter &painter, const QPalette &pal, Q3TextCursor *cursor,
       
  4631                             bool drawSelections, int clipx, int clipy, int clipw, int cliph)
       
  4632 {
       
  4633     if (!visible)
       
  4634         return;
       
  4635     int i, y, h, baseLine, xstart, xend = 0;
       
  4636     i = y =h = baseLine = 0;
       
  4637     QRect cursorRect;
       
  4638     drawSelections &= (mSelections != 0);
       
  4639     // macintosh full-width selection style
       
  4640     bool fullWidthStyle = QApplication::style()->styleHint(QStyle::SH_RichText_FullWidthSelection);
       
  4641     int fullSelectionWidth = 0;
       
  4642     if (drawSelections && fullWidthStyle)
       
  4643         fullSelectionWidth = (hasdoc ? document()->width() : r.width());
       
  4644 
       
  4645     QString qstr = str->toString();
       
  4646     qstr.detach();
       
  4647     // ### workaround so that \n are not drawn, actually this should
       
  4648     // be fixed in QFont somewhere (under Windows you get ugly boxes
       
  4649     // otherwise)
       
  4650     QChar* uc = (QChar*) qstr.unicode();
       
  4651     for (int ii = 0; ii < qstr.length(); ii++)
       
  4652         if (uc[(int)ii]== QLatin1Char(QLatin1Char('\n')) || uc[(int)ii] == QLatin1Char('\t'))
       
  4653             uc[(int)ii] = 0x20;
       
  4654 
       
  4655     int line = -1;
       
  4656     int paintStart = 0;
       
  4657     Q3TextStringChar *chr = 0;
       
  4658     Q3TextStringChar *nextchr = at(0);
       
  4659     for (i = 0; i < length(); i++) {
       
  4660         chr = nextchr;
       
  4661         if (i < length()-1)
       
  4662             nextchr = at(i+1);
       
  4663 
       
  4664         // we flush at end of document
       
  4665         bool flush = (i == length()-1);
       
  4666         bool ignoreSoftHyphen = false;
       
  4667         if (!flush) {
       
  4668             // we flush at end of line
       
  4669             flush |= nextchr->lineStart;
       
  4670             // we flush on format changes
       
  4671             flush |= (nextchr->format() != chr->format());
       
  4672             // we flush on link changes
       
  4673             flush |= (nextchr->isLink() != chr->isLink());
       
  4674             // we flush on start of run
       
  4675             flush |= (nextchr->bidiLevel != chr->bidiLevel);
       
  4676             // we flush on bidi changes
       
  4677             flush |= (nextchr->rightToLeft != chr->rightToLeft);
       
  4678             // we flush before and after tabs
       
  4679             flush |= (chr->c == QLatin1Char('\t') || nextchr->c == QLatin1Char('\t'));
       
  4680             // we flush on soft hyphens
       
  4681             if (chr->c.unicode() == 0xad) {
       
  4682                 flush = true;
       
  4683                 if (!nextchr->lineStart)
       
  4684                     ignoreSoftHyphen = true;
       
  4685             }
       
  4686             // we flush on custom items
       
  4687             flush |= chr->isCustom();
       
  4688             // we flush before custom items
       
  4689             flush |= nextchr->isCustom();
       
  4690             // when painting justified, we flush on spaces
       
  4691             if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify)
       
  4692                 flush |= chr->whiteSpace;
       
  4693         }
       
  4694 
       
  4695         // init a new line
       
  4696         if (chr->lineStart) {
       
  4697             ++line;
       
  4698             paintStart = i;
       
  4699             lineInfo(line, y, h, baseLine);
       
  4700             if (clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph) { // outside clip area, leave
       
  4701                 break;
       
  4702             }
       
  4703 
       
  4704             // if this is the first line and we are a list item, draw the the bullet label
       
  4705             if (line == 0 && isListItem()) {
       
  4706                 int x = chr->x;
       
  4707                 if (str->isBidi()) {
       
  4708                     if (str->isRightToLeft()) {
       
  4709                         x = chr->x + str->width(0);
       
  4710                         for (int k = 1; k < length(); ++k) {
       
  4711                             if (str->at(k).lineStart)
       
  4712                                 break;
       
  4713                             x = qMax(x, str->at(k).x + str->width(k));
       
  4714                         }
       
  4715                     } else {
       
  4716                         x = chr->x;
       
  4717                         for (int k = 1; k < length(); ++k) {
       
  4718                             if (str->at(k).lineStart)
       
  4719                                 break;
       
  4720                             x = qMin(x, str->at(k).x);
       
  4721                         }
       
  4722                     }
       
  4723                 }
       
  4724                 drawLabel(&painter, x, y, 0, 0, baseLine, pal);
       
  4725             }
       
  4726         }
       
  4727 
       
  4728         // check for cursor mark
       
  4729         if (cursor && this == cursor->paragraph() && i == cursor->index()) {
       
  4730             Q3TextStringChar *c = i == 0 ? chr : chr - 1;
       
  4731             cursorRect.setRect(cursor->x() , y + baseLine - c->format()->ascent(),
       
  4732                                 1, c->format()->height());
       
  4733         }
       
  4734 
       
  4735         if (flush) {  // something changed, draw what we have so far
       
  4736             if (chr->rightToLeft) {
       
  4737                 xstart = chr->x;
       
  4738                 xend = at(paintStart)->x + str->width(paintStart);
       
  4739             } else {
       
  4740                 xstart = at(paintStart)->x;
       
  4741                 xend = chr->x;
       
  4742                 if (i < length() - 1) {
       
  4743                     if (!str->at(i + 1).lineStart &&
       
  4744                          str->at(i + 1).rightToLeft == chr->rightToLeft)
       
  4745                         xend = str->at(i + 1).x;
       
  4746                     else
       
  4747                         xend += str->width(i);
       
  4748                 }
       
  4749             }
       
  4750 
       
  4751             if ((clipx == -1 || clipw <= 0 || (xend >= clipx && xstart <= clipx + clipw)) &&
       
  4752                  (clipy == -1 || clipy < y+r.y()+h)) {
       
  4753                 if (!chr->isCustom())
       
  4754                     drawString(painter, qstr, paintStart, i - paintStart + (ignoreSoftHyphen ? 0 : 1), xstart, y,
       
  4755                                 baseLine, xend-xstart, h, drawSelections, fullSelectionWidth,
       
  4756                                 chr, pal, chr->rightToLeft);
       
  4757 #ifndef QT_NO_TEXTCUSTOMITEM
       
  4758                 else if (chr->customItem()->placement() == Q3TextCustomItem::PlaceInline) {
       
  4759                     bool inSelection = false;
       
  4760                     if (drawSelections) {
       
  4761                         QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(Q3TextDocument::Standard);
       
  4762                         inSelection = (it != mSelections->constEnd() && (*it).start <= i && (*it).end > i);
       
  4763                     }
       
  4764                     chr->customItem()->draw(&painter, chr->x, y,
       
  4765                                              clipx == -1 ? clipx : (clipx - r.x()),
       
  4766                                              clipy == -1 ? clipy : (clipy - r.y()),
       
  4767                                              clipw, cliph, pal, inSelection);
       
  4768                 }
       
  4769 #endif
       
  4770             }
       
  4771             paintStart = i+1;
       
  4772         }
       
  4773 
       
  4774     }
       
  4775 
       
  4776     // time to draw the cursor
       
  4777     const int cursor_extent = 4;
       
  4778     if (!cursorRect.isNull() && cursor &&
       
  4779          ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw))) {
       
  4780         painter.fillRect(cursorRect, pal.color(QPalette::Text));
       
  4781         painter.save();
       
  4782         if (string()->isBidi()) {
       
  4783             if (at(cursor->index())->rightToLeft) {
       
  4784                 painter.setPen(Qt::black);
       
  4785                 painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
       
  4786                 painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
       
  4787             } else {
       
  4788                 painter.setPen(Qt::black);
       
  4789                 painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
       
  4790                 painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
       
  4791             }
       
  4792         }
       
  4793         painter.restore();
       
  4794     }
       
  4795 }
       
  4796 
       
  4797 //#define BIDI_DEBUG
       
  4798 
       
  4799 void Q3TextParagraph::setColorForSelection(QColor &color, QPainter &painter,
       
  4800                                            const QPalette &pal, int selection)
       
  4801 {
       
  4802     if (selection < 0)
       
  4803         return;
       
  4804     color = (hasdoc && selection != Q3TextDocument::Standard) ?
       
  4805             document()->selectionColor(selection) :
       
  4806             pal.color(QPalette::Highlight);
       
  4807     QColor text = (hasdoc && document()->hasSelectionTextColor(selection)) ? document()->selectionTextColor(selection) : pal.color(QPalette::HighlightedText);
       
  4808     if (text.isValid())
       
  4809         painter.setPen(text);
       
  4810 }
       
  4811 
       
  4812 void Q3TextParagraph::drawString(QPainter &painter, const QString &str, int start, int len,
       
  4813                                  int xstart, int y, int baseLine, int w, int h,
       
  4814                                  bool drawSelections, int fullSelectionWidth,
       
  4815                                  Q3TextStringChar *formatChar, const QPalette& pal,
       
  4816                                  bool rightToLeft)
       
  4817 {
       
  4818     bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : false;
       
  4819     Q3TextFormat* format = formatChar->format();
       
  4820 
       
  4821     int textFlags = int(rightToLeft ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
       
  4822 
       
  4823     if (!plainText || (hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color()))
       
  4824         painter.setPen(QPen(format->color()));
       
  4825     else
       
  4826         painter.setPen(pal.text().color());
       
  4827     painter.setFont(format->font());
       
  4828 
       
  4829     if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty()) {
       
  4830         if (format->useLinkColor())
       
  4831             painter.setPen(document()->linkColor.isValid() ? document()->linkColor :
       
  4832                            pal.link().color());
       
  4833         if (document()->underlineLinks()) {
       
  4834             QFont fn = format->font();
       
  4835             fn.setUnderline(true);
       
  4836             painter.setFont(fn);
       
  4837         }
       
  4838     }
       
  4839 
       
  4840     int real_length = len;
       
  4841     if (len && !rightToLeft && start + len == length()) // don't draw the last character (trailing space)
       
  4842         len--;
       
  4843     if (len && str.unicode()[start+len-1] == QChar::LineSeparator)
       
  4844         len--;
       
  4845 
       
  4846 
       
  4847     Q3TextFormat::VerticalAlignment vAlign = format->vAlign();
       
  4848     if (vAlign != Q3TextFormat::AlignNormal) {
       
  4849         // sub or superscript
       
  4850         QFont f(painter.font());
       
  4851         if (format->fontSizesInPixels())
       
  4852             f.setPixelSize((f.pixelSize() * 2) / 3);
       
  4853         else
       
  4854             f.setPointSize((f.pointSize() * 2) / 3);
       
  4855         painter.setFont(f);
       
  4856         int h = painter.fontMetrics().height();
       
  4857         baseLine += (vAlign == Q3TextFormat::AlignSubScript) ? h/6 : -h/2;
       
  4858     }
       
  4859 
       
  4860     bool allSelected = false;
       
  4861     if (drawSelections) {
       
  4862         QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(Q3TextDocument::Standard);
       
  4863         allSelected = (it != mSelections->constEnd() && (*it).start <= start && (*it).end >= start+len);
       
  4864     }
       
  4865     if (!allSelected)
       
  4866         painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0);
       
  4867 
       
  4868 #ifdef BIDI_DEBUG
       
  4869     painter.save();
       
  4870     painter.setPen (Qt::red);
       
  4871     painter.drawLine(xstart, y, xstart, y + baseLine);
       
  4872     painter.drawLine(xstart, y + baseLine/2, xstart + 10, y + baseLine/2);
       
  4873     int w = 0;
       
  4874     int i = 0;
       
  4875     while(i < len)
       
  4876         w += painter.fontMetrics().charWidth(str, start + i++);
       
  4877     painter.setPen (Qt::blue);
       
  4878     painter.drawLine(xstart + w - 1, y, xstart + w - 1, y + baseLine);
       
  4879     painter.drawLine(xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2);
       
  4880     painter.restore();
       
  4881 #endif
       
  4882 
       
  4883     // check if we are in a selection and draw it
       
  4884     if (drawSelections) {
       
  4885         QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constEnd();
       
  4886         while (it != mSelections->constBegin()) {
       
  4887             --it;
       
  4888             int selStart = (*it).start;
       
  4889             int selEnd = (*it).end;
       
  4890             int tmpw = w;
       
  4891 
       
  4892             selStart = qMax(selStart, start);
       
  4893             int real_selEnd = qMin(selEnd, start+real_length);
       
  4894             selEnd = qMin(selEnd, start+len);
       
  4895             bool extendRight = false;
       
  4896             bool extendLeft = false;
       
  4897             bool selWrap = (real_selEnd == length()-1 && n && n->hasSelection(it.key()));
       
  4898             if (selWrap
       
  4899                 || ((real_selEnd < this->str->length()) && this->str->at(real_selEnd).lineStart)) {
       
  4900                 extendRight = (fullSelectionWidth != 0);
       
  4901                 if (!extendRight && !rightToLeft)
       
  4902                     tmpw += painter.fontMetrics().width(QLatin1Char(' '));
       
  4903             }
       
  4904             if (fullSelectionWidth && (selStart == 0 || this->str->at(selStart).lineStart)) {
       
  4905                 extendLeft = true;
       
  4906             }
       
  4907             if (this->str->isRightToLeft() != rightToLeft)
       
  4908                 extendLeft = extendRight = false;
       
  4909 
       
  4910             if (this->str->isRightToLeft()) {
       
  4911                 bool tmp = extendLeft;
       
  4912                 extendLeft = extendRight;
       
  4913                 extendRight = tmp;
       
  4914             }
       
  4915 
       
  4916             if (selStart < real_selEnd ||
       
  4917                 (selWrap && fullSelectionWidth && extendRight &&
       
  4918                 // don't draw the standard selection on a printer=
       
  4919                 (it.key() != Q3TextDocument::Standard || !is_printer(&painter)))) {
       
  4920                 int selection = it.key();
       
  4921                 QColor color;
       
  4922                 setColorForSelection(color, painter, pal, selection);
       
  4923                 if (selStart != start || selEnd != start + len || selWrap) {
       
  4924                     // have to clip
       
  4925                     painter.save();
       
  4926                     int cs, ce;
       
  4927                     if (rightToLeft) {
       
  4928                         cs = (selEnd != start + len) ?
       
  4929                              this->str->at(this->str->previousCursorPosition(selEnd)).x : xstart;
       
  4930                         ce = (selStart != start) ?
       
  4931                              this->str->at(this->str->previousCursorPosition(selStart)).x : xstart+tmpw;
       
  4932                     } else {
       
  4933                         cs = (selStart != start) ? this->str->at(selStart).x : xstart;
       
  4934                         ce = (selEnd != start + len) ? this->str->at(selEnd).x : xstart+tmpw;
       
  4935                     }
       
  4936                     QRect r(cs, y, ce-cs, h);
       
  4937                     if (extendLeft)
       
  4938                         r.setLeft(0);
       
  4939                     if (extendRight)
       
  4940                         r.setRight(fullSelectionWidth);
       
  4941                     QRegion reg(r);
       
  4942                     if (painter.hasClipping())
       
  4943                         reg &= painter.clipRegion();
       
  4944                     painter.setClipRegion(reg);
       
  4945                 }
       
  4946                 int xleft = xstart;
       
  4947                 if (extendLeft) {
       
  4948                     tmpw += xstart;
       
  4949                     xleft = 0;
       
  4950                 }
       
  4951                 if (extendRight)
       
  4952                     tmpw = fullSelectionWidth - xleft;
       
  4953                 if(color.isValid())
       
  4954                     painter.fillRect(xleft, y, tmpw, h, color);
       
  4955                 painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0);
       
  4956                 if (selStart != start || selEnd != start + len || selWrap)
       
  4957                     painter.restore();
       
  4958             }
       
  4959         }
       
  4960     }
       
  4961 
       
  4962     if (format->isMisspelled()) {
       
  4963         painter.save();
       
  4964         painter.setPen(QPen(Qt::red, 1, Qt::DotLine));
       
  4965         painter.drawLine(xstart, y + baseLine + 1, xstart + w, y + baseLine + 1);
       
  4966         painter.restore();
       
  4967     }
       
  4968 
       
  4969     if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() &&
       
  4970          document()->focusIndicator.parag == this &&
       
  4971          ((document()->focusIndicator.start >= start  &&
       
  4972            document()->focusIndicator.start + document()->focusIndicator.len <= start + len)
       
  4973           || (document()->focusIndicator.start <= start &&
       
  4974               document()->focusIndicator.start + document()->focusIndicator.len >= start + len))) {
       
  4975         QStyleOptionFocusRect opt;
       
  4976         opt.rect.setRect(xstart, y, w, h);
       
  4977 #ifndef Q_WS_WIN
       
  4978         opt.state = QStyle::State_None;
       
  4979 #else
       
  4980         // force drawing a focus rect but only on windows because it's
       
  4981         // configurable by the user in windows settings (see
       
  4982         // SH_UnderlineShortcut style hint) and we want to override
       
  4983         // this settings.
       
  4984         opt.state = QStyle::State_KeyboardFocusChange;
       
  4985 #endif
       
  4986         opt.palette = pal;
       
  4987         QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &painter);
       
  4988     }
       
  4989 }
       
  4990 
       
  4991 void Q3TextParagraph::drawLabel(QPainter* p, int x, int y, int w, int h, int base,
       
  4992                                 const QPalette& pal)
       
  4993 {
       
  4994     QRect r (x, y, w, h);
       
  4995     Q3StyleSheetItem::ListStyle s = listStyle();
       
  4996 
       
  4997     p->save();
       
  4998     Q3TextFormat *format = at(0)->format();
       
  4999     if (format) {
       
  5000         p->setPen(format->color());
       
  5001         p->setFont(format->font());
       
  5002     }
       
  5003     QFontMetrics fm(p->fontMetrics());
       
  5004     int size = fm.lineSpacing() / 3;
       
  5005 
       
  5006     bool rtl = str->isRightToLeft();
       
  5007 
       
  5008     switch (s) {
       
  5009     case Q3StyleSheetItem::ListDecimal:
       
  5010     case Q3StyleSheetItem::ListLowerAlpha:
       
  5011     case Q3StyleSheetItem::ListUpperAlpha:
       
  5012         {
       
  5013             if (list_val == -1) { // uninitialised list value, calcluate the right one
       
  5014                 int depth = listDepth();
       
  5015                 list_val--;
       
  5016                 // ### evil, square and expensive. This needs to be done when formatting, not when painting
       
  5017                 Q3TextParagraph* s = prev();
       
  5018                 int depth_s;
       
  5019                 while (s && (depth_s = s->listDepth()) >= depth) {
       
  5020                     if (depth_s == depth && s->isListItem())
       
  5021                         list_val--;
       
  5022                     s = s->prev();
       
  5023                 }
       
  5024             }
       
  5025 
       
  5026             int n = list_val;
       
  5027             if (n < -1)
       
  5028                 n = -n - 1;
       
  5029             QString l;
       
  5030             switch (s) {
       
  5031             case Q3StyleSheetItem::ListLowerAlpha:
       
  5032                 if (n < 27) {
       
  5033                     l = QLatin1Char(('a' + (char) (n-1)));
       
  5034                     break;
       
  5035                 }
       
  5036             case Q3StyleSheetItem::ListUpperAlpha:
       
  5037                 if (n < 27) {
       
  5038                     l = QLatin1Char(('A' + (char) (n-1)));
       
  5039                     break;
       
  5040                 }
       
  5041                 break;
       
  5042             default:  //Q3StyleSheetItem::ListDecimal:
       
  5043                 l.setNum(n);
       
  5044                 break;
       
  5045             }
       
  5046             if (rtl)
       
  5047                 l.prepend(QLatin1String(" ."));
       
  5048             else
       
  5049                 l += QString::fromLatin1(". ");
       
  5050             int x = (rtl ? r.left() : r.right() - fm.width(l));
       
  5051             p->drawText(x, r.top() + base, l);
       
  5052         }
       
  5053         break;
       
  5054     case Q3StyleSheetItem::ListSquare:
       
  5055         {
       
  5056             int x = rtl ? r.left() + size : r.right() - size*2;
       
  5057             QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
       
  5058             p->fillRect(er , pal.brush(QPalette::Text));
       
  5059         }
       
  5060         break;
       
  5061     case Q3StyleSheetItem::ListCircle:
       
  5062         {
       
  5063             int x = rtl ? r.left() + size : r.right() - size*2;
       
  5064             QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
       
  5065             p->drawEllipse(er);
       
  5066         }
       
  5067         break;
       
  5068     case Q3StyleSheetItem::ListDisc:
       
  5069     default:
       
  5070         {
       
  5071             p->setBrush(pal.brush(QPalette::Text));
       
  5072             int x = rtl ? r.left() + size : r.right() - size*2;
       
  5073             QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
       
  5074             p->drawEllipse(er);
       
  5075             p->setBrush(Qt::NoBrush);
       
  5076         }
       
  5077         break;
       
  5078     }
       
  5079 
       
  5080     p->restore();
       
  5081 }
       
  5082 
       
  5083 #ifndef QT_NO_DATASTREAM
       
  5084 void Q3TextParagraph::readStyleInformation(QDataStream &stream)
       
  5085 {
       
  5086     int int_align, int_lstyle;
       
  5087     uchar uchar_litem, uchar_rtext, uchar_dir;
       
  5088     stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm
       
  5089            >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir;
       
  5090     align = int_align; lstyle = (Q3StyleSheetItem::ListStyle) int_lstyle;
       
  5091     litem = uchar_litem; rtext = uchar_rtext; str->setDirection((QChar::Direction)uchar_dir);
       
  5092     Q3TextParagraph* s = prev() ? prev() : this;
       
  5093     while (s) {
       
  5094         s->invalidate(0);
       
  5095         s = s->next();
       
  5096     }
       
  5097 }
       
  5098 
       
  5099 void Q3TextParagraph::writeStyleInformation(QDataStream& stream) const
       
  5100 {
       
  5101     stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction();
       
  5102 }
       
  5103 #endif
       
  5104 
       
  5105 
       
  5106 void Q3TextParagraph::setListItem(bool li)
       
  5107 {
       
  5108     if ((bool)litem == li)
       
  5109         return;
       
  5110     litem = li;
       
  5111     changed = true;
       
  5112     Q3TextParagraph* s = prev() ? prev() : this;
       
  5113     while (s) {
       
  5114         s->invalidate(0);
       
  5115         s = s->next();
       
  5116     }
       
  5117 }
       
  5118 
       
  5119 void Q3TextParagraph::setListDepth(int depth) {
       
  5120     if (!hasdoc || depth == ldepth)
       
  5121         return;
       
  5122     ldepth = depth;
       
  5123     Q3TextParagraph* s = prev() ? prev() : this;
       
  5124     while (s) {
       
  5125         s->invalidate(0);
       
  5126         s = s->next();
       
  5127     }
       
  5128 }
       
  5129 
       
  5130 int *Q3TextParagraph::tabArray() const
       
  5131 {
       
  5132     int *ta = tArray;
       
  5133     if (!ta && hasdoc)
       
  5134         ta = document()->tabArray();
       
  5135     return ta;
       
  5136 }
       
  5137 
       
  5138 int Q3TextParagraph::nextTab(int, int x)
       
  5139 {
       
  5140     int *ta = tArray;
       
  5141     if (hasdoc) {
       
  5142         if (!ta)
       
  5143             ta = document()->tabArray();
       
  5144         tabStopWidth = document()->tabStopWidth();
       
  5145     }
       
  5146     if (ta) {
       
  5147         int i = 0;
       
  5148         while (ta[i]) {
       
  5149             if (ta[i] >= x)
       
  5150                 return tArray[i];
       
  5151             ++i;
       
  5152         }
       
  5153         return tArray[0];
       
  5154     } else {
       
  5155         int n;
       
  5156         if (tabStopWidth != 0)
       
  5157             n = x / tabStopWidth;
       
  5158         else
       
  5159             return x;
       
  5160         return tabStopWidth * (n + 1);
       
  5161     }
       
  5162 }
       
  5163 
       
  5164 void Q3TextParagraph::adjustToPainter(QPainter *p)
       
  5165 {
       
  5166 #ifndef QT_NO_TEXTCUSTOMITEM
       
  5167     for (int i = 0; i < length(); ++i) {
       
  5168         if (at(i)->isCustom())
       
  5169             at(i)->customItem()->adjustToPainter(p);
       
  5170     }
       
  5171 #endif
       
  5172 }
       
  5173 
       
  5174 Q3TextFormatCollection *Q3TextParagraph::formatCollection() const
       
  5175 {
       
  5176     if (hasdoc)
       
  5177         return document()->formatCollection();
       
  5178     Q3TextFormatCollection* fc = &pseudoDocument()->collection;
       
  5179     if (paintdevice != fc->paintDevice())
       
  5180         fc->setPaintDevice(paintdevice);
       
  5181     return fc;
       
  5182 }
       
  5183 
       
  5184 QString Q3TextParagraph::richText() const
       
  5185 {
       
  5186     QString s;
       
  5187     Q3TextStringChar *formatChar = 0;
       
  5188     QString spaces;
       
  5189     bool doStart = richTextExportStart && richTextExportStart->paragraph() == this;
       
  5190     bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this;
       
  5191     int i;
       
  5192     QString lastAnchorName;
       
  5193     for (i = 0; i < length()-1; ++i) {
       
  5194         if (doStart && i && richTextExportStart->index() == i)
       
  5195             s += QLatin1String("<!--StartFragment-->");
       
  5196         if (doEnd && richTextExportEnd->index() == i)
       
  5197             s += QLatin1String("<!--EndFragment-->");
       
  5198         Q3TextStringChar *c = &str->at(i);
       
  5199         if (c->isAnchor() && !c->anchorName().isEmpty() && c->anchorName() != lastAnchorName) {
       
  5200 	    lastAnchorName = c->anchorName();
       
  5201             if (c->anchorName().contains(QLatin1Char('#'))) {
       
  5202                 QStringList l = c->anchorName().split(QLatin1Char('#'));
       
  5203                 for (QStringList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it)
       
  5204                     s += QLatin1String("<a name=\"") + *it + QLatin1String("\"></a>");
       
  5205             } else {
       
  5206                 s += QLatin1String("<a name=\"") + c->anchorName() + QLatin1String("\"></a>");
       
  5207             }
       
  5208         }
       
  5209         if (!formatChar) {
       
  5210             s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(),
       
  5211                                                     0, QString(), c->anchorHref());
       
  5212             formatChar = c;
       
  5213         } else if ((formatChar->format()->key() != c->format()->key()) ||
       
  5214                   (c->anchorHref() != formatChar->anchorHref()))  {
       
  5215             s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(),
       
  5216                                                     formatChar->format() , formatChar->anchorHref(), c->anchorHref());
       
  5217             formatChar = c;
       
  5218         }
       
  5219         if (c->c == QLatin1Char('<'))
       
  5220             s += QLatin1String("&lt;");
       
  5221         else if (c->c == QLatin1Char('>'))
       
  5222             s += QLatin1String("&gt;");
       
  5223         else if (c->c == QLatin1Char('&'))
       
  5224             s += QLatin1String("&amp;");
       
  5225         else if (c->c == QLatin1Char('\"'))
       
  5226             s += QLatin1String("&quot;");
       
  5227 #ifndef QT_NO_TEXTCUSTOMITEM
       
  5228         else if (c->isCustom())
       
  5229             s += c->customItem()->richText();
       
  5230 #endif
       
  5231         else if (c->c == QLatin1Char('\n') || c->c == QChar::LineSeparator)
       
  5232             s += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
       
  5233         else
       
  5234             s += c->c;
       
  5235     }
       
  5236     if (doEnd && richTextExportEnd->index() == i)
       
  5237         s += QLatin1String("<!--EndFragment-->");
       
  5238     if (formatChar)
       
  5239         s += formatChar->format()->makeFormatEndTags(formatCollection()->defaultFormat(), formatChar->anchorHref());
       
  5240     return s;
       
  5241 }
       
  5242 
       
  5243 void Q3TextParagraph::addCommand(Q3TextCommand *cmd)
       
  5244 {
       
  5245     if (!hasdoc)
       
  5246         pseudoDocument()->commandHistory->addCommand(cmd);
       
  5247     else
       
  5248         document()->commands()->addCommand(cmd);
       
  5249 }
       
  5250 
       
  5251 Q3TextCursor *Q3TextParagraph::undo(Q3TextCursor *c)
       
  5252 {
       
  5253     if (!hasdoc)
       
  5254         return pseudoDocument()->commandHistory->undo(c);
       
  5255     return document()->commands()->undo(c);
       
  5256 }
       
  5257 
       
  5258 Q3TextCursor *Q3TextParagraph::redo(Q3TextCursor *c)
       
  5259 {
       
  5260     if (!hasdoc)
       
  5261         return pseudoDocument()->commandHistory->redo(c);
       
  5262     return document()->commands()->redo(c);
       
  5263 }
       
  5264 
       
  5265 int Q3TextParagraph::topMargin() const
       
  5266 {
       
  5267     int m = 0;
       
  5268     if (rtext) {
       
  5269         m = isListItem() ? (document()->li_tm/qMax(1,listDepth()*listDepth())) :
       
  5270             (listDepth() ? 0 : document()->par_tm);
       
  5271         if (listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth()))
       
  5272             m = qMax<int>(m, document()->list_tm);
       
  5273     }
       
  5274     m += utm;
       
  5275     return scale(m, Q3TextFormat::painter());
       
  5276 }
       
  5277 
       
  5278 int Q3TextParagraph::bottomMargin() const
       
  5279 {
       
  5280     int m = 0;
       
  5281     if (rtext) {
       
  5282         m = isListItem() ? (document()->li_bm/qMax(1,listDepth()*listDepth())) :
       
  5283             (listDepth() ? 0 : document()->par_bm);
       
  5284         if (listDepth() == 1 &&( !next() || next()->listDepth() < listDepth()))
       
  5285             m = qMax<int>(m, document()->list_bm);
       
  5286     }
       
  5287     m += ubm;
       
  5288     return scale(m, Q3TextFormat::painter());
       
  5289 }
       
  5290 
       
  5291 int Q3TextParagraph::leftMargin() const
       
  5292 {
       
  5293     int m = ulm;
       
  5294     if (listDepth() && !string()->isRightToLeft())
       
  5295         m += listDepth() * document()->list_lm;
       
  5296     return scale(m, Q3TextFormat::painter());
       
  5297 }
       
  5298 
       
  5299 int Q3TextParagraph::firstLineMargin() const
       
  5300 {
       
  5301     int m = uflm;
       
  5302     return scale(m, Q3TextFormat::painter());
       
  5303 }
       
  5304 
       
  5305 int Q3TextParagraph::rightMargin() const
       
  5306 {
       
  5307     int m = urm;
       
  5308     if (listDepth() && string()->isRightToLeft())
       
  5309         m += listDepth() * document()->list_lm;
       
  5310     return scale(m, Q3TextFormat::painter());
       
  5311 }
       
  5312 
       
  5313 int Q3TextParagraph::lineSpacing() const
       
  5314 {
       
  5315     int l = ulinespacing;
       
  5316     l = scale(l, Q3TextFormat::painter());
       
  5317     return l;
       
  5318 }
       
  5319 
       
  5320 void Q3TextParagraph::copyParagData(Q3TextParagraph *parag)
       
  5321 {
       
  5322     rtext = parag->rtext;
       
  5323     lstyle = parag->lstyle;
       
  5324     ldepth = parag->ldepth;
       
  5325     litem = parag->litem;
       
  5326     align = parag->align;
       
  5327     utm = parag->utm;
       
  5328     ubm = parag->ubm;
       
  5329     urm = parag->urm;
       
  5330     ulm = parag->ulm;
       
  5331     uflm = parag->uflm;
       
  5332     ulinespacing = parag->ulinespacing;
       
  5333     QColor *c = parag->backgroundColor();
       
  5334     if (c)
       
  5335         setBackgroundColor(*c);
       
  5336     str->setDirection(parag->str->direction());
       
  5337 }
       
  5338 
       
  5339 void Q3TextParagraph::show()
       
  5340 {
       
  5341     if (visible || !hasdoc)
       
  5342         return;
       
  5343     visible = true;
       
  5344 }
       
  5345 
       
  5346 void Q3TextParagraph::hide()
       
  5347 {
       
  5348     if (!visible || !hasdoc)
       
  5349         return;
       
  5350     visible = false;
       
  5351 }
       
  5352 
       
  5353 void Q3TextParagraph::setDirection(QChar::Direction dir)
       
  5354 {
       
  5355     if (str && str->direction() != dir) {
       
  5356         str->setDirection(dir);
       
  5357         invalidate(0);
       
  5358     }
       
  5359 }
       
  5360 
       
  5361 QChar::Direction Q3TextParagraph::direction() const
       
  5362 {
       
  5363     return (str ? str->direction() : QChar::DirON);
       
  5364 }
       
  5365 
       
  5366 void Q3TextParagraph::setChanged(bool b, bool recursive)
       
  5367 {
       
  5368     changed = b;
       
  5369     if (recursive) {
       
  5370         if (document() && document()->parentParagraph())
       
  5371             document()->parentParagraph()->setChanged(b, recursive);
       
  5372     }
       
  5373 }
       
  5374 
       
  5375 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  5376 
       
  5377 
       
  5378 Q3TextPreProcessor::Q3TextPreProcessor()
       
  5379 {
       
  5380 }
       
  5381 
       
  5382 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  5383 
       
  5384 Q3TextFormatter::Q3TextFormatter()
       
  5385     : thisminw(0), thiswused(0), wrapEnabled(true), wrapColumn(-1), biw(false)
       
  5386 {
       
  5387 }
       
  5388 
       
  5389 QTextLineStart *Q3TextFormatter::formatLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line,
       
  5390                                                    Q3TextStringChar *startChar, Q3TextStringChar *lastChar, int align, int space)
       
  5391 {
       
  5392     if (lastChar < startChar)
       
  5393         return new QTextLineStart;
       
  5394 #ifndef QT_NO_COMPLEXTEXT
       
  5395     if(string->isBidi())
       
  5396         return bidiReorderLine(parag, string, line, startChar, lastChar, align, space);
       
  5397 #endif
       
  5398     int start = (startChar - &string->at(0));
       
  5399     int last = (lastChar - &string->at(0));
       
  5400 
       
  5401     // ignore white space at the end of the line.
       
  5402     Q3TextStringChar *ch = lastChar;
       
  5403     while (ch > startChar && ch->whiteSpace) {
       
  5404         space += ch->format()->width(QLatin1Char(' '));
       
  5405         --ch;
       
  5406     }
       
  5407 
       
  5408     if (space < 0)
       
  5409         space = 0;
       
  5410 
       
  5411     // do alignment Auto == Left in this case
       
  5412     if (align & Qt::AlignHCenter || align & Qt::AlignRight) {
       
  5413         if (align & Qt::AlignHCenter)
       
  5414             space /= 2;
       
  5415         for (int j = start; j <= last; ++j)
       
  5416             string->at(j).x += space;
       
  5417     } else if (align & Qt::AlignJustify) {
       
  5418         int numSpaces = 0;
       
  5419         // End at "last-1", the last space ends up with a width of 0
       
  5420         for (int j = last-1; j >= start; --j) {
       
  5421             // Start at last tab, if any.
       
  5422             Q3TextStringChar &ch = string->at(j);
       
  5423             if (ch.c == QLatin1Char('\t')) {
       
  5424                 start = j+1;
       
  5425                 break;
       
  5426             }
       
  5427             if(ch.whiteSpace)
       
  5428                 numSpaces++;
       
  5429         }
       
  5430         int toAdd = 0;
       
  5431         for (int k = start + 1; k <= last; ++k) {
       
  5432             Q3TextStringChar &ch = string->at(k);
       
  5433             if(numSpaces && ch.whiteSpace) {
       
  5434                 int s = space / numSpaces;
       
  5435                 toAdd += s;
       
  5436                 space -= s;
       
  5437                 numSpaces--;
       
  5438             }
       
  5439             string->at(k).x += toAdd;
       
  5440         }
       
  5441     }
       
  5442 
       
  5443     if (last >= 0 && last < string->length())
       
  5444         line->w = string->at(last).x + string->width(last);
       
  5445     else
       
  5446         line->w = 0;
       
  5447 
       
  5448     return new QTextLineStart;
       
  5449 }
       
  5450 
       
  5451 #ifndef QT_NO_COMPLEXTEXT
       
  5452 
       
  5453 #ifdef BIDI_DEBUG
       
  5454 QT_BEGIN_INCLUDE_NAMESPACE
       
  5455 #include <iostream>
       
  5456 QT_END_INCLUDE_NAMESPACE
       
  5457 #endif
       
  5458 
       
  5459 // collects one line of the paragraph and transforms it to visual order
       
  5460 QTextLineStart *Q3TextFormatter::bidiReorderLine(Q3TextParagraph * /*parag*/, Q3TextString *text, QTextLineStart *line,
       
  5461                                                         Q3TextStringChar *startChar, Q3TextStringChar *lastChar, int align, int space)
       
  5462 {
       
  5463     // ignore white space at the end of the line.
       
  5464     int endSpaces = 0;
       
  5465     while (lastChar > startChar && lastChar->whiteSpace) {
       
  5466         space += lastChar->format()->width(QLatin1Char(' '));
       
  5467         --lastChar;
       
  5468         ++endSpaces;
       
  5469     }
       
  5470 
       
  5471     int start = (startChar - &text->at(0));
       
  5472     int last = (lastChar - &text->at(0));
       
  5473 
       
  5474     int length = lastChar - startChar + 1;
       
  5475 
       
  5476 
       
  5477     int x = startChar->x;
       
  5478 
       
  5479     unsigned char _levels[256];
       
  5480     int _visual[256];
       
  5481 
       
  5482     unsigned char *levels = _levels;
       
  5483     int *visual = _visual;
       
  5484 
       
  5485     if (length > 255) {
       
  5486         levels = (unsigned char *)malloc(length*sizeof(unsigned char));
       
  5487         visual = (int *)malloc(length*sizeof(int));
       
  5488     }
       
  5489 
       
  5490     //qDebug("bidiReorderLine: length=%d (%d-%d)", length, start, last);
       
  5491 
       
  5492     Q3TextStringChar *ch = startChar;
       
  5493     unsigned char *l = levels;
       
  5494     while (ch <= lastChar) {
       
  5495         //qDebug("  level: %d", ch->bidiLevel);
       
  5496         *(l++) = (ch++)->bidiLevel;
       
  5497     }
       
  5498 
       
  5499     QTextEngine::bidiReorder(length, levels, visual);
       
  5500 
       
  5501     // now construct the reordered string out of the runs...
       
  5502 
       
  5503     int numSpaces = 0;
       
  5504     align = QStyle::visualAlignment(text->isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight, QFlag(align));
       
  5505 
       
  5506     // This is not really correct, but as we can't make the scroll bar move to the left of the origin,
       
  5507     // this ensures all text can be scrolled to and read.
       
  5508     if (space < 0)
       
  5509         space = 0;
       
  5510 
       
  5511     if (align & Qt::AlignHCenter)
       
  5512         x += space/2;
       
  5513     else if (align & Qt::AlignRight)
       
  5514         x += space;
       
  5515     else if (align & Qt::AlignJustify) {
       
  5516         // End at "last-1", the last space ends up with a width of 0
       
  5517         for (int j = last-1; j >= start; --j) {
       
  5518             // Start at last tab, if any.
       
  5519             Q3TextStringChar &ch = text->at(j);
       
  5520             if (ch.c == QLatin1Char('\t')) {
       
  5521                 start = j+1;
       
  5522                 break;
       
  5523             }
       
  5524             if(ch.whiteSpace)
       
  5525                 numSpaces++;
       
  5526         }
       
  5527     }
       
  5528 
       
  5529     int toAdd = 0;
       
  5530     int xorig = x;
       
  5531     Q3TextStringChar *lc = startChar + visual[0];
       
  5532     for (int i = 0; i < length; i++) {
       
  5533         Q3TextStringChar *ch = startChar + visual[i];
       
  5534         if (numSpaces && ch->whiteSpace) {
       
  5535             int s = space / numSpaces;
       
  5536             toAdd += s;
       
  5537             space -= s;
       
  5538             numSpaces--;
       
  5539         }
       
  5540 
       
  5541         if (lc->format() != ch->format() && !ch->c.isSpace()
       
  5542             && lc->format()->font().italic() && !ch->format()->font().italic()) {
       
  5543             int rb = lc->format()->fontMetrics().rightBearing(lc->c);
       
  5544             if (rb < 0)
       
  5545                 x -= rb;
       
  5546         }
       
  5547 
       
  5548         ch->x = x + toAdd;
       
  5549         ch->rightToLeft = ch->bidiLevel % 2;
       
  5550         //qDebug("visual: %d (%p) placed at %d rightToLeft=%d", visual[i], ch, x +toAdd, ch->rightToLeft );
       
  5551         int ww = 0;
       
  5552         if (ch->c.unicode() >= 32 || ch->c == QLatin1Char(QLatin1Char('\t')) || ch->c == QLatin1Char('\n') || ch->isCustom()) {
       
  5553             ww = text->width(start+visual[i]);
       
  5554         } else {
       
  5555             ww = ch->format()->width(QLatin1Char(' '));
       
  5556         }
       
  5557         x += ww;
       
  5558         lc = ch;
       
  5559     }
       
  5560     x += toAdd;
       
  5561 
       
  5562     while (endSpaces--) {
       
  5563         ++lastChar;
       
  5564         int sw = lastChar->format()->width(QLatin1Char(' '));
       
  5565         if (text->isRightToLeft()) {
       
  5566             xorig -= sw;
       
  5567             lastChar->x = xorig;
       
  5568             ch->rightToLeft = true;
       
  5569         } else {
       
  5570             lastChar->x = x;
       
  5571             x += sw;
       
  5572             ch->rightToLeft = false;
       
  5573         }
       
  5574     }
       
  5575 
       
  5576     line->w = x;
       
  5577 
       
  5578     if (length > 255) {
       
  5579         free(levels);
       
  5580         free(visual);
       
  5581     }
       
  5582 
       
  5583     return new QTextLineStart;
       
  5584 }
       
  5585 #endif
       
  5586 
       
  5587 
       
  5588 void Q3TextFormatter::insertLineStart(Q3TextParagraph *parag, int index, QTextLineStart *ls)
       
  5589 {
       
  5590     QMap<int, QTextLineStart*>::Iterator it;
       
  5591     if ((it = parag->lineStartList().find(index)) == parag->lineStartList().end()) {
       
  5592         parag->lineStartList().insert(index, ls);
       
  5593     } else {
       
  5594         delete *it;
       
  5595         parag->lineStartList().erase(it);
       
  5596         parag->lineStartList().insert(index, ls);
       
  5597     }
       
  5598 }
       
  5599 
       
  5600 
       
  5601 /* Standard pagebreak algorithm using Q3TextFlow::adjustFlow. Returns
       
  5602  the shift of the paragraphs bottom line.
       
  5603  */
       
  5604 int Q3TextFormatter::formatVertically(Q3TextDocument* doc, Q3TextParagraph* parag)
       
  5605 {
       
  5606     int oldHeight = parag->rect().height();
       
  5607     QMap<int, QTextLineStart*>& lineStarts = parag->lineStartList();
       
  5608     QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
       
  5609     int h = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
       
  5610     for (; it != lineStarts.end() ; ++it ) {
       
  5611         QTextLineStart * ls = it.value();
       
  5612         ls->y = h;
       
  5613         Q3TextStringChar *c = &parag->string()->at(it.key());
       
  5614 #ifndef QT_NO_TEXTCUSTOMITEM
       
  5615         if (c && c->customItem() && c->customItem()->ownLine()) {
       
  5616             int h = c->customItem()->height;
       
  5617             c->customItem()->pageBreak(parag->rect().y() + ls->y + ls->baseLine - h, doc->flow());
       
  5618             int delta = c->customItem()->height - h;
       
  5619             ls->h += delta;
       
  5620             if (delta)
       
  5621                 parag->setMovedDown(true);
       
  5622         } else
       
  5623 #endif
       
  5624         {
       
  5625 
       
  5626             int shift = doc->flow()->adjustFlow(parag->rect().y() + ls->y, ls->w, ls->h);
       
  5627             ls->y += shift;
       
  5628             if (shift)
       
  5629                 parag->setMovedDown(true);
       
  5630         }
       
  5631         h = ls->y + ls->h;
       
  5632     }
       
  5633     int m = parag->bottomMargin();
       
  5634     if (!parag->next())
       
  5635         m = 0;
       
  5636     else
       
  5637         m = qMax(m, parag->next()->topMargin()) / 2;
       
  5638     h += m;
       
  5639     parag->setHeight(h);
       
  5640     return h - oldHeight;
       
  5641 }
       
  5642 
       
  5643 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  5644 
       
  5645 Q3TextFormatterBreakInWords::Q3TextFormatterBreakInWords()
       
  5646 {
       
  5647 }
       
  5648 
       
  5649 #define SPACE(s) s
       
  5650 
       
  5651 int Q3TextFormatterBreakInWords::format(Q3TextDocument *doc,Q3TextParagraph *parag,
       
  5652                                         int start, const QMap<int, QTextLineStart*> &)
       
  5653 {
       
  5654     // make sure bidi information is correct.
       
  5655     (void)parag->string()->isBidi();
       
  5656 
       
  5657     Q3TextStringChar *c = 0;
       
  5658     Q3TextStringChar *firstChar = 0;
       
  5659     int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
       
  5660     int x = left + (doc ? parag->firstLineMargin() : 0);
       
  5661     int dw = parag->documentVisibleWidth() - (doc ? doc->rightMargin() : 0);
       
  5662     int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
       
  5663     int h = y;
       
  5664     int len = parag->length();
       
  5665     if (doc)
       
  5666         x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 4);
       
  5667     int rm = parag->rightMargin();
       
  5668     int w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
       
  5669     bool fullWidth = true;
       
  5670     int minw = 0;
       
  5671     int wused = 0;
       
  5672     bool wrapEnabled = isWrapEnabled(parag);
       
  5673 
       
  5674     start = 0;    //######### what is the point with start?! (Matthias)
       
  5675     if (start == 0)
       
  5676         c = &parag->string()->at(0);
       
  5677 
       
  5678     int i = start;
       
  5679     QTextLineStart *lineStart = new QTextLineStart(y, y, 0);
       
  5680     insertLineStart(parag, 0, lineStart);
       
  5681 
       
  5682     QPainter *painter = Q3TextFormat::painter();
       
  5683 
       
  5684     int col = 0;
       
  5685     int ww = 0;
       
  5686     QChar lastChr;
       
  5687     int tabBase = left < x ? left : x;
       
  5688     for (; i < len; ++i, ++col) {
       
  5689         if (c)
       
  5690             lastChr = c->c;
       
  5691         c = &parag->string()->at(i);
       
  5692         // ### the lines below should not be needed
       
  5693         if (painter)
       
  5694             c->format()->setPainter(painter);
       
  5695         if (i > 0) {
       
  5696             c->lineStart = 0;
       
  5697         } else {
       
  5698             c->lineStart = 1;
       
  5699             firstChar = c;
       
  5700         }
       
  5701         if (c->c.unicode() >= 32 || c->isCustom()) {
       
  5702             ww = parag->string()->width(i);
       
  5703         } else if (c->c == QLatin1Char('\t')) {
       
  5704             int nx = parag->nextTab(i, x - tabBase) + tabBase;
       
  5705             if (nx < x)
       
  5706                 ww = w - x;
       
  5707             else
       
  5708                 ww = nx - x;
       
  5709         } else {
       
  5710             ww = c->format()->width(QLatin1Char(' '));
       
  5711         }
       
  5712 
       
  5713 #ifndef QT_NO_TEXTCUSTOMITEM
       
  5714         if (c->isCustom() && c->customItem()->ownLine()) {
       
  5715             x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
       
  5716             w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
       
  5717             c->customItem()->resize(w - x);
       
  5718             w = dw;
       
  5719             y += h;
       
  5720             h = c->height();
       
  5721             lineStart = new QTextLineStart(y, h, h);
       
  5722             insertLineStart(parag, i, lineStart);
       
  5723             c->lineStart = 1;
       
  5724             firstChar = c;
       
  5725             x = 0xffffff;
       
  5726             continue;
       
  5727         }
       
  5728 #endif
       
  5729 
       
  5730         if (wrapEnabled &&
       
  5731              ((wrapAtColumn() == -1 && x + ww > w) ||
       
  5732                (wrapAtColumn() != -1 && col >= wrapAtColumn()))) {
       
  5733             x = doc ? parag->document()->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
       
  5734             w = dw;
       
  5735             y += h;
       
  5736             h = c->height();
       
  5737             lineStart = formatLine(parag, parag->string(), lineStart, firstChar, c-1);
       
  5738             lineStart->y = y;
       
  5739             insertLineStart(parag, i, lineStart);
       
  5740             lineStart->baseLine = c->ascent();
       
  5741             lineStart->h = c->height();
       
  5742             c->lineStart = 1;
       
  5743             firstChar = c;
       
  5744             col = 0;
       
  5745             if (wrapAtColumn() != -1)
       
  5746                 minw = qMax(minw, w);
       
  5747         } else if (lineStart) {
       
  5748             lineStart->baseLine = qMax(lineStart->baseLine, c->ascent());
       
  5749             h = qMax(h, c->height());
       
  5750             lineStart->h = h;
       
  5751         }
       
  5752 
       
  5753         c->x = x;
       
  5754         x += ww;
       
  5755         wused = qMax(wused, x);
       
  5756     }
       
  5757 
       
  5758     int m = parag->bottomMargin();
       
  5759     if (!parag->next())
       
  5760         m = 0;
       
  5761     else
       
  5762         m = qMax(m, parag->next()->topMargin()) / 2;
       
  5763     parag->setFullWidth(fullWidth);
       
  5764     y += h + m;
       
  5765     if (doc)
       
  5766         minw += doc->rightMargin();
       
  5767     if (!wrapEnabled)
       
  5768         minw = qMax(minw, wused);
       
  5769 
       
  5770     thisminw = minw;
       
  5771     thiswused = wused;
       
  5772     return y;
       
  5773 }
       
  5774 
       
  5775 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  5776 
       
  5777 Q3TextFormatterBreakWords::Q3TextFormatterBreakWords()
       
  5778 {
       
  5779 }
       
  5780 
       
  5781 #define DO_FLOW(lineStart) do{ if (doc && doc->isPageBreakEnabled()) { \
       
  5782                     int yflow = lineStart->y + parag->rect().y();\
       
  5783                     int shift = doc->flow()->adjustFlow(yflow, dw, lineStart->h); \
       
  5784                     lineStart->y += shift;\
       
  5785                     y += shift;\
       
  5786                 }}while(false)
       
  5787 
       
  5788 int Q3TextFormatterBreakWords::format(Q3TextDocument *doc, Q3TextParagraph *parag,
       
  5789                                       int start, const QMap<int, QTextLineStart*> &)
       
  5790 {
       
  5791     // make sure bidi information is correct.
       
  5792     (void)parag->string()->isBidi();
       
  5793 
       
  5794     Q3TextStringChar *c = 0;
       
  5795     Q3TextStringChar *firstChar = 0;
       
  5796     Q3TextString *string = parag->string();
       
  5797     int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
       
  5798     int x = left + (doc ? parag->firstLineMargin() : 0);
       
  5799     int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
       
  5800     int h = y;
       
  5801     int len = parag->length();
       
  5802     if (doc)
       
  5803         x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 0);
       
  5804     int dw = parag->documentVisibleWidth() - (doc ? (left != x ? 0 : doc->rightMargin()) : 0);
       
  5805 
       
  5806     int curLeft = x;
       
  5807     int rm = parag->rightMargin();
       
  5808     int rdiff = doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 0) : 0;
       
  5809     int w = dw - rdiff;
       
  5810     bool fullWidth = true;
       
  5811     int marg = left + rdiff;
       
  5812     int minw = 0;
       
  5813     int wused = 0;
       
  5814     int tminw = marg;
       
  5815     int linespacing = doc ? parag->lineSpacing() : 0;
       
  5816     bool wrapEnabled = isWrapEnabled(parag);
       
  5817 
       
  5818     start = 0;
       
  5819 
       
  5820     int i = start;
       
  5821     QTextLineStart *lineStart = new QTextLineStart(y, y, 0);
       
  5822     insertLineStart(parag, 0, lineStart);
       
  5823     int lastBreak = -1;
       
  5824     int tmpBaseLine = 0, tmph = 0;
       
  5825     bool lastWasNonInlineCustom = false;
       
  5826 
       
  5827     int align = parag->alignment();
       
  5828     if (align == Qt::AlignAuto && doc && doc->alignment() != Qt::AlignAuto)
       
  5829         align = doc->alignment();
       
  5830 
       
  5831     align &= Qt::AlignHorizontal_Mask;
       
  5832 
       
  5833     // ### hack. The last char in the paragraph is always invisible,
       
  5834     // ### and somehow sometimes has a wrong format. It changes
       
  5835     // ### between // layouting and printing. This corrects some
       
  5836     // ### layouting errors in BiDi mode due to this.
       
  5837     if (len > 1) {
       
  5838         c = &parag->string()->at(len - 1);
       
  5839         if (!c->isAnchor()) {
       
  5840             if (c->format())
       
  5841                 c->format()->removeRef();
       
  5842             c->setFormat(string->at(len - 2).format());
       
  5843             if (c->format())
       
  5844                 c->format()->addRef();
       
  5845         }
       
  5846     }
       
  5847 
       
  5848     c = &parag->string()->at(0);
       
  5849 
       
  5850     QPainter *painter = Q3TextFormat::painter();
       
  5851     int col = 0;
       
  5852     int ww = 0;
       
  5853     QChar lastChr = c->c;
       
  5854     Q3TextFormat *lastFormat = c->format();
       
  5855     int tabBase = left < x ? left : x;
       
  5856     for (; i < len; ++i, ++col) {
       
  5857         if (i) {
       
  5858             c = &parag->string()->at(i-1);
       
  5859             lastChr = c->c;
       
  5860             lastFormat = c->format();
       
  5861         }
       
  5862         bool lastWasOwnLineCustomItem = lastBreak == -2;
       
  5863         bool hadBreakableChar = lastBreak != -1;
       
  5864         bool lastWasHardBreak = lastChr == QChar::LineSeparator;
       
  5865 
       
  5866         // ### next line should not be needed
       
  5867         if (painter)
       
  5868             c->format()->setPainter(painter);
       
  5869         c = &string->at(i);
       
  5870 
       
  5871         if (lastFormat != c->format() && !c->c.isSpace()
       
  5872             && lastFormat->font().italic() && !c->format()->font().italic()) {
       
  5873             int rb = lastFormat->fontMetrics().rightBearing(lastChr);
       
  5874             if (rb < 0)
       
  5875                 x -= rb;
       
  5876         }
       
  5877 
       
  5878         if ((i > 0 && (x > curLeft || ww == 0)) || lastWasNonInlineCustom) {
       
  5879             c->lineStart = 0;
       
  5880         } else {
       
  5881             c->lineStart = 1;
       
  5882             firstChar = c;
       
  5883         }
       
  5884 
       
  5885         // ignore non spacing marks for column count.
       
  5886         if (col != 0 && QChar::category(c->c.unicode()) == QChar::Mark_NonSpacing)
       
  5887             --col;
       
  5888 
       
  5889 #ifndef QT_NO_TEXTCUSTOMITEM
       
  5890         lastWasNonInlineCustom =  (c->isCustom() && c->customItem()->placement() != Q3TextCustomItem::PlaceInline);
       
  5891 #endif
       
  5892 
       
  5893         if (c->c.unicode() >= 32 || c->isCustom()) {
       
  5894             ww = string->width(i);
       
  5895         } else if (c->c == QLatin1Char('\t')) {
       
  5896             if (align == Qt::AlignRight || align == Qt::AlignCenter) {
       
  5897                 // we can not  (yet) do tabs
       
  5898                 ww = c->format()->width(QLatin1Char(' '));
       
  5899             } else {
       
  5900                 int tabx = lastWasHardBreak ? (left + (doc ? parag->firstLineMargin() : 0)) : x;
       
  5901                 int nx = parag->nextTab(i, tabx - tabBase) + tabBase;
       
  5902                 if (nx < tabx) // strrrange...
       
  5903                     ww = 0;
       
  5904                 else
       
  5905                     ww = nx - tabx;
       
  5906             }
       
  5907         } else {
       
  5908             ww = c->format()->width(QLatin1Char(' '));
       
  5909         }
       
  5910 
       
  5911 #ifndef QT_NO_TEXTCUSTOMITEM
       
  5912         Q3TextCustomItem* ci = c->customItem();
       
  5913         if (c->isCustom() && ci->ownLine()) {
       
  5914             QTextLineStart *lineStart2 = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x - ww));
       
  5915             x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
       
  5916             w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
       
  5917             ci->resize(w - x);
       
  5918             if (ci->width < w - x) {
       
  5919                 if (align & Qt::AlignHCenter)
       
  5920                     x = (w - ci->width) / 2;
       
  5921                 else if (align & Qt::AlignRight) {
       
  5922                     x = w - ci->width;
       
  5923                 }
       
  5924             }
       
  5925             c->x = x;
       
  5926             curLeft = x;
       
  5927             if (i == 0 || !isBreakable(string, i-1) ||
       
  5928                  string->at(i - 1).lineStart == 0) {
       
  5929                 y += qMax(h, qMax(tmph, linespacing));
       
  5930                 tmph = c->height();
       
  5931                 h = tmph;
       
  5932                 lineStart = lineStart2;
       
  5933                 lineStart->y = y;
       
  5934                 insertLineStart(parag, i, lineStart);
       
  5935                 c->lineStart = 1;
       
  5936                 firstChar = c;
       
  5937             } else {
       
  5938                 tmph = c->height();
       
  5939                 h = tmph;
       
  5940                 delete lineStart2;
       
  5941             }
       
  5942             lineStart->h = h;
       
  5943             lineStart->baseLine = h;
       
  5944             tmpBaseLine = lineStart->baseLine;
       
  5945             lastBreak = -2;
       
  5946             x = w;
       
  5947             minw = qMax(minw, tminw);
       
  5948 
       
  5949             int tw = ci->minimumWidth() + (doc ? doc->leftMargin() : 0);
       
  5950             if (tw < QWIDGETSIZE_MAX)
       
  5951                 tminw = tw;
       
  5952             else
       
  5953                 tminw = marg;
       
  5954             wused = qMax(wused, ci->width);
       
  5955             continue;
       
  5956         } else if (c->isCustom() && ci->placement() != Q3TextCustomItem::PlaceInline) {
       
  5957             int tw = ci->minimumWidth();
       
  5958             if (tw < QWIDGETSIZE_MAX)
       
  5959                 minw = qMax(minw, tw);
       
  5960         }
       
  5961 #endif
       
  5962         // we break if
       
  5963         // 1. the last character was a hard break (QChar::LineSeparator) or
       
  5964         // 2. the last character was a own-line custom item (eg. table or ruler) or
       
  5965         // 3. wrapping was enabled, it was not a space and following
       
  5966         // condition is true: We either had a breakable character
       
  5967         // previously or we ar allowed to break in words and - either
       
  5968         // we break at w pixels and the current char would exceed that
       
  5969         // or - we break at a column and the current character would
       
  5970         // exceed that.
       
  5971         if (lastWasHardBreak || lastWasOwnLineCustomItem ||
       
  5972              (wrapEnabled &&
       
  5973                ((!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) &&
       
  5974                   ((wrapAtColumn() == -1 && x + ww > w) ||
       
  5975                     (wrapAtColumn() != -1 && col >= wrapAtColumn()))))
       
  5976               )
       
  5977             ) {
       
  5978             if (wrapAtColumn() != -1)
       
  5979                 minw = qMax(minw, x + ww);
       
  5980             // if a break was forced (no breakable char, hard break or own line custom item), break immediately....
       
  5981             if (!hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem) {
       
  5982                 if (lineStart) {
       
  5983                     lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
       
  5984                     h = qMax(h, tmph);
       
  5985                     lineStart->h = h;
       
  5986                     DO_FLOW(lineStart);
       
  5987                 }
       
  5988                 lineStart = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x));
       
  5989                 x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
       
  5990                 w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
       
  5991                 if (!doc && c->c == QLatin1Char('\t')) { // qt_format_text tab handling
       
  5992                     int nx = parag->nextTab(i, x - tabBase) + tabBase;
       
  5993                     if (nx < x)
       
  5994                         ww = w - x;
       
  5995                     else
       
  5996                         ww = nx - x;
       
  5997                 }
       
  5998                 curLeft = x;
       
  5999                 y += qMax(h, linespacing);
       
  6000                 tmph = c->height();
       
  6001                 h = 0;
       
  6002                 lineStart->y = y;
       
  6003                 insertLineStart(parag, i, lineStart);
       
  6004                 lineStart->baseLine = c->ascent();
       
  6005                 lineStart->h = c->height();
       
  6006                 c->lineStart = 1;
       
  6007                 firstChar = c;
       
  6008                 tmpBaseLine = lineStart->baseLine;
       
  6009                 lastBreak = -1;
       
  6010                 col = 0;
       
  6011                 if (allowBreakInWords() || lastWasHardBreak) {
       
  6012                     minw = qMax(minw, tminw);
       
  6013                     tminw = marg + ww;
       
  6014                 }
       
  6015             } else { // ... otherwise if we had a breakable char, break there
       
  6016                 DO_FLOW(lineStart);
       
  6017                 c->x = x;
       
  6018                 i = lastBreak;
       
  6019                 lineStart = formatLine(parag, string, lineStart, firstChar, parag->at(lastBreak),align, SPACE(w - string->at(i+1).x));
       
  6020                 x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
       
  6021                 w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
       
  6022                 if (!doc && c->c == QLatin1Char('\t')) { // qt_format_text tab handling
       
  6023                     int nx = parag->nextTab(i, x - tabBase) + tabBase;
       
  6024                     if (nx < x)
       
  6025                         ww = w - x;
       
  6026                     else
       
  6027                         ww = nx - x;
       
  6028                 }
       
  6029                 curLeft = x;
       
  6030                 y += qMax(h, linespacing);
       
  6031                 tmph = c->height();
       
  6032                 h = tmph;
       
  6033                 lineStart->y = y;
       
  6034                 insertLineStart(parag, i + 1, lineStart);
       
  6035                 lineStart->baseLine = c->ascent();
       
  6036                 lineStart->h = c->height();
       
  6037                 c->lineStart = 1;
       
  6038                 firstChar = c;
       
  6039                 tmpBaseLine = lineStart->baseLine;
       
  6040                 lastBreak = -1;
       
  6041                 col = 0;
       
  6042                 minw = qMax(minw, tminw);
       
  6043                 tminw = marg;
       
  6044                 continue;
       
  6045             }
       
  6046         } else if (lineStart && isBreakable(string, i)) {
       
  6047             if (len <= 2 || i < len - 1) {
       
  6048                 tmpBaseLine = qMax(tmpBaseLine, c->ascent());
       
  6049                 tmph = qMax(tmph, c->height());
       
  6050             }
       
  6051             minw = qMax(minw, tminw);
       
  6052 
       
  6053             tminw = marg + ww;
       
  6054             lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
       
  6055             h = qMax(h, tmph);
       
  6056             lineStart->h = h;
       
  6057             if (i < len - 2 || c->c != QLatin1Char(' '))
       
  6058                 lastBreak = i;
       
  6059         } else {
       
  6060             tminw += ww;
       
  6061             int cascent = c->ascent();
       
  6062             int cheight = c->height();
       
  6063             int belowBaseLine = qMax(tmph - tmpBaseLine, cheight-cascent);
       
  6064             tmpBaseLine = qMax(tmpBaseLine, cascent);
       
  6065             tmph = tmpBaseLine + belowBaseLine;
       
  6066         }
       
  6067 
       
  6068         c->x = x;
       
  6069         x += ww;
       
  6070         wused = qMax(wused, x);
       
  6071     }
       
  6072 
       
  6073     if (lineStart) {
       
  6074         lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
       
  6075         h = qMax(h, tmph);
       
  6076         lineStart->h = h;
       
  6077         // last line in a paragraph is not justified
       
  6078         if (align & Qt::AlignJustify) {
       
  6079             align |= Qt::AlignLeft;
       
  6080             align &= ~(Qt::AlignJustify|Qt::AlignAbsolute);
       
  6081         }
       
  6082         DO_FLOW(lineStart);
       
  6083         lineStart = formatLine(parag, string, lineStart, firstChar, c, align, SPACE(w - x));
       
  6084         delete lineStart;
       
  6085     }
       
  6086 
       
  6087     minw = qMax(minw, tminw);
       
  6088     if (doc)
       
  6089         minw += doc->rightMargin();
       
  6090 
       
  6091     int m = parag->bottomMargin();
       
  6092     if (!parag->next())
       
  6093         m = 0;
       
  6094     else
       
  6095         m = qMax(m, parag->next()->topMargin()) / 2;
       
  6096     parag->setFullWidth(fullWidth);
       
  6097     y += qMax(h, linespacing) + m;
       
  6098 
       
  6099     wused += rm;
       
  6100     if (!wrapEnabled || wrapAtColumn() != -1)
       
  6101         minw = qMax(minw, wused);
       
  6102 
       
  6103     // This is the case where we are breaking wherever we darn well please
       
  6104     // in cases like that, the minw should not be the length of the entire
       
  6105     // word, because we necessarily want to show the word on the whole line.
       
  6106     // example: word wrap in iconview
       
  6107     if (allowBreakInWords() && minw > wused)
       
  6108         minw = wused;
       
  6109 
       
  6110     thisminw = minw;
       
  6111     thiswused = wused;
       
  6112     return y;
       
  6113 }
       
  6114 
       
  6115 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  6116 
       
  6117 Q3TextIndent::Q3TextIndent()
       
  6118 {
       
  6119 }
       
  6120 
       
  6121 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  6122 
       
  6123 Q3TextFormatCollection::Q3TextFormatCollection()
       
  6124     : paintdevice(0)
       
  6125 {
       
  6126     defFormat = new Q3TextFormat(QApplication::font(),
       
  6127                                   QApplication::palette().color(QPalette::Active, QPalette::Text));
       
  6128     lastFormat = cres = 0;
       
  6129     cflags = -1;
       
  6130     cachedFormat = 0;
       
  6131 }
       
  6132 
       
  6133 Q3TextFormatCollection::~Q3TextFormatCollection()
       
  6134 {
       
  6135     QHash<QString, Q3TextFormat *>::ConstIterator it = cKey.constBegin();
       
  6136     while (it != cKey.constEnd()) {
       
  6137         delete it.value();
       
  6138         ++it;
       
  6139     }
       
  6140     delete defFormat;
       
  6141 }
       
  6142 
       
  6143 void Q3TextFormatCollection::setPaintDevice(QPaintDevice *pd)
       
  6144 {
       
  6145     paintdevice = pd;
       
  6146 
       
  6147 #if defined(Q_WS_X11)
       
  6148     int scr = (paintdevice) ? paintdevice->x11Screen() : QX11Info::appScreen();
       
  6149 
       
  6150     defFormat->fn.x11SetScreen(scr);
       
  6151     defFormat->update();
       
  6152 
       
  6153     QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin();
       
  6154     for (; it != cKey.end(); ++it) {
       
  6155         Q3TextFormat *format = *it;
       
  6156         format->fn.x11SetScreen(scr);
       
  6157         format->update();
       
  6158     }
       
  6159 #endif // Q_WS_X11
       
  6160 }
       
  6161 
       
  6162 Q3TextFormat *Q3TextFormatCollection::format(Q3TextFormat *f)
       
  6163 {
       
  6164     if (f->parent() == this || f == defFormat) {
       
  6165         lastFormat = f;
       
  6166         lastFormat->addRef();
       
  6167         return lastFormat;
       
  6168     }
       
  6169 
       
  6170     if (f == lastFormat || (lastFormat && f->key() == lastFormat->key())) {
       
  6171         lastFormat->addRef();
       
  6172         return lastFormat;
       
  6173     }
       
  6174 
       
  6175     Q3TextFormat *fm = cKey.value(f->key());
       
  6176     if (fm) {
       
  6177         lastFormat = fm;
       
  6178         lastFormat->addRef();
       
  6179         return lastFormat;
       
  6180     }
       
  6181 
       
  6182     if (f->key() == defFormat->key())
       
  6183         return defFormat;
       
  6184 
       
  6185     lastFormat = createFormat(*f);
       
  6186     lastFormat->collection = this;
       
  6187     cKey.insert(lastFormat->key(), lastFormat);
       
  6188     return lastFormat;
       
  6189 }
       
  6190 
       
  6191 Q3TextFormat *Q3TextFormatCollection::format(Q3TextFormat *of, Q3TextFormat *nf, int flags)
       
  6192 {
       
  6193     if (cres && kof == of->key() && knf == nf->key() && cflags == flags) {
       
  6194         cres->addRef();
       
  6195         return cres;
       
  6196     }
       
  6197 
       
  6198     cres = createFormat(*of);
       
  6199     kof = of->key();
       
  6200     knf = nf->key();
       
  6201     cflags = flags;
       
  6202     if (flags & Q3TextFormat::Bold)
       
  6203         cres->fn.setBold(nf->fn.bold());
       
  6204     if (flags & Q3TextFormat::Italic)
       
  6205         cres->fn.setItalic(nf->fn.italic());
       
  6206     if (flags & Q3TextFormat::Underline)
       
  6207         cres->fn.setUnderline(nf->fn.underline());
       
  6208     if (flags & Q3TextFormat::StrikeOut)
       
  6209         cres->fn.setStrikeOut(nf->fn.strikeOut());
       
  6210     if (flags & Q3TextFormat::Family)
       
  6211         cres->fn.setFamily(nf->fn.family());
       
  6212     if (flags & Q3TextFormat::Size) {
       
  6213         if (of->usePixelSizes)
       
  6214             cres->fn.setPixelSize(nf->fn.pixelSize());
       
  6215         else
       
  6216             cres->fn.setPointSize(nf->fn.pointSize());
       
  6217     }
       
  6218     if (flags & Q3TextFormat::Color)
       
  6219         cres->col = nf->col;
       
  6220     if (flags & Q3TextFormat::Misspelled)
       
  6221         cres->missp = nf->missp;
       
  6222     if (flags & Q3TextFormat::VAlign)
       
  6223         cres->ha = nf->ha;
       
  6224     cres->update();
       
  6225 
       
  6226     Q3TextFormat *fm = cKey.value(cres->key());
       
  6227     if (!fm) {
       
  6228         cres->collection = this;
       
  6229         cKey.insert(cres->key(), cres);
       
  6230     } else {
       
  6231         delete cres;
       
  6232         cres = fm;
       
  6233         cres->addRef();
       
  6234     }
       
  6235 
       
  6236     return cres;
       
  6237 }
       
  6238 
       
  6239 Q3TextFormat *Q3TextFormatCollection::format(const QFont &f, const QColor &c)
       
  6240 {
       
  6241     if (cachedFormat && cfont == f && ccol == c) {
       
  6242         cachedFormat->addRef();
       
  6243         return cachedFormat;
       
  6244     }
       
  6245 
       
  6246     QString key = Q3TextFormat::getKey(f, c, false, Q3TextFormat::AlignNormal);
       
  6247     cachedFormat = cKey.value(key);
       
  6248     cfont = f;
       
  6249     ccol = c;
       
  6250 
       
  6251     if (cachedFormat) {
       
  6252         cachedFormat->addRef();
       
  6253         return cachedFormat;
       
  6254     }
       
  6255 
       
  6256     if (key == defFormat->key())
       
  6257         return defFormat;
       
  6258 
       
  6259     cachedFormat = createFormat(f, c);
       
  6260     cachedFormat->collection = this;
       
  6261     cKey.insert(cachedFormat->key(), cachedFormat);
       
  6262     if (cachedFormat->key() != key)
       
  6263         qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1());
       
  6264     return cachedFormat;
       
  6265 }
       
  6266 
       
  6267 void Q3TextFormatCollection::remove(Q3TextFormat *f)
       
  6268 {
       
  6269     if (lastFormat == f)
       
  6270         lastFormat = 0;
       
  6271     if (cres == f)
       
  6272         cres = 0;
       
  6273     if (cachedFormat == f)
       
  6274         cachedFormat = 0;
       
  6275     if (cKey.value(f->key()) == f)
       
  6276         delete cKey.take(f->key());
       
  6277 }
       
  6278 
       
  6279 #define UPDATE(up, lo, rest) \
       
  6280         if (font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest()) \
       
  6281             fm->fn.set##up##rest(font.lo##rest())
       
  6282 
       
  6283 void Q3TextFormatCollection::updateDefaultFormat(const QFont &font, const QColor &color, Q3StyleSheet *sheet)
       
  6284 {
       
  6285     bool usePixels = font.pointSize() == -1;
       
  6286     bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() :
       
  6287         font.pointSize() != defFormat->fn.pointSize();
       
  6288     int base = usePixels ? font.pixelSize() : font.pointSize();
       
  6289     QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin();
       
  6290     for (; it != cKey.end(); ++it) {
       
  6291         Q3TextFormat *fm = *it;
       
  6292         UPDATE(F, f, amily);
       
  6293         UPDATE(W, w, eight);
       
  6294         UPDATE(B, b, old);
       
  6295         UPDATE(I, i, talic);
       
  6296         UPDATE(U, u, nderline);
       
  6297         if (changeSize) {
       
  6298             fm->stdSize = base;
       
  6299             fm->usePixelSizes = usePixels;
       
  6300             if (usePixels)
       
  6301                 fm->fn.setPixelSize(fm->stdSize);
       
  6302             else
       
  6303                 fm->fn.setPointSize(fm->stdSize);
       
  6304             sheet->scaleFont(fm->fn, fm->logicalFontSize);
       
  6305         }
       
  6306         if (color.isValid() && color != defFormat->col && fm->col == defFormat->col)
       
  6307             fm->col = color;
       
  6308         fm->update();
       
  6309     }
       
  6310 
       
  6311     defFormat->fn = font;
       
  6312     defFormat->col = color;
       
  6313     defFormat->update();
       
  6314     defFormat->stdSize = base;
       
  6315     defFormat->usePixelSizes = usePixels;
       
  6316 
       
  6317     updateKeys();
       
  6318 }
       
  6319 
       
  6320 // the keys in cKey have changed, rebuild the hashtable
       
  6321 void Q3TextFormatCollection::updateKeys()
       
  6322 {
       
  6323     if (cKey.isEmpty())
       
  6324         return;
       
  6325     Q3TextFormat** formats = new Q3TextFormat *[cKey.count() + 1];
       
  6326     Q3TextFormat **f = formats;
       
  6327     for (QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin(); it != cKey.end(); ++it, ++f)
       
  6328         *f = *it;
       
  6329     *f = 0;
       
  6330     cKey.clear();
       
  6331     for (f = formats; *f; f++)
       
  6332         cKey.insert((*f)->key(), *f);
       
  6333     delete [] formats;
       
  6334 }
       
  6335 
       
  6336 
       
  6337 
       
  6338 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  6339 
       
  6340 void Q3TextFormat::setBold(bool b)
       
  6341 {
       
  6342     if (b == fn.bold())
       
  6343         return;
       
  6344     fn.setBold(b);
       
  6345     update();
       
  6346 }
       
  6347 
       
  6348 void Q3TextFormat::setMisspelled(bool b)
       
  6349 {
       
  6350     if (b == (bool)missp)
       
  6351         return;
       
  6352     missp = b;
       
  6353     update();
       
  6354 }
       
  6355 
       
  6356 void Q3TextFormat::setVAlign(VerticalAlignment a)
       
  6357 {
       
  6358     if (a == ha)
       
  6359         return;
       
  6360     ha = a;
       
  6361     update();
       
  6362 }
       
  6363 
       
  6364 void Q3TextFormat::setItalic(bool b)
       
  6365 {
       
  6366     if (b == fn.italic())
       
  6367         return;
       
  6368     fn.setItalic(b);
       
  6369     update();
       
  6370 }
       
  6371 
       
  6372 void Q3TextFormat::setUnderline(bool b)
       
  6373 {
       
  6374     if (b == fn.underline())
       
  6375         return;
       
  6376     fn.setUnderline(b);
       
  6377     update();
       
  6378 }
       
  6379 
       
  6380 void Q3TextFormat::setStrikeOut(bool b)
       
  6381 {
       
  6382     if (b == fn.strikeOut())
       
  6383         return;
       
  6384     fn.setStrikeOut(b);
       
  6385     update();
       
  6386 }
       
  6387 
       
  6388 void Q3TextFormat::setFamily(const QString &f)
       
  6389 {
       
  6390     if (f == fn.family())
       
  6391         return;
       
  6392     fn.setFamily(f);
       
  6393     update();
       
  6394 }
       
  6395 
       
  6396 void Q3TextFormat::setPointSize(int s)
       
  6397 {
       
  6398     if (s == fn.pointSize())
       
  6399         return;
       
  6400     fn.setPointSize(s);
       
  6401     usePixelSizes = false;
       
  6402     update();
       
  6403 }
       
  6404 
       
  6405 void Q3TextFormat::setFont(const QFont &f)
       
  6406 {
       
  6407     if (f == fn && !k.isEmpty())
       
  6408         return;
       
  6409     fn = f;
       
  6410     update();
       
  6411 }
       
  6412 
       
  6413 void Q3TextFormat::setColor(const QColor &c)
       
  6414 {
       
  6415     if (c == col)
       
  6416         return;
       
  6417     col = c;
       
  6418     update();
       
  6419 }
       
  6420 
       
  6421 QString Q3TextFormat::makeFormatChangeTags(Q3TextFormat* defaultFormat, Q3TextFormat *f,
       
  6422                            const QString& oldAnchorHref, const QString& anchorHref ) const
       
  6423 {
       
  6424     QString tag;
       
  6425     if (f)
       
  6426         tag += f->makeFormatEndTags(defaultFormat, oldAnchorHref);
       
  6427 
       
  6428     if (!anchorHref.isEmpty())
       
  6429         tag += QLatin1String("<a href=\"") + anchorHref + QLatin1String("\">");
       
  6430 
       
  6431     if (font() != defaultFormat->font()
       
  6432         || vAlign() != defaultFormat->vAlign()
       
  6433         || color().rgb() != defaultFormat->color().rgb()) {
       
  6434         QString s;
       
  6435         if (font().family() != defaultFormat->font().family())
       
  6436             s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-family:") + fn.family();
       
  6437         if (font().italic() && font().italic() != defaultFormat->font().italic())
       
  6438             s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-style:") + (font().italic() ? QLatin1String("italic") : QLatin1String("normal"));
       
  6439         if (font().pointSize() != defaultFormat->font().pointSize())
       
  6440             s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-size:") + QString::number(fn.pointSize()) + QLatin1String("pt");
       
  6441         if (font().weight() != defaultFormat->font().weight())
       
  6442             s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-weight:") + QString::number(fn.weight() * 8);
       
  6443         QString textDecoration;
       
  6444         bool none = false;
       
  6445  	if ( font().underline() != defaultFormat->font().underline() ) {
       
  6446             if (font().underline())
       
  6447                 textDecoration = QLatin1String("underline");
       
  6448             else
       
  6449                 none = true;
       
  6450         }
       
  6451  	if ( font().overline() != defaultFormat->font().overline() ) {
       
  6452             if (font().overline())
       
  6453                 textDecoration += QLatin1String(" overline");
       
  6454             else
       
  6455                 none = true;
       
  6456         }
       
  6457 	if ( font().strikeOut() != defaultFormat->font().strikeOut() ) {
       
  6458             if (font().strikeOut())
       
  6459                 textDecoration += QLatin1String(" line-through");
       
  6460             else
       
  6461                 none = true;
       
  6462         }
       
  6463         if (none && textDecoration.isEmpty())
       
  6464             textDecoration = QLatin1String("none");
       
  6465         if (!textDecoration.isEmpty())
       
  6466  	    s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("text-decoration:") + textDecoration;
       
  6467         if (vAlign() != defaultFormat->vAlign()) {
       
  6468             s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("vertical-align:");
       
  6469             if (vAlign() == Q3TextFormat::AlignSuperScript)
       
  6470                 s += QLatin1String("super");
       
  6471             else if (vAlign() == Q3TextFormat::AlignSubScript)
       
  6472                 s += QLatin1String("sub");
       
  6473             else
       
  6474                 s += QLatin1String("normal");
       
  6475         }
       
  6476         if (color().rgb() != defaultFormat->color().rgb())
       
  6477             s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("color:") + col.name();
       
  6478         if (!s.isEmpty())
       
  6479             tag += QLatin1String("<span style=\"") + s + QLatin1String("\">");
       
  6480     }
       
  6481 
       
  6482     return tag;
       
  6483 }
       
  6484 
       
  6485 QString Q3TextFormat::makeFormatEndTags(Q3TextFormat* defaultFormat, const QString& anchorHref) const
       
  6486 {
       
  6487     QString tag;
       
  6488     if (font().family() != defaultFormat->font().family()
       
  6489          || font().pointSize() != defaultFormat->font().pointSize()
       
  6490          || font().weight() != defaultFormat->font().weight()
       
  6491          || font().italic() != defaultFormat->font().italic()
       
  6492          || font().underline() != defaultFormat->font().underline()
       
  6493          || font().strikeOut() != defaultFormat->font().strikeOut()
       
  6494          || vAlign() != defaultFormat->vAlign()
       
  6495          || color().rgb() != defaultFormat->color().rgb())
       
  6496         tag += QLatin1String("</span>");
       
  6497     if (!anchorHref.isEmpty())
       
  6498         tag += QLatin1String("</a>");
       
  6499     return tag;
       
  6500 }
       
  6501 
       
  6502 Q3TextFormat Q3TextFormat::makeTextFormat(const Q3StyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor) const
       
  6503 {
       
  6504     Q3TextFormat format(*this);
       
  6505     if (!style)
       
  6506         return format;
       
  6507 
       
  6508     if (!style->isAnchor() && style->color().isValid()) {
       
  6509         // the style is not an anchor and defines a color.
       
  6510         // It might be used inside an anchor and it should
       
  6511         // override the link color.
       
  6512         format.linkColor = false;
       
  6513     }
       
  6514     switch (style->verticalAlignment()) {
       
  6515     case Q3StyleSheetItem::VAlignBaseline:
       
  6516         format.setVAlign(Q3TextFormat::AlignNormal);
       
  6517         break;
       
  6518     case Q3StyleSheetItem::VAlignSuper:
       
  6519         format.setVAlign(Q3TextFormat::AlignSuperScript);
       
  6520         break;
       
  6521     case Q3StyleSheetItem::VAlignSub:
       
  6522         format.setVAlign(Q3TextFormat::AlignSubScript);
       
  6523         break;
       
  6524     }
       
  6525 
       
  6526     if (style->fontWeight() != Q3StyleSheetItem::Undefined)
       
  6527         format.fn.setWeight(style->fontWeight());
       
  6528     if (style->fontSize() != Q3StyleSheetItem::Undefined) {
       
  6529         format.fn.setPointSize(style->fontSize());
       
  6530     } else if (style->logicalFontSize() != Q3StyleSheetItem::Undefined) {
       
  6531         format.logicalFontSize = style->logicalFontSize();
       
  6532         if (format.usePixelSizes)
       
  6533             format.fn.setPixelSize(format.stdSize);
       
  6534         else
       
  6535             format.fn.setPointSize(format.stdSize);
       
  6536         style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
       
  6537     } else if (style->logicalFontSizeStep()) {
       
  6538         format.logicalFontSize += style->logicalFontSizeStep();
       
  6539         if (format.usePixelSizes)
       
  6540             format.fn.setPixelSize(format.stdSize);
       
  6541         else
       
  6542             format.fn.setPointSize(format.stdSize);
       
  6543         style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
       
  6544     }
       
  6545     if (!style->fontFamily().isEmpty())
       
  6546         format.fn.setFamily(style->fontFamily());
       
  6547     if (style->color().isValid())
       
  6548         format.col = style->color();
       
  6549     if (style->definesFontItalic())
       
  6550         format.fn.setItalic(style->fontItalic());
       
  6551     if (style->definesFontUnderline())
       
  6552         format.fn.setUnderline(style->fontUnderline());
       
  6553     if (style->definesFontStrikeOut())
       
  6554         format.fn.setStrikeOut(style->fontStrikeOut());
       
  6555 
       
  6556     QMap<QString, QString>::ConstIterator it, end = attr.end();
       
  6557 
       
  6558     if (style->name() == QLatin1String("font")) {
       
  6559         it = attr.find(QLatin1String("color"));
       
  6560         if (it != end && ! (*it).isEmpty()){
       
  6561             format.col.setNamedColor(*it);
       
  6562             format.linkColor = false;
       
  6563         }
       
  6564         it = attr.find(QLatin1String("face"));
       
  6565         if (it != end) {
       
  6566             QString family = (*it).section(QLatin1Char(','), 0, 0);
       
  6567             if (family.size())
       
  6568                 format.fn.setFamily(family);
       
  6569         }
       
  6570         it = attr.find(QLatin1String("size"));
       
  6571         if (it != end) {
       
  6572             QString a = *it;
       
  6573             int n = a.toInt();
       
  6574             if (a[0] == QLatin1Char('+') || a[0] == QLatin1Char('-'))
       
  6575                 n += 3;
       
  6576             format.logicalFontSize = n;
       
  6577             if (format.usePixelSizes)
       
  6578                 format.fn.setPixelSize(format.stdSize);
       
  6579             else
       
  6580                 format.fn.setPointSize(format.stdSize);
       
  6581             style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
       
  6582         }
       
  6583     }
       
  6584 
       
  6585     it = attr.find(QLatin1String("style"));
       
  6586     if (it != end) {
       
  6587         QString a = *it;
       
  6588         int count = a.count(QLatin1Char(';'))+1;
       
  6589         for (int s = 0; s < count; s++) {
       
  6590             QString style = a.section(QLatin1Char(';'), s, s);
       
  6591             if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
       
  6592                 format.logicalFontSize = 0;
       
  6593                 int size = int(scaleFontsFactor * style.mid(10, style.length() - 12).toDouble());
       
  6594                 format.setPointSize(size);
       
  6595             } else if (style.startsWith(QLatin1String("font-style:"))) {
       
  6596                 QString s = style.mid(11).trimmed();
       
  6597                 if (s == QLatin1String("normal"))
       
  6598                     format.fn.setItalic(false);
       
  6599                 else if (s == QLatin1String("italic") || s == QLatin1String("oblique"))
       
  6600                     format.fn.setItalic(true);
       
  6601             } else if (style.startsWith(QLatin1String("font-weight:"))) {
       
  6602                 QString s = style.mid(12);
       
  6603                 bool ok = true;
       
  6604                 int n = s.toInt(&ok);
       
  6605                 if (ok)
       
  6606                     format.fn.setWeight(n/8);
       
  6607             } else if (style.startsWith(QLatin1String("font-family:"))) {
       
  6608                 QString family = style.mid(12).section(QLatin1Char(','),0,0);
       
  6609                 family.replace(QLatin1Char('\"'), QLatin1Char(' '));
       
  6610                 family.replace(QLatin1Char('\''), QLatin1Char(' '));
       
  6611                 family = family.trimmed();
       
  6612                 format.fn.setFamily(family);
       
  6613             } else if (style.startsWith(QLatin1String("text-decoration:"))) {
       
  6614  		QString s = style.mid( 16 );
       
  6615  		format.fn.setOverline(s.contains(QLatin1String("overline")));
       
  6616  		format.fn.setStrikeOut(s.contains(QLatin1String("line-through")));
       
  6617  		format.fn.setUnderline(s.contains(QLatin1String("underline")));
       
  6618             } else if (style.startsWith(QLatin1String("vertical-align:"))) {
       
  6619                 QString s = style.mid(15).trimmed();
       
  6620                 if (s == QLatin1String("sub"))
       
  6621                     format.setVAlign(Q3TextFormat::AlignSubScript);
       
  6622                 else if (s == QLatin1String("super"))
       
  6623                     format.setVAlign(Q3TextFormat::AlignSuperScript);
       
  6624                 else
       
  6625                     format.setVAlign(Q3TextFormat::AlignNormal);
       
  6626             } else if (style.startsWith(QLatin1String("color:"))) {
       
  6627                 format.col.setNamedColor(style.mid(6));
       
  6628                 format.linkColor = false;
       
  6629             }
       
  6630         }
       
  6631     }
       
  6632 
       
  6633     format.update();
       
  6634     return format;
       
  6635 }
       
  6636 
       
  6637 #ifndef QT_NO_TEXTCUSTOMITEM
       
  6638 
       
  6639 struct QPixmapInt
       
  6640 {
       
  6641     QPixmapInt() : ref(0) {}
       
  6642     QPixmap pm;
       
  6643     int            ref;
       
  6644     Q_DUMMY_COMPARISON_OPERATOR(QPixmapInt)
       
  6645 };
       
  6646 
       
  6647 static QMap<QString, QPixmapInt> *pixmap_map = 0;
       
  6648 
       
  6649 Q3TextImage::Q3TextImage(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
       
  6650                         Q3MimeSourceFactory &factory)
       
  6651     : Q3TextCustomItem(p)
       
  6652 {
       
  6653     width = height = 0;
       
  6654 
       
  6655     QMap<QString, QString>::ConstIterator it, end = attr.end();
       
  6656     it = attr.find(QLatin1String("width"));
       
  6657     if (it != end)
       
  6658         width = (*it).toInt();
       
  6659     it = attr.find(QLatin1String("height"));
       
  6660     if (it != end)
       
  6661         height = (*it).toInt();
       
  6662 
       
  6663     reg = 0;
       
  6664     QString imageName = attr[QLatin1String("src")];
       
  6665 
       
  6666     if (imageName.size() == 0)
       
  6667         imageName = attr[QLatin1String("source")];
       
  6668 
       
  6669     if (!imageName.isEmpty()) {
       
  6670         imgId = QString::fromLatin1("%1,%2,%3,%4").arg(imageName).arg(width).arg(height).arg((ulong)&factory);
       
  6671         if (!pixmap_map)
       
  6672             pixmap_map = new QMap<QString, QPixmapInt>;
       
  6673         if (pixmap_map->contains(imgId)) {
       
  6674             QPixmapInt& pmi = pixmap_map->operator[](imgId);
       
  6675             pm = pmi.pm;
       
  6676             pmi.ref++;
       
  6677             width = pm.width();
       
  6678             height = pm.height();
       
  6679         } else {
       
  6680             QImage img;
       
  6681             const QMimeSource* m =
       
  6682                 factory.data(imageName, context);
       
  6683             if (!m) {
       
  6684                 qCritical("Q3TextImage: no mimesource for %s", imageName.latin1());
       
  6685             }
       
  6686             else {
       
  6687                 if (!Q3ImageDrag::decode(m, img)) {
       
  6688                     qCritical("Q3TextImage: cannot decode %s", imageName.latin1());
       
  6689                 }
       
  6690             }
       
  6691 
       
  6692             if (!img.isNull()) {
       
  6693                 if (width == 0) {
       
  6694                     width = img.width();
       
  6695                     if (height != 0) {
       
  6696                         width = img.width() * height / img.height();
       
  6697                     }
       
  6698                 }
       
  6699                 if (height == 0) {
       
  6700                     height = img.height();
       
  6701                     if (width != img.width()) {
       
  6702                         height = img.height() * width / img.width();
       
  6703                     }
       
  6704                 }
       
  6705                 if (img.width() != width || img.height() != height){
       
  6706 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  6707                     img = img.smoothScale(width, height);
       
  6708 #endif
       
  6709                     width = img.width();
       
  6710                     height = img.height();
       
  6711                 }
       
  6712                 pm.convertFromImage(img);
       
  6713             }
       
  6714             if (!pm.isNull()) {
       
  6715                 QPixmapInt& pmi = pixmap_map->operator[](imgId);
       
  6716                 pmi.pm = pm;
       
  6717                 pmi.ref++;
       
  6718             }
       
  6719         }
       
  6720         if (pm.hasAlphaChannel()) {
       
  6721             QRegion mask(pm.mask());
       
  6722             QRegion all(0, 0, pm.width(), pm.height());
       
  6723             reg = new QRegion(all.subtracted(mask));
       
  6724         }
       
  6725     }
       
  6726 
       
  6727     if (pm.isNull() && (width*height)==0)
       
  6728         width = height = 50;
       
  6729 
       
  6730     place = PlaceInline;
       
  6731     if (attr[QLatin1String("align")] == QLatin1String("left"))
       
  6732         place = PlaceLeft;
       
  6733     else if (attr[QLatin1String("align")] == QLatin1String("right"))
       
  6734         place = PlaceRight;
       
  6735 
       
  6736     tmpwidth = width;
       
  6737     tmpheight = height;
       
  6738 
       
  6739     attributes = attr;
       
  6740 }
       
  6741 
       
  6742 Q3TextImage::~Q3TextImage()
       
  6743 {
       
  6744     if (pixmap_map && pixmap_map->contains(imgId)) {
       
  6745         QPixmapInt& pmi = pixmap_map->operator[](imgId);
       
  6746         pmi.ref--;
       
  6747         if (!pmi.ref) {
       
  6748             pixmap_map->remove(imgId);
       
  6749             if (pixmap_map->isEmpty()) {
       
  6750                 delete pixmap_map;
       
  6751                 pixmap_map = 0;
       
  6752             }
       
  6753         }
       
  6754     }
       
  6755     delete reg;
       
  6756 }
       
  6757 
       
  6758 QString Q3TextImage::richText() const
       
  6759 {
       
  6760     QString s;
       
  6761     s += QLatin1String("<img ");
       
  6762     QMap<QString, QString>::ConstIterator it = attributes.begin();
       
  6763     for (; it != attributes.end(); ++it) {
       
  6764         s += it.key() + QLatin1Char('=');
       
  6765         if ((*it).contains(QLatin1Char(' ')))
       
  6766             s += QLatin1Char('\"') + *it + QLatin1String("\" ");
       
  6767         else
       
  6768             s += *it + QLatin1Char(' ');
       
  6769     }
       
  6770     s += QLatin1Char('>');
       
  6771     return s;
       
  6772 }
       
  6773 
       
  6774 void Q3TextImage::adjustToPainter(QPainter* p)
       
  6775 {
       
  6776     width = scale(tmpwidth, p);
       
  6777     height = scale(tmpheight, p);
       
  6778 }
       
  6779 
       
  6780 #if !defined(Q_WS_X11)
       
  6781 static QPixmap *qrt_selection = 0;
       
  6782 static Q3SingleCleanupHandler<QPixmap> qrt_cleanup_pixmap;
       
  6783 static void qrt_createSelectionPixmap(const QPalette &pal)
       
  6784 {
       
  6785     qrt_selection = new QPixmap(2, 2);
       
  6786     qrt_cleanup_pixmap.set(&qrt_selection);
       
  6787     qrt_selection->fill(Qt::color0);
       
  6788     QBitmap m(2, 2);
       
  6789     m.fill(Qt::color1);
       
  6790     QPainter p(&m);
       
  6791     p.setPen(Qt::color0);
       
  6792     for (int j = 0; j < 2; ++j) {
       
  6793         p.drawPoint(j % 2, j);
       
  6794     }
       
  6795     p.end();
       
  6796     qrt_selection->setMask(m);
       
  6797     qrt_selection->fill(pal.highlight().color());
       
  6798 }
       
  6799 #endif
       
  6800 
       
  6801 void Q3TextImage::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
       
  6802                        const QPalette &pal, bool selected)
       
  6803 {
       
  6804     if (placement() != PlaceInline) {
       
  6805         x = xpos;
       
  6806         y = ypos;
       
  6807     }
       
  6808 
       
  6809     if (pm.isNull()) {
       
  6810         p->fillRect(x , y, width, height, pal.dark());
       
  6811         return;
       
  6812     }
       
  6813 
       
  6814     if (is_printer(p)) {
       
  6815         p->drawPixmap(QRect(x, y, width, height), pm);
       
  6816         return;
       
  6817     }
       
  6818 
       
  6819     if (placement() != PlaceInline && !QRect(xpos, ypos, width, height).intersects(QRect(cx, cy, cw, ch)))
       
  6820         return;
       
  6821 
       
  6822     if (placement() == PlaceInline)
       
  6823         p->drawPixmap(x , y, pm);
       
  6824     else
       
  6825         p->drawPixmap(cx , cy, pm, cx - x, cy - y, cw, ch);
       
  6826 
       
  6827     if (selected && placement() == PlaceInline && is_printer(p)) {
       
  6828 #if defined(Q_WS_X11)
       
  6829         p->fillRect(QRect(QPoint(x, y), pm.size()), QBrush(pal.highlight(),
       
  6830                                                                  Qt::Dense4Pattern));
       
  6831 #else // in WIN32 Qt::Dense4Pattern doesn't work correctly (transparency problem), so work around it
       
  6832         if (!qrt_selection)
       
  6833             qrt_createSelectionPixmap(pal);
       
  6834         p->drawTiledPixmap(x, y, pm.width(), pm.height(), *qrt_selection);
       
  6835 #endif
       
  6836     }
       
  6837 }
       
  6838 
       
  6839 void Q3TextHorizontalLine::adjustToPainter(QPainter* p)
       
  6840 {
       
  6841     height = scale(tmpheight, p);
       
  6842 }
       
  6843 
       
  6844 
       
  6845 Q3TextHorizontalLine::Q3TextHorizontalLine(Q3TextDocument *p, const QMap<QString, QString> &attr,
       
  6846                                           const QString &,
       
  6847                                           Q3MimeSourceFactory &)
       
  6848     : Q3TextCustomItem(p)
       
  6849 {
       
  6850     height = tmpheight = 8;
       
  6851     QMap<QString, QString>::ConstIterator it, end = attr.end();
       
  6852     it = attr.find(QLatin1String("color"));
       
  6853     if (it != end)
       
  6854         color = QColor(*it);
       
  6855     shade = attr.find(QLatin1String("noshade")) == end;
       
  6856 }
       
  6857 
       
  6858 Q3TextHorizontalLine::~Q3TextHorizontalLine()
       
  6859 {
       
  6860 }
       
  6861 
       
  6862 QString Q3TextHorizontalLine::richText() const
       
  6863 {
       
  6864     return QLatin1String("<hr>");
       
  6865 }
       
  6866 
       
  6867 void Q3TextHorizontalLine::draw(QPainter* p, int x, int y, int , int , int , int ,
       
  6868                                 const QPalette& pal, bool selected)
       
  6869 {
       
  6870     QRect r(x, y, width, height);
       
  6871     if (is_printer(p) || !shade) {
       
  6872         QPen oldPen = p->pen();
       
  6873         if (!color.isValid())
       
  6874             p->setPen(QPen(pal.text().color(), is_printer(p) ? height/8 : qMax(2, height/4)));
       
  6875         else
       
  6876             p->setPen(QPen(color, is_printer(p) ? height/8 : qMax(2, height/4)));
       
  6877         p->drawLine(r.left()-1, y + height / 2, r.right() + 1, y + height / 2);
       
  6878         p->setPen(oldPen);
       
  6879     } else {
       
  6880         if (selected)
       
  6881             p->fillRect(r, pal.highlight());
       
  6882         QPalette pal2(pal);
       
  6883         if (color.isValid())
       
  6884             pal2.setColor(pal2.currentColorGroup(), QPalette::Dark, color);
       
  6885         qDrawShadeLine(p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, pal2,
       
  6886                         true, height / 8);
       
  6887     }
       
  6888 }
       
  6889 #endif //QT_NO_TEXTCUSTOMITEM
       
  6890 
       
  6891 /*****************************************************************/
       
  6892 // Small set of utility functions to make the parser a bit simpler
       
  6893 //
       
  6894 
       
  6895 bool Q3TextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c)
       
  6896 {
       
  6897     if (pos + 1 > length)
       
  6898         return false;
       
  6899     return doc[pos].toLower() == c.toLower();
       
  6900 }
       
  6901 
       
  6902 bool Q3TextDocument::hasPrefix(const QChar* doc, int length, int pos, const QString& s)
       
  6903 {
       
  6904     if (pos + (int) s.length() > length)
       
  6905         return false;
       
  6906     for (int i = 0; i < (int)s.length(); i++) {
       
  6907         if (doc[pos + i].toLower() != s[i].toLower())
       
  6908             return false;
       
  6909     }
       
  6910     return true;
       
  6911 }
       
  6912 
       
  6913 #ifndef QT_NO_TEXTCUSTOMITEM
       
  6914 static bool qt_is_cell_in_use(QList<Q3TextTableCell *>& cells, int row, int col)
       
  6915 {
       
  6916     for (int idx = 0; idx < cells.size(); ++idx) {
       
  6917         Q3TextTableCell *c = cells.at(idx);
       
  6918         if (row >= c->row() && row < c->row() + c->rowspan()
       
  6919              && col >= c->column() && col < c->column() + c->colspan())
       
  6920             return true;
       
  6921     }
       
  6922     return false;
       
  6923 }
       
  6924 
       
  6925 Q3TextCustomItem* Q3TextDocument::parseTable(const QMap<QString, QString> &attr, const Q3TextFormat &fmt,
       
  6926                                             const QChar* doc, int length, int& pos, Q3TextParagraph *curpar)
       
  6927 {
       
  6928 
       
  6929     Q3TextTable* table = new Q3TextTable(this, attr);
       
  6930     int row = -1;
       
  6931     int col = -1;
       
  6932 
       
  6933     QString rowbgcolor;
       
  6934     QString rowalign;
       
  6935     QString tablebgcolor = attr[QLatin1String("bgcolor")];
       
  6936 
       
  6937     QList<Q3TextTableCell *> multicells;
       
  6938 
       
  6939     QString tagname;
       
  6940     (void) eatSpace(doc, length, pos);
       
  6941     while (pos < length) {
       
  6942         if (hasPrefix(doc, length, pos, QLatin1Char('<'))){
       
  6943             if (hasPrefix(doc, length, pos+1, QLatin1Char('/'))) {
       
  6944                 tagname = parseCloseTag(doc, length, pos);
       
  6945                 if (tagname == QLatin1String("table")) {
       
  6946                     return table;
       
  6947                 }
       
  6948             } else {
       
  6949                 QMap<QString, QString> attr2;
       
  6950                 bool emptyTag = false;
       
  6951                 tagname = parseOpenTag(doc, length, pos, attr2, emptyTag);
       
  6952                 if (tagname == QLatin1String("tr")) {
       
  6953                     rowbgcolor = attr2[QLatin1String("bgcolor")];
       
  6954                     rowalign = attr2[QLatin1String("align")];
       
  6955                     row++;
       
  6956                     col = -1;
       
  6957                 }
       
  6958                 else if (tagname == QLatin1String("td") || tagname == QLatin1String("th")) {
       
  6959                     col++;
       
  6960                     while (qt_is_cell_in_use(multicells, row, col)) {
       
  6961                         col++;
       
  6962                     }
       
  6963 
       
  6964                     if (row >= 0 && col >= 0) {
       
  6965                         const Q3StyleSheetItem* s = sheet_->item(tagname);
       
  6966                         if (!attr2.contains(QLatin1String("bgcolor"))) {
       
  6967                             if (!rowbgcolor.isEmpty())
       
  6968                                 attr2[QLatin1String("bgcolor")] = rowbgcolor;
       
  6969                             else if (!tablebgcolor.isEmpty())
       
  6970                                 attr2[QLatin1String("bgcolor")] = tablebgcolor;
       
  6971                         }
       
  6972                         if (!attr2.contains(QLatin1String("align"))) {
       
  6973                             if (!rowalign.isEmpty())
       
  6974                                 attr2[QLatin1String("align")] = rowalign;
       
  6975                         }
       
  6976 
       
  6977                         // extract the cell contents
       
  6978                         int end = pos;
       
  6979                         while (end < length
       
  6980                                 && !hasPrefix(doc, length, end, QLatin1String("</td"))
       
  6981                                 && !hasPrefix(doc, length, end, QLatin1String("<td"))
       
  6982                                 && !hasPrefix(doc, length, end, QLatin1String("</th"))
       
  6983                                 && !hasPrefix(doc, length, end, QLatin1String("<th"))
       
  6984                                 && !hasPrefix(doc, length, end, QLatin1String("<td"))
       
  6985                                 && !hasPrefix(doc, length, end, QLatin1String("</tr"))
       
  6986                                 && !hasPrefix(doc, length, end, QLatin1String("<tr"))
       
  6987                                 && !hasPrefix(doc, length, end, QLatin1String("</table"))) {
       
  6988                             if (hasPrefix(doc, length, end, QLatin1String("<table"))) { // nested table
       
  6989                                 int nested = 1;
       
  6990                                 ++end;
       
  6991                                 while (end < length && nested != 0) {
       
  6992                                     if (hasPrefix(doc, length, end, QLatin1String("</table")))
       
  6993                                         nested--;
       
  6994                                     if (hasPrefix(doc, length, end, QLatin1String("<table")))
       
  6995                                         nested++;
       
  6996                                     end++;
       
  6997                                 }
       
  6998                             }
       
  6999                             end++;
       
  7000                         }
       
  7001                         Q3TextTableCell* cell = new Q3TextTableCell(table, row, col,
       
  7002                                             attr2, s, fmt.makeTextFormat(s, attr2, scaleFontsFactor),
       
  7003                                             contxt, *factory_, sheet_,
       
  7004                                             QString::fromRawData(doc + pos, end - pos));
       
  7005                         cell->richText()->parentPar = curpar;
       
  7006                         if (cell->colspan() > 1 || cell->rowspan() > 1)
       
  7007                             multicells.append(cell);
       
  7008                         col += cell->colspan()-1;
       
  7009                         pos = end;
       
  7010                     }
       
  7011                 }
       
  7012             }
       
  7013 
       
  7014         } else {
       
  7015             ++pos;
       
  7016         }
       
  7017     }
       
  7018     return table;
       
  7019 }
       
  7020 #endif // QT_NO_TEXTCUSTOMITEM
       
  7021 
       
  7022 bool Q3TextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp)
       
  7023 {
       
  7024     int old_pos = pos;
       
  7025     while (pos < length && doc[pos].isSpace() && (includeNbsp || (doc[pos] != QChar(QChar::nbsp))))
       
  7026         pos++;
       
  7027     return old_pos < pos;
       
  7028 }
       
  7029 
       
  7030 bool Q3TextDocument::eat(const QChar* doc, int length, int& pos, QChar c)
       
  7031 {
       
  7032     bool ok = pos < length && doc[pos] == c;
       
  7033     if (ok)
       
  7034         pos++;
       
  7035     return ok;
       
  7036 }
       
  7037 /*****************************************************************/
       
  7038 
       
  7039 struct Entity {
       
  7040     const char * name;
       
  7041     Q_UINT16 code;
       
  7042 };
       
  7043 
       
  7044 static const Entity entitylist [] = {
       
  7045     { "AElig", 0x00c6 },
       
  7046     { "Aacute", 0x00c1 },
       
  7047     { "Acirc", 0x00c2 },
       
  7048     { "Agrave", 0x00c0 },
       
  7049     { "Alpha", 0x0391 },
       
  7050     { "AMP", 38 },
       
  7051     { "Aring", 0x00c5 },
       
  7052     { "Atilde", 0x00c3 },
       
  7053     { "Auml", 0x00c4 },
       
  7054     { "Beta", 0x0392 },
       
  7055     { "Ccedil", 0x00c7 },
       
  7056     { "Chi", 0x03a7 },
       
  7057     { "Dagger", 0x2021 },
       
  7058     { "Delta", 0x0394 },
       
  7059     { "ETH", 0x00d0 },
       
  7060     { "Eacute", 0x00c9 },
       
  7061     { "Ecirc", 0x00ca },
       
  7062     { "Egrave", 0x00c8 },
       
  7063     { "Epsilon", 0x0395 },
       
  7064     { "Eta", 0x0397 },
       
  7065     { "Euml", 0x00cb },
       
  7066     { "Gamma", 0x0393 },
       
  7067     { "GT", 62 },
       
  7068     { "Iacute", 0x00cd },
       
  7069     { "Icirc", 0x00ce },
       
  7070     { "Igrave", 0x00cc },
       
  7071     { "Iota", 0x0399 },
       
  7072     { "Iuml", 0x00cf },
       
  7073     { "Kappa", 0x039a },
       
  7074     { "Lambda", 0x039b },
       
  7075     { "LT", 60 },
       
  7076     { "Mu", 0x039c },
       
  7077     { "Ntilde", 0x00d1 },
       
  7078     { "Nu", 0x039d },
       
  7079     { "OElig", 0x0152 },
       
  7080     { "Oacute", 0x00d3 },
       
  7081     { "Ocirc", 0x00d4 },
       
  7082     { "Ograve", 0x00d2 },
       
  7083     { "Omega", 0x03a9 },
       
  7084     { "Omicron", 0x039f },
       
  7085     { "Oslash", 0x00d8 },
       
  7086     { "Otilde", 0x00d5 },
       
  7087     { "Ouml", 0x00d6 },
       
  7088     { "Phi", 0x03a6 },
       
  7089     { "Pi", 0x03a0 },
       
  7090     { "Prime", 0x2033 },
       
  7091     { "Psi", 0x03a8 },
       
  7092     { "QUOT", 34 },
       
  7093     { "Rho", 0x03a1 },
       
  7094     { "Scaron", 0x0160 },
       
  7095     { "Sigma", 0x03a3 },
       
  7096     { "THORN", 0x00de },
       
  7097     { "Tau", 0x03a4 },
       
  7098     { "Theta", 0x0398 },
       
  7099     { "Uacute", 0x00da },
       
  7100     { "Ucirc", 0x00db },
       
  7101     { "Ugrave", 0x00d9 },
       
  7102     { "Upsilon", 0x03a5 },
       
  7103     { "Uuml", 0x00dc },
       
  7104     { "Xi", 0x039e },
       
  7105     { "Yacute", 0x00dd },
       
  7106     { "Yuml", 0x0178 },
       
  7107     { "Zeta", 0x0396 },
       
  7108     { "aacute", 0x00e1 },
       
  7109     { "acirc", 0x00e2 },
       
  7110     { "acute", 0x00b4 },
       
  7111     { "aelig", 0x00e6 },
       
  7112     { "agrave", 0x00e0 },
       
  7113     { "alefsym", 0x2135 },
       
  7114     { "alpha", 0x03b1 },
       
  7115     { "amp", 38 },
       
  7116     { "and", 0x22a5 },
       
  7117     { "ang", 0x2220 },
       
  7118     { "apos", 0x0027 },
       
  7119     { "aring", 0x00e5 },
       
  7120     { "asymp", 0x2248 },
       
  7121     { "atilde", 0x00e3 },
       
  7122     { "auml", 0x00e4 },
       
  7123     { "bdquo", 0x201e },
       
  7124     { "beta", 0x03b2 },
       
  7125     { "brvbar", 0x00a6 },
       
  7126     { "bull", 0x2022 },
       
  7127     { "cap", 0x2229 },
       
  7128     { "ccedil", 0x00e7 },
       
  7129     { "cedil", 0x00b8 },
       
  7130     { "cent", 0x00a2 },
       
  7131     { "chi", 0x03c7 },
       
  7132     { "circ", 0x02c6 },
       
  7133     { "clubs", 0x2663 },
       
  7134     { "cong", 0x2245 },
       
  7135     { "copy", 0x00a9 },
       
  7136     { "crarr", 0x21b5 },
       
  7137     { "cup", 0x222a },
       
  7138     { "cur" "ren", 0x00a4 },
       
  7139     { "dArr", 0x21d3 },
       
  7140     { "dagger", 0x2020 },
       
  7141     { "darr", 0x2193 },
       
  7142     { "deg", 0x00b0 },
       
  7143     { "delta", 0x03b4 },
       
  7144     { "diams", 0x2666 },
       
  7145     { "divide", 0x00f7 },
       
  7146     { "eacute", 0x00e9 },
       
  7147     { "ecirc", 0x00ea },
       
  7148     { "egrave", 0x00e8 },
       
  7149     { "empty", 0x2205 },
       
  7150     { "emsp", 0x2003 },
       
  7151     { "ensp", 0x2002 },
       
  7152     { "epsilon", 0x03b5 },
       
  7153     { "equiv", 0x2261 },
       
  7154     { "eta", 0x03b7 },
       
  7155     { "eth", 0x00f0 },
       
  7156     { "euml", 0x00eb },
       
  7157     { "euro", 0x20ac },
       
  7158     { "exist", 0x2203 },
       
  7159     { "fnof", 0x0192 },
       
  7160     { "forall", 0x2200 },
       
  7161     { "frac12", 0x00bd },
       
  7162     { "frac14", 0x00bc },
       
  7163     { "frac34", 0x00be },
       
  7164     { "frasl", 0x2044 },
       
  7165     { "gamma", 0x03b3 },
       
  7166     { "ge", 0x2265 },
       
  7167     { "gt", 62 },
       
  7168     { "hArr", 0x21d4 },
       
  7169     { "harr", 0x2194 },
       
  7170     { "hearts", 0x2665 },
       
  7171     { "hellip", 0x2026 },
       
  7172     { "iacute", 0x00ed },
       
  7173     { "icirc", 0x00ee },
       
  7174     { "iexcl", 0x00a1 },
       
  7175     { "igrave", 0x00ec },
       
  7176     { "image", 0x2111 },
       
  7177     { "infin", 0x221e },
       
  7178     { "int", 0x222b },
       
  7179     { "iota", 0x03b9 },
       
  7180     { "iquest", 0x00bf },
       
  7181     { "isin", 0x2208 },
       
  7182     { "iuml", 0x00ef },
       
  7183     { "kappa", 0x03ba },
       
  7184     { "lArr", 0x21d0 },
       
  7185     { "lambda", 0x03bb },
       
  7186     { "lang", 0x2329 },
       
  7187     { "laquo", 0x00ab },
       
  7188     { "larr", 0x2190 },
       
  7189     { "lceil", 0x2308 },
       
  7190     { "ldquo", 0x201c },
       
  7191     { "le", 0x2264 },
       
  7192     { "lfloor", 0x230a },
       
  7193     { "lowast", 0x2217 },
       
  7194     { "loz", 0x25ca },
       
  7195     { "lrm", 0x200e },
       
  7196     { "lsaquo", 0x2039 },
       
  7197     { "lsquo", 0x2018 },
       
  7198     { "lt", 60 },
       
  7199     { "macr", 0x00af },
       
  7200     { "mdash", 0x2014 },
       
  7201     { "micro", 0x00b5 },
       
  7202     { "middot", 0x00b7 },
       
  7203     { "minus", 0x2212 },
       
  7204     { "mu", 0x03bc },
       
  7205     { "nabla", 0x2207 },
       
  7206     { "nbsp", 0x00a0 },
       
  7207     { "ndash", 0x2013 },
       
  7208     { "ne", 0x2260 },
       
  7209     { "ni", 0x220b },
       
  7210     { "not", 0x00ac },
       
  7211     { "notin", 0x2209 },
       
  7212     { "nsub", 0x2284 },
       
  7213     { "ntilde", 0x00f1 },
       
  7214     { "nu", 0x03bd },
       
  7215     { "oacute", 0x00f3 },
       
  7216     { "ocirc", 0x00f4 },
       
  7217     { "oelig", 0x0153 },
       
  7218     { "ograve", 0x00f2 },
       
  7219     { "oline", 0x203e },
       
  7220     { "omega", 0x03c9 },
       
  7221     { "omicron", 0x03bf },
       
  7222     { "oplus", 0x2295 },
       
  7223     { "or", 0x22a6 },
       
  7224     { "ordf", 0x00aa },
       
  7225     { "ordm", 0x00ba },
       
  7226     { "oslash", 0x00f8 },
       
  7227     { "otilde", 0x00f5 },
       
  7228     { "otimes", 0x2297 },
       
  7229     { "ouml", 0x00f6 },
       
  7230     { "para", 0x00b6 },
       
  7231     { "part", 0x2202 },
       
  7232     { "percnt", 0x0025 },
       
  7233     { "permil", 0x2030 },
       
  7234     { "perp", 0x22a5 },
       
  7235     { "phi", 0x03c6 },
       
  7236     { "pi", 0x03c0 },
       
  7237     { "piv", 0x03d6 },
       
  7238     { "plusmn", 0x00b1 },
       
  7239     { "pound", 0x00a3 },
       
  7240     { "prime", 0x2032 },
       
  7241     { "prod", 0x220f },
       
  7242     { "prop", 0x221d },
       
  7243     { "psi", 0x03c8 },
       
  7244     { "quot", 34 },
       
  7245     { "rArr", 0x21d2 },
       
  7246     { "radic", 0x221a },
       
  7247     { "rang", 0x232a },
       
  7248     { "raquo", 0x00bb },
       
  7249     { "rarr", 0x2192 },
       
  7250     { "rceil", 0x2309 },
       
  7251     { "rdquo", 0x201d },
       
  7252     { "real", 0x211c },
       
  7253     { "reg", 0x00ae },
       
  7254     { "rfloor", 0x230b },
       
  7255     { "rho", 0x03c1 },
       
  7256     { "rlm", 0x200f },
       
  7257     { "rsaquo", 0x203a },
       
  7258     { "rsquo", 0x2019 },
       
  7259     { "sbquo", 0x201a },
       
  7260     { "scaron", 0x0161 },
       
  7261     { "sdot", 0x22c5 },
       
  7262     { "sect", 0x00a7 },
       
  7263     { "shy", 0x00ad },
       
  7264     { "sigma", 0x03c3 },
       
  7265     { "sigmaf", 0x03c2 },
       
  7266     { "sim", 0x223c },
       
  7267     { "spades", 0x2660 },
       
  7268     { "sub", 0x2282 },
       
  7269     { "sube", 0x2286 },
       
  7270     { "sum", 0x2211 },
       
  7271     { "sup1", 0x00b9 },
       
  7272     { "sup2", 0x00b2 },
       
  7273     { "sup3", 0x00b3 },
       
  7274     { "sup", 0x2283 },
       
  7275     { "supe", 0x2287 },
       
  7276     { "szlig", 0x00df },
       
  7277     { "tau", 0x03c4 },
       
  7278     { "there4", 0x2234 },
       
  7279     { "theta", 0x03b8 },
       
  7280     { "thetasym", 0x03d1 },
       
  7281     { "thinsp", 0x2009 },
       
  7282     { "thorn", 0x00fe },
       
  7283     { "tilde", 0x02dc },
       
  7284     { "times", 0x00d7 },
       
  7285     { "trade", 0x2122 },
       
  7286     { "uArr", 0x21d1 },
       
  7287     { "uacute", 0x00fa },
       
  7288     { "uarr", 0x2191 },
       
  7289     { "ucirc", 0x00fb },
       
  7290     { "ugrave", 0x00f9 },
       
  7291     { "uml", 0x00a8 },
       
  7292     { "upsih", 0x03d2 },
       
  7293     { "upsilon", 0x03c5 },
       
  7294     { "uuml", 0x00fc },
       
  7295     { "weierp", 0x2118 },
       
  7296     { "xi", 0x03be },
       
  7297     { "yacute", 0x00fd },
       
  7298     { "yen", 0x00a5 },
       
  7299     { "yuml", 0x00ff },
       
  7300     { "zeta", 0x03b6 },
       
  7301     { "zwj", 0x200d },
       
  7302     { "zwnj", 0x200c },
       
  7303     { "", 0x0000 }
       
  7304 };
       
  7305 
       
  7306 
       
  7307 
       
  7308 
       
  7309 
       
  7310 static QMap<QByteArray, QChar> *html_map = 0;
       
  7311 static void qt_cleanup_html_map()
       
  7312 {
       
  7313     delete html_map;
       
  7314     html_map = 0;
       
  7315 }
       
  7316 
       
  7317 static QMap<QByteArray, QChar> *htmlMap()
       
  7318 {
       
  7319     if (!html_map) {
       
  7320         html_map = new QMap<QByteArray, QChar>;
       
  7321         qAddPostRoutine(qt_cleanup_html_map);
       
  7322 
       
  7323         const Entity *ent = entitylist;
       
  7324         while(ent->code) {
       
  7325             html_map->insert(QByteArray(ent->name), QChar(ent->code));
       
  7326             ent++;
       
  7327         }
       
  7328     }
       
  7329     return html_map;
       
  7330 }
       
  7331 
       
  7332 QChar Q3TextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos)
       
  7333 {
       
  7334     QString s;
       
  7335     pos++;
       
  7336     int recoverpos = pos;
       
  7337     while (pos < length && doc[pos] != QLatin1Char(';') && !doc[pos].isSpace() && pos < recoverpos + 8) {
       
  7338         s += doc[pos];
       
  7339         pos++;
       
  7340     }
       
  7341     if (doc[pos] != QLatin1Char(';') && !doc[pos].isSpace()) {
       
  7342         pos = recoverpos;
       
  7343         return QLatin1Char('&');
       
  7344     }
       
  7345     pos++;
       
  7346 
       
  7347     if (s.length() > 1 && s[0] == QLatin1Char('#')) {
       
  7348         int off = 1;
       
  7349         int base = 10;
       
  7350         if (s[1] == QLatin1Char('x')) {
       
  7351             off = 2;
       
  7352             base = 16;
       
  7353         }
       
  7354         bool ok;
       
  7355 	int num = s.mid(off).toInt(&ok, base);
       
  7356         if (num == 151) // ### hack for designer manual
       
  7357             return QLatin1Char('-');
       
  7358         return num;
       
  7359     }
       
  7360 
       
  7361     QMap<QByteArray, QChar>::Iterator it = htmlMap()->find(s.toLatin1());
       
  7362     if (it != htmlMap()->end()) {
       
  7363         return *it;
       
  7364     }
       
  7365 
       
  7366     pos = recoverpos;
       
  7367     return QLatin1Char('&');
       
  7368 }
       
  7369 
       
  7370 QString Q3TextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower)
       
  7371 {
       
  7372     QString s;
       
  7373 
       
  7374     if (doc[pos] == QLatin1Char('"')) {
       
  7375         pos++;
       
  7376         while (pos < length && doc[pos] != QLatin1Char('"')) {
       
  7377             if (doc[pos] == QLatin1Char('&')) {
       
  7378                 s += parseHTMLSpecialChar(doc, length, pos);
       
  7379             } else {
       
  7380                 s += doc[pos];
       
  7381                 pos++;
       
  7382             }
       
  7383         }
       
  7384         eat(doc, length, pos, QLatin1Char('"'));
       
  7385     } else if (doc[pos] == QLatin1Char('\'')) {
       
  7386         pos++;
       
  7387         while (pos < length  && doc[pos] != QLatin1Char('\'')) {
       
  7388             s += doc[pos];
       
  7389             pos++;
       
  7390         }
       
  7391         eat(doc, length, pos, QLatin1Char('\''));
       
  7392     } else {
       
  7393         static QString term = QString::fromLatin1("/>");
       
  7394         while (pos < length
       
  7395                 && doc[pos] != QLatin1Char('>')
       
  7396                 && !hasPrefix(doc, length, pos, term)
       
  7397                 && doc[pos] != QLatin1Char('<')
       
  7398                 && doc[pos] != QLatin1Char('=')
       
  7399                 && !doc[pos].isSpace())
       
  7400         {
       
  7401             if (doc[pos] == QLatin1Char('&')) {
       
  7402                 s += parseHTMLSpecialChar(doc, length, pos);
       
  7403             } else {
       
  7404                 s += doc[pos];
       
  7405                 pos++;
       
  7406             }
       
  7407         }
       
  7408         if (lower)
       
  7409             s = s.toLower();
       
  7410     }
       
  7411     return s;
       
  7412 }
       
  7413 
       
  7414 QChar Q3TextDocument::parseChar(const QChar* doc, int length, int& pos, Q3StyleSheetItem::WhiteSpaceMode wsm)
       
  7415 {
       
  7416     if (pos >=  length)
       
  7417         return QChar::null;
       
  7418 
       
  7419     QChar c = doc[pos++];
       
  7420 
       
  7421     if (c == QLatin1Char('<'))
       
  7422         return QChar::null;
       
  7423 
       
  7424     if (c.isSpace() && c != QChar(QChar::nbsp)) {
       
  7425         if (wsm == Q3StyleSheetItem::WhiteSpacePre) {
       
  7426             if (c == QLatin1Char('\n'))
       
  7427                 return QChar::LineSeparator;
       
  7428             else
       
  7429                 return c;
       
  7430         } else { // non-pre mode: collapse whitespace except nbsp
       
  7431             while (pos< length &&
       
  7432                     doc[pos].isSpace()  && doc[pos] != QChar(QChar::nbsp))
       
  7433                 pos++;
       
  7434             return QLatin1Char(' ');
       
  7435         }
       
  7436     }
       
  7437     else if (c == QLatin1Char('&'))
       
  7438         return parseHTMLSpecialChar(doc, length, --pos);
       
  7439     else
       
  7440         return c;
       
  7441 }
       
  7442 
       
  7443 QString Q3TextDocument::parseOpenTag(const QChar* doc, int length, int& pos,
       
  7444                                   QMap<QString, QString> &attr, bool& emptyTag)
       
  7445 {
       
  7446     emptyTag = false;
       
  7447     pos++;
       
  7448     if (hasPrefix(doc, length, pos, QLatin1Char('!'))) {
       
  7449         if (hasPrefix(doc, length, pos+1, QLatin1String("--"))) {
       
  7450             pos += 3;
       
  7451             // eat comments
       
  7452             QString pref = QString::fromLatin1("-->");
       
  7453             while (!hasPrefix(doc, length, pos, pref) && pos < length)
       
  7454                 pos++;
       
  7455             if (hasPrefix(doc, length, pos, pref)) {
       
  7456                 pos += 3;
       
  7457                 eatSpace(doc, length, pos, true);
       
  7458             }
       
  7459             emptyTag = true;
       
  7460             return QString();
       
  7461         }
       
  7462         else {
       
  7463             // eat strange internal tags
       
  7464             while (!hasPrefix(doc, length, pos, QLatin1Char('>')) && pos < length)
       
  7465                 pos++;
       
  7466             if (hasPrefix(doc, length, pos, QLatin1Char('>'))) {
       
  7467                 pos++;
       
  7468                 eatSpace(doc, length, pos, true);
       
  7469             }
       
  7470             return QString();
       
  7471         }
       
  7472     }
       
  7473 
       
  7474     QString tag = parseWord(doc, length, pos);
       
  7475     eatSpace(doc, length, pos, true);
       
  7476     static QString term = QString::fromLatin1("/>");
       
  7477     static QString s_TRUE = QString::fromLatin1("TRUE");
       
  7478 
       
  7479     while (doc[pos] != QLatin1Char('>') && ! (emptyTag = hasPrefix(doc, length, pos, term))) {
       
  7480         QString key = parseWord(doc, length, pos);
       
  7481         eatSpace(doc, length, pos, true);
       
  7482         if (key.isEmpty()) {
       
  7483             // error recovery
       
  7484             while (pos < length && doc[pos] != QLatin1Char('>'))
       
  7485                 pos++;
       
  7486             break;
       
  7487         }
       
  7488         QString value;
       
  7489         if (hasPrefix(doc, length, pos, QLatin1Char('='))){
       
  7490             pos++;
       
  7491             eatSpace(doc, length, pos);
       
  7492             value = parseWord(doc, length, pos, false);
       
  7493         }
       
  7494         else
       
  7495             value = s_TRUE;
       
  7496         attr.insert(key.toLower(), value);
       
  7497         eatSpace(doc, length, pos, true);
       
  7498     }
       
  7499 
       
  7500     if (emptyTag) {
       
  7501         eat(doc, length, pos, QLatin1Char('/'));
       
  7502         eat(doc, length, pos, QLatin1Char('>'));
       
  7503     }
       
  7504     else
       
  7505         eat(doc, length, pos, QLatin1Char('>'));
       
  7506 
       
  7507     return tag;
       
  7508 }
       
  7509 
       
  7510 QString Q3TextDocument::parseCloseTag(const QChar* doc, int length, int& pos)
       
  7511 {
       
  7512     pos++;
       
  7513     pos++;
       
  7514     QString tag = parseWord(doc, length, pos);
       
  7515     eatSpace(doc, length, pos, true);
       
  7516     eat(doc, length, pos, QLatin1Char('>'));
       
  7517     return tag;
       
  7518 }
       
  7519 
       
  7520 Q3TextFlow::Q3TextFlow()
       
  7521 {
       
  7522     w = pagesize = 0;
       
  7523 }
       
  7524 
       
  7525 Q3TextFlow::~Q3TextFlow()
       
  7526 {
       
  7527     clear();
       
  7528 }
       
  7529 
       
  7530 void Q3TextFlow::clear()
       
  7531 {
       
  7532 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7533     while (!leftItems.isEmpty())
       
  7534         delete leftItems.takeFirst();
       
  7535     while (!rightItems.isEmpty())
       
  7536         delete rightItems.takeFirst();
       
  7537 #endif
       
  7538 }
       
  7539 
       
  7540 void Q3TextFlow::setWidth(int width)
       
  7541 {
       
  7542     w = width;
       
  7543 }
       
  7544 
       
  7545 int Q3TextFlow::adjustLMargin(int yp, int, int margin, int space)
       
  7546 {
       
  7547 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7548     for (int idx = 0; idx < leftItems.size(); ++idx) {
       
  7549         Q3TextCustomItem* item = leftItems.at(idx);
       
  7550         if (item->ypos == -1)
       
  7551             continue;
       
  7552         if (yp >= item->ypos && yp < item->ypos + item->height)
       
  7553             margin = qMax(margin, item->xpos + item->width + space);
       
  7554     }
       
  7555 #endif
       
  7556     return margin;
       
  7557 }
       
  7558 
       
  7559 int Q3TextFlow::adjustRMargin(int yp, int, int margin, int space)
       
  7560 {
       
  7561 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7562     for (int idx = 0; idx < rightItems.size(); ++idx) {
       
  7563         Q3TextCustomItem* item = rightItems.at(idx);
       
  7564         if (item->ypos == -1)
       
  7565             continue;
       
  7566         if (yp >= item->ypos && yp < item->ypos + item->height)
       
  7567             margin = qMax(margin, w - item->xpos - space);
       
  7568     }
       
  7569 #endif
       
  7570     return margin;
       
  7571 }
       
  7572 
       
  7573 
       
  7574 int Q3TextFlow::adjustFlow(int y, int /*w*/, int h)
       
  7575 {
       
  7576     if (pagesize > 0) { // check pages
       
  7577         int yinpage = y % pagesize;
       
  7578         if (yinpage <= border_tolerance)
       
  7579             return border_tolerance - yinpage;
       
  7580         else
       
  7581             if (yinpage + h > pagesize - border_tolerance)
       
  7582                 return (pagesize - yinpage) + border_tolerance;
       
  7583     }
       
  7584     return 0;
       
  7585 }
       
  7586 
       
  7587 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7588 void Q3TextFlow::unregisterFloatingItem(Q3TextCustomItem* item)
       
  7589 {
       
  7590     leftItems.removeAll(item);
       
  7591     rightItems.removeAll(item);
       
  7592 }
       
  7593 
       
  7594 void Q3TextFlow::registerFloatingItem(Q3TextCustomItem* item)
       
  7595 {
       
  7596     if (item->placement() == Q3TextCustomItem::PlaceRight) {
       
  7597         if (!rightItems.contains(item))
       
  7598             rightItems.append(item);
       
  7599     } else if (item->placement() == Q3TextCustomItem::PlaceLeft &&
       
  7600                 !leftItems.contains(item)) {
       
  7601         leftItems.append(item);
       
  7602     }
       
  7603 }
       
  7604 #endif // QT_NO_TEXTCUSTOMITEM
       
  7605 
       
  7606 QRect Q3TextFlow::boundingRect() const
       
  7607 {
       
  7608     QRect br;
       
  7609 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7610     for (int idx = 0; idx < leftItems.size(); ++idx) {
       
  7611         Q3TextCustomItem* item = leftItems.at(idx);
       
  7612         br = br.united(item->geometry());
       
  7613     }
       
  7614     for (int idx = 0; idx < rightItems.size(); ++idx) {
       
  7615         Q3TextCustomItem* item = rightItems.at(idx);
       
  7616         br = br.united(item->geometry());
       
  7617     }
       
  7618 #endif
       
  7619     return br;
       
  7620 }
       
  7621 
       
  7622 
       
  7623 void Q3TextFlow::drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch,
       
  7624                                    const QPalette &pal, bool selected)
       
  7625 {
       
  7626 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7627     for (int idx = 0; idx < leftItems.size(); ++idx) {
       
  7628         Q3TextCustomItem* item = leftItems.at(idx);
       
  7629         if (item->xpos == -1 || item->ypos == -1)
       
  7630             continue;
       
  7631         item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected);
       
  7632     }
       
  7633 
       
  7634     for (int idx = 0; idx < rightItems.size(); ++idx) {
       
  7635         Q3TextCustomItem* item = rightItems.at(idx);
       
  7636         if (item->xpos == -1 || item->ypos == -1)
       
  7637             continue;
       
  7638         item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected);
       
  7639     }
       
  7640 #endif
       
  7641 }
       
  7642 
       
  7643 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       
  7644 
       
  7645 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7646 void Q3TextCustomItem::pageBreak(int /*y*/ , Q3TextFlow* /*flow*/)
       
  7647 {
       
  7648 }
       
  7649 #endif
       
  7650 
       
  7651 #ifndef QT_NO_TEXTCUSTOMITEM
       
  7652 Q3TextTable::Q3TextTable(Q3TextDocument *p, const QMap<QString, QString> & attr )
       
  7653     : Q3TextCustomItem(p)
       
  7654 {
       
  7655     cellspacing = 2;
       
  7656     cellpadding = 1;
       
  7657     border = innerborder = 0;
       
  7658 
       
  7659     QMap<QString, QString>::ConstIterator it, end = attr.end();
       
  7660     it = attr.find(QLatin1String("cellspacing"));
       
  7661     if (it != end)
       
  7662         cellspacing = (*it).toInt();
       
  7663     it = attr.find(QLatin1String("cellpadding"));
       
  7664     if (it != end)
       
  7665         cellpadding = (*it).toInt();
       
  7666     it = attr.find(QLatin1String("border"));
       
  7667     if (it != end) {
       
  7668         if (*it == QLatin1String("TRUE"))
       
  7669             border = 1;
       
  7670         else
       
  7671             border = (*it).toInt();
       
  7672     }
       
  7673     us_b = border;
       
  7674 
       
  7675     innerborder = us_ib = border ? 1 : 0;
       
  7676 
       
  7677     if (border)
       
  7678         cellspacing += 2;
       
  7679 
       
  7680     us_ib = innerborder;
       
  7681     us_cs = cellspacing;
       
  7682     us_cp = cellpadding;
       
  7683     outerborder = cellspacing + border;
       
  7684     us_ob = outerborder;
       
  7685     layout = new QGridLayout(1, 1, cellspacing);
       
  7686 
       
  7687     fixwidth = 0;
       
  7688     stretch = 0;
       
  7689     it = attr.find(QLatin1String("width"));
       
  7690     if (it != end) {
       
  7691         bool b;
       
  7692         QString s(*it);
       
  7693         int w = s.toInt(&b);
       
  7694         if (b) {
       
  7695             fixwidth = w;
       
  7696         } else {
       
  7697             s = s.trimmed();
       
  7698             if (s.length() > 1 && s[(int)s.length()-1] == QLatin1Char('%'))
       
  7699                 stretch = s.left(s.length()-1).toInt();
       
  7700         }
       
  7701     }
       
  7702     us_fixwidth = fixwidth;
       
  7703 
       
  7704     place = PlaceInline;
       
  7705     if (attr[QLatin1String("align")] == QLatin1String("left"))
       
  7706         place = PlaceLeft;
       
  7707     else if (attr[QLatin1String("align")] == QLatin1String("right"))
       
  7708         place = PlaceRight;
       
  7709     cachewidth = 0;
       
  7710     attributes = attr;
       
  7711     pageBreakFor = -1;
       
  7712 }
       
  7713 
       
  7714 Q3TextTable::~Q3TextTable()
       
  7715 {
       
  7716     delete layout;
       
  7717 }
       
  7718 
       
  7719 QString Q3TextTable::richText() const
       
  7720 {
       
  7721     QString s;
       
  7722     s = QLatin1String("<table ");
       
  7723     QMap<QString, QString>::ConstIterator it = attributes.begin();
       
  7724     for (; it != attributes.end(); ++it)
       
  7725         s += it.key() + QLatin1Char('=') + *it + QLatin1Char(' ');
       
  7726     s += QLatin1String(">\n");
       
  7727 
       
  7728     int lastRow = -1;
       
  7729     bool needEnd = false;
       
  7730     for (int idx = 0; idx < cells.size(); ++idx) {
       
  7731         Q3TextTableCell *cell = cells.at(idx);
       
  7732         if (lastRow != cell->row()) {
       
  7733             if (lastRow != -1)
       
  7734                 s += QLatin1String("</tr>\n");
       
  7735             s += QLatin1String("<tr>");
       
  7736             lastRow = cell->row();
       
  7737             needEnd = true;
       
  7738         }
       
  7739         s += QLatin1String("<td");
       
  7740         it = cell->attributes.constBegin();
       
  7741         for (; it != cell->attributes.constEnd(); ++it)
       
  7742             s += QLatin1Char(' ') + it.key() + QLatin1Char('=') + *it;
       
  7743         s += QLatin1Char('>');
       
  7744         s += cell->richText()->richText();
       
  7745         s += QLatin1String("</td>");
       
  7746     }
       
  7747     if (needEnd)
       
  7748         s += QLatin1String("</tr>\n");
       
  7749     s += QLatin1String("</table>\n");
       
  7750     return s;
       
  7751 }
       
  7752 
       
  7753 void Q3TextTable::adjustToPainter(QPainter* p)
       
  7754 {
       
  7755     cellspacing = scale(us_cs, p);
       
  7756     cellpadding = scale(us_cp, p);
       
  7757     border = scale(us_b , p);
       
  7758     innerborder = scale(us_ib, p);
       
  7759     outerborder = scale(us_ob ,p);
       
  7760     fixwidth = scale( us_fixwidth, p);
       
  7761     width = 0;
       
  7762     cachewidth = 0;
       
  7763     for (int idx = 0; idx < cells.size(); ++idx) {
       
  7764         Q3TextTableCell *cell = cells.at(idx);
       
  7765         cell->adjustToPainter(p);
       
  7766     }
       
  7767 }
       
  7768 
       
  7769 void Q3TextTable::adjustCells(int y , int shift)
       
  7770 {
       
  7771     bool enlarge = false;
       
  7772     for (int idx = 0; idx < cells.size(); ++idx) {
       
  7773         Q3TextTableCell *cell = cells.at(idx);
       
  7774         QRect r = cell->geometry();
       
  7775         if (y <= r.top()) {
       
  7776             r.moveBy(0, shift);
       
  7777             cell->setGeometry(r);
       
  7778             enlarge = true;
       
  7779         } else if (y <= r.bottom()) {
       
  7780             r.rBottom() += shift;
       
  7781             cell->setGeometry(r);
       
  7782             enlarge = true;
       
  7783         }
       
  7784     }
       
  7785     if (enlarge)
       
  7786         height += shift;
       
  7787 }
       
  7788 
       
  7789 void Q3TextTable::pageBreak(int  yt, Q3TextFlow* flow)
       
  7790 {
       
  7791     if (flow->pageSize() <= 0)
       
  7792         return;
       
  7793     if (layout && pageBreakFor > 0 && pageBreakFor != yt) {
       
  7794         layout->invalidate();
       
  7795         int h = layout->heightForWidth(width-2*outerborder);
       
  7796         layout->setGeometry(QRect(0, 0, width-2*outerborder, h) );
       
  7797         height = layout->geometry().height()+2*outerborder;
       
  7798     }
       
  7799     pageBreakFor = yt;
       
  7800     for (int idx = 0; idx < cells.size(); ++idx) {
       
  7801         Q3TextTableCell *cell = cells.at(idx);
       
  7802         int y = yt + outerborder + cell->geometry().y();
       
  7803         int shift = flow->adjustFlow(y - cellspacing, width, cell->richText()->height() + 2*cellspacing);
       
  7804         adjustCells(y - outerborder - yt, shift);
       
  7805     }
       
  7806 }
       
  7807 
       
  7808 
       
  7809 void Q3TextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
       
  7810                       const QPalette &pal, bool selected)
       
  7811 {
       
  7812     if (placement() != PlaceInline) {
       
  7813         x = xpos;
       
  7814         y = ypos;
       
  7815     }
       
  7816 
       
  7817     for (int idx = 0; idx < cells.size(); ++idx) {
       
  7818         Q3TextTableCell *cell = cells.at(idx);
       
  7819         if ((cx < 0 && cy < 0) ||
       
  7820              QRect(cx, cy, cw, ch).intersects(QRect(x + outerborder + cell->geometry().x(),
       
  7821                                                         y + outerborder + cell->geometry().y(),
       
  7822                                                         cell->geometry().width(),
       
  7823                                                         cell->geometry().height()))) {
       
  7824             cell->draw(p, x+outerborder, y+outerborder, cx, cy, cw, ch, pal, selected);
       
  7825             if (border) {
       
  7826                 QRect r(x+outerborder+cell->geometry().x() - innerborder,
       
  7827                          y+outerborder+cell->geometry().y() - innerborder,
       
  7828                          cell->geometry().width() + 2 * innerborder,
       
  7829                          cell->geometry().height() + 2 * innerborder);
       
  7830                 if (is_printer(p)) {
       
  7831                     QPen oldPen = p->pen();
       
  7832                     QRect r2 = r;
       
  7833                     r2.adjust(innerborder/2, innerborder/2, -innerborder/2, -innerborder/2);
       
  7834                     p->setPen(QPen(pal.text().color(), innerborder));
       
  7835                     p->drawRect(r2);
       
  7836                     p->setPen(oldPen);
       
  7837                 } else {
       
  7838                     int s =  qMax(cellspacing-2*innerborder, 0);
       
  7839                     if (s) {
       
  7840                         p->fillRect(r.left()-s, r.top(), s+1, r.height(), pal.button());
       
  7841                         p->fillRect(r.right(), r.top(), s+1, r.height(), pal.button());
       
  7842                         p->fillRect(r.left()-s, r.top()-s, r.width()+2*s, s, pal.button());
       
  7843                         p->fillRect(r.left()-s, r.bottom(), r.width()+2*s, s, pal.button());
       
  7844                     }
       
  7845                     qDrawShadePanel(p, r, pal, true, innerborder);
       
  7846                 }
       
  7847             }
       
  7848         }
       
  7849     }
       
  7850     if (border) {
       
  7851         QRect r (x, y, width, height);
       
  7852         if (is_printer(p)) {
       
  7853             QRect r2 = r;
       
  7854             r2.adjust(border/2, border/2, -border/2, -border/2);
       
  7855             QPen oldPen = p->pen();
       
  7856             p->setPen(QPen(pal.text().color(), border));
       
  7857             p->drawRect(r2);
       
  7858             p->setPen(oldPen);
       
  7859         } else {
       
  7860             int s = border+qMax(cellspacing-2*innerborder, 0);
       
  7861             if (s) {
       
  7862                 p->fillRect(r.left(), r.top(), s, r.height(), pal.button());
       
  7863                 p->fillRect(r.right()-s, r.top(), s, r.height(), pal.button());
       
  7864                 p->fillRect(r.left(), r.top(), r.width(), s, pal.button());
       
  7865                 p->fillRect(r.left(), r.bottom()-s, r.width(), s, pal.button());
       
  7866             }
       
  7867             qDrawShadePanel(p, r, pal, false, border);
       
  7868         }
       
  7869     }
       
  7870 
       
  7871 }
       
  7872 
       
  7873 int Q3TextTable::minimumWidth() const
       
  7874 {
       
  7875     return qMax(fixwidth, ((layout ? layout->minimumSize().width() : 0) + 2 * outerborder));
       
  7876 }
       
  7877 
       
  7878 void Q3TextTable::resize(int nwidth)
       
  7879 {
       
  7880     if (fixwidth && cachewidth != 0)
       
  7881         return;
       
  7882     if (nwidth == cachewidth)
       
  7883         return;
       
  7884 
       
  7885 
       
  7886     cachewidth = nwidth;
       
  7887     int w = nwidth;
       
  7888 
       
  7889     format(w);
       
  7890 
       
  7891     if (stretch)
       
  7892         nwidth = nwidth * stretch / 100;
       
  7893 
       
  7894     width = nwidth;
       
  7895     layout->invalidate();
       
  7896     int shw = layout->sizeHint().width() + 2*outerborder;
       
  7897     int mw = layout->minimumSize().width() + 2*outerborder;
       
  7898     if (stretch)
       
  7899         width = qMax(mw, nwidth);
       
  7900     else
       
  7901         width = qMax(mw, qMin(nwidth, shw));
       
  7902 
       
  7903     if (fixwidth)
       
  7904         width = fixwidth;
       
  7905 
       
  7906     layout->invalidate();
       
  7907     mw = layout->minimumSize().width() + 2*outerborder;
       
  7908     width = qMax(width, mw);
       
  7909 
       
  7910     int h = layout->heightForWidth(width-2*outerborder);
       
  7911     layout->setGeometry(QRect(0, 0, width-2*outerborder, h) );
       
  7912     height = layout->geometry().height()+2*outerborder;
       
  7913 }
       
  7914 
       
  7915 void Q3TextTable::format(int w)
       
  7916 {
       
  7917     for (int i = 0; i < (int)cells.count(); ++i) {
       
  7918         Q3TextTableCell *cell = cells.at(i);
       
  7919         QRect r = cell->geometry();
       
  7920         r.setWidth(w - 2*outerborder);
       
  7921         cell->setGeometry(r);
       
  7922     }
       
  7923 }
       
  7924 
       
  7925 void Q3TextTable::addCell(Q3TextTableCell* cell)
       
  7926 {
       
  7927     cells.append(cell);
       
  7928     layout->addMultiCell(cell, cell->row(), cell->row() + cell->rowspan()-1,
       
  7929                           cell->column(), cell->column() + cell->colspan()-1);
       
  7930 }
       
  7931 
       
  7932 bool Q3TextTable::enter(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd)
       
  7933 {
       
  7934     currCell.remove(c);
       
  7935     if (!atEnd)
       
  7936         return next(c, doc, parag, idx, ox, oy);
       
  7937     currCell.insert(c, cells.count());
       
  7938     return prev(c, doc, parag, idx, ox, oy);
       
  7939 }
       
  7940 
       
  7941 bool Q3TextTable::enterAt(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &pos)
       
  7942 {
       
  7943     currCell.remove(c);
       
  7944     int lastCell = -1;
       
  7945     int lastY = -1;
       
  7946     int i;
       
  7947     for (i = 0; i < (int)cells.count(); ++i) {
       
  7948         Q3TextTableCell *cell = cells.at(i);
       
  7949         if (!cell)
       
  7950             continue;
       
  7951         QRect r(cell->geometry().x(),
       
  7952                  cell->geometry().y(),
       
  7953                  cell->geometry().width() + 2 * innerborder + 2 * outerborder,
       
  7954                  cell->geometry().height() + 2 * innerborder + 2 * outerborder);
       
  7955 
       
  7956         if (r.left() <= pos.x() && r.right() >= pos.x()) {
       
  7957             if (cell->geometry().y() > lastY) {
       
  7958                 lastCell = i;
       
  7959                 lastY = cell->geometry().y();
       
  7960             }
       
  7961             if (r.top() <= pos.y() && r.bottom() >= pos.y()) {
       
  7962                 currCell.insert(c, i);
       
  7963                 break;
       
  7964             }
       
  7965         }
       
  7966     }
       
  7967     if (i == (int) cells.count())
       
  7968         return false; // no cell found
       
  7969 
       
  7970     if (currCell.find(c) == currCell.end()) {
       
  7971         if (lastY != -1)
       
  7972             currCell.insert(c, lastCell);
       
  7973         else
       
  7974             return false;
       
  7975     }
       
  7976 
       
  7977     Q3TextTableCell *cell = cells.at(*currCell.find(c));
       
  7978     if (!cell)
       
  7979         return false;
       
  7980     doc = cell->richText();
       
  7981     parag = doc->firstParagraph();
       
  7982     idx = 0;
       
  7983     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
       
  7984     oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
       
  7985     return true;
       
  7986 }
       
  7987 
       
  7988 bool Q3TextTable::next(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
       
  7989 {
       
  7990     int cc = -1;
       
  7991     if (currCell.find(c) != currCell.end())
       
  7992         cc = *currCell.find(c);
       
  7993     if (cc > (int)cells.count() - 1 || cc < 0)
       
  7994         cc = -1;
       
  7995     currCell.remove(c);
       
  7996     currCell.insert(c, ++cc);
       
  7997     if (cc >= (int)cells.count()) {
       
  7998         currCell.insert(c, 0);
       
  7999         Q3TextCustomItem::next(c, doc, parag, idx, ox, oy);
       
  8000         Q3TextTableCell *cell = cells.first();
       
  8001         if (!cell)
       
  8002             return false;
       
  8003         doc = cell->richText();
       
  8004         idx = -1;
       
  8005         return true;
       
  8006     }
       
  8007 
       
  8008     if (currCell.find(c) == currCell.end())
       
  8009         return false;
       
  8010     Q3TextTableCell *cell = cells.at(*currCell.find(c));
       
  8011     if (!cell)
       
  8012         return false;
       
  8013     doc = cell->richText();
       
  8014     parag = doc->firstParagraph();
       
  8015     idx = 0;
       
  8016     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
       
  8017     oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
       
  8018     return true;
       
  8019 }
       
  8020 
       
  8021 bool Q3TextTable::prev(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
       
  8022 {
       
  8023     int cc = -1;
       
  8024     if (currCell.find(c) != currCell.end())
       
  8025         cc = *currCell.find(c);
       
  8026     if (cc > (int)cells.count() - 1 || cc < 0)
       
  8027         cc = cells.count();
       
  8028     currCell.remove(c);
       
  8029     currCell.insert(c, --cc);
       
  8030     if (cc < 0) {
       
  8031         currCell.insert(c, 0);
       
  8032         Q3TextCustomItem::prev(c, doc, parag, idx, ox, oy);
       
  8033         Q3TextTableCell *cell = cells.first();
       
  8034         if (!cell)
       
  8035             return false;
       
  8036         doc = cell->richText();
       
  8037         idx = -1;
       
  8038         return true;
       
  8039     }
       
  8040 
       
  8041     if (currCell.find(c) == currCell.end())
       
  8042         return false;
       
  8043     Q3TextTableCell *cell = cells.at(*currCell.find(c));
       
  8044     if (!cell)
       
  8045         return false;
       
  8046     doc = cell->richText();
       
  8047     parag = doc->lastParagraph();
       
  8048     idx = parag->length() - 1;
       
  8049     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
       
  8050     oy += cell->geometry().y()  + cell->verticalAlignmentOffset() + outerborder;
       
  8051     return true;
       
  8052 }
       
  8053 
       
  8054 bool Q3TextTable::down(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
       
  8055 {
       
  8056     if (currCell.find(c) == currCell.end())
       
  8057         return false;
       
  8058     Q3TextTableCell *cell = cells.at(*currCell.find(c));
       
  8059     if (cell->row_ == layout->numRows() - 1) {
       
  8060         currCell.insert(c, 0);
       
  8061         Q3TextCustomItem::down(c, doc, parag, idx, ox, oy);
       
  8062         Q3TextTableCell *cell = cells.first();
       
  8063         if (!cell)
       
  8064             return false;
       
  8065         doc = cell->richText();
       
  8066         idx = -1;
       
  8067         return true;
       
  8068     }
       
  8069 
       
  8070     int oldRow = cell->row_;
       
  8071     int oldCol = cell->col_;
       
  8072     if (currCell.find(c) == currCell.end())
       
  8073         return false;
       
  8074     int cc = *currCell.find(c);
       
  8075     for (int i = cc; i < (int)cells.count(); ++i) {
       
  8076         cell = cells.at(i);
       
  8077         if (cell->row_ > oldRow && cell->col_ == oldCol) {
       
  8078             currCell.insert(c, i);
       
  8079             break;
       
  8080         }
       
  8081     }
       
  8082     doc = cell->richText();
       
  8083     if (!cell)
       
  8084         return false;
       
  8085     parag = doc->firstParagraph();
       
  8086     idx = 0;
       
  8087     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
       
  8088     oy += cell->geometry().y()  + cell->verticalAlignmentOffset() + outerborder;
       
  8089     return true;
       
  8090 }
       
  8091 
       
  8092 bool Q3TextTable::up(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
       
  8093 {
       
  8094     if (currCell.find(c) == currCell.end())
       
  8095         return false;
       
  8096     Q3TextTableCell *cell = cells.at(*currCell.find(c));
       
  8097     if (cell->row_ == 0) {
       
  8098         currCell.insert(c, 0);
       
  8099         Q3TextCustomItem::up(c, doc, parag, idx, ox, oy);
       
  8100         Q3TextTableCell *cell = cells.first();
       
  8101         if (!cell)
       
  8102             return false;
       
  8103         doc = cell->richText();
       
  8104         idx = -1;
       
  8105         return true;
       
  8106     }
       
  8107 
       
  8108     int oldRow = cell->row_;
       
  8109     int oldCol = cell->col_;
       
  8110     if (currCell.find(c) == currCell.end())
       
  8111         return false;
       
  8112     int cc = *currCell.find(c);
       
  8113     for (int i = cc; i >= 0; --i) {
       
  8114         cell = cells.at(i);
       
  8115         if (cell->row_ < oldRow && cell->col_ == oldCol) {
       
  8116             currCell.insert(c, i);
       
  8117             break;
       
  8118         }
       
  8119     }
       
  8120     doc = cell->richText();
       
  8121     if (!cell)
       
  8122         return false;
       
  8123     parag = doc->lastParagraph();
       
  8124     idx = parag->length() - 1;
       
  8125     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
       
  8126     oy += cell->geometry().y()  + cell->verticalAlignmentOffset() + outerborder;
       
  8127     return true;
       
  8128 }
       
  8129 
       
  8130 Q3TextTableCell::Q3TextTableCell(Q3TextTable* table,
       
  8131                                 int row, int column,
       
  8132                                 const QMap<QString, QString> &attr,
       
  8133                                 const Q3StyleSheetItem* style,
       
  8134                                 const Q3TextFormat& fmt, const QString& context,
       
  8135                                 Q3MimeSourceFactory &factory, Q3StyleSheet *sheet,
       
  8136                                 const QString& doc)
       
  8137 {
       
  8138     cached_width = -1;
       
  8139     cached_sizehint = -1;
       
  8140 
       
  8141     maxw = QWIDGETSIZE_MAX;
       
  8142     minw = 0;
       
  8143 
       
  8144     parent = table;
       
  8145     row_ = row;
       
  8146     col_ = column;
       
  8147     stretch_ = 0;
       
  8148     richtext = new Q3TextDocument(table->parent);
       
  8149     richtext->formatCollection()->setPaintDevice(table->parent->formatCollection()->paintDevice());
       
  8150     richtext->bodyText = fmt.color();
       
  8151     richtext->setTableCell(this);
       
  8152 
       
  8153     QMap<QString,QString>::ConstIterator it, end = attr.end();
       
  8154     int halign = style->alignment();
       
  8155     if (halign != Q3StyleSheetItem::Undefined)
       
  8156         richtext->setAlignment(halign);
       
  8157     it = attr.find(QLatin1String("align"));
       
  8158     if (it != end && ! (*it).isEmpty()) {
       
  8159         QString a = (*it).toLower();
       
  8160         if (a == QLatin1String("left"))
       
  8161             richtext->setAlignment(Qt::AlignLeft);
       
  8162         else if (a == QLatin1String("center"))
       
  8163             richtext->setAlignment(Qt::AlignHCenter);
       
  8164         else if (a == QLatin1String("right"))
       
  8165             richtext->setAlignment(Qt::AlignRight);
       
  8166     }
       
  8167     align = 0;
       
  8168     it = attr.find(QLatin1String("valign"));
       
  8169     if (it != end && ! (*it).isEmpty()) {
       
  8170         QString va = (*it).toLower();
       
  8171         if ( va == QLatin1String("top") )
       
  8172 	    align |= Qt::AlignTop;
       
  8173 	else if ( va == QLatin1String("center") || va == QLatin1String("middle") )
       
  8174             align |= Qt::AlignVCenter;
       
  8175         else if (va == QLatin1String("bottom"))
       
  8176             align |= Qt::AlignBottom;
       
  8177     }
       
  8178     richtext->setFormatter(table->parent->formatter());
       
  8179     richtext->setUseFormatCollection(table->parent->useFormatCollection());
       
  8180     richtext->setMimeSourceFactory(&factory);
       
  8181     richtext->setStyleSheet(sheet);
       
  8182     richtext->setRichText(doc, context, &fmt);
       
  8183     rowspan_ = 1;
       
  8184     colspan_ = 1;
       
  8185 
       
  8186     it = attr.find(QLatin1String("colspan"));
       
  8187     if (it != end)
       
  8188         colspan_ = (*it).toInt();
       
  8189     it = attr.find(QLatin1String("rowspan"));
       
  8190     if (it != end)
       
  8191         rowspan_ = (*it).toInt();
       
  8192 
       
  8193     background = 0;
       
  8194     it = attr.find(QLatin1String("bgcolor"));
       
  8195     if (it != end) {
       
  8196         background = new QBrush(QColor(*it));
       
  8197     }
       
  8198 
       
  8199     hasFixedWidth = false;
       
  8200     it = attr.find(QLatin1String("width"));
       
  8201     if (it != end) {
       
  8202         bool b;
       
  8203         QString s(*it);
       
  8204         int w = s.toInt(&b);
       
  8205         if (b) {
       
  8206             maxw = w;
       
  8207             minw = maxw;
       
  8208             hasFixedWidth = true;
       
  8209         } else {
       
  8210             s = s.trimmed();
       
  8211             if (s.length() > 1 && s[(int)s.length()-1] == QLatin1Char('%'))
       
  8212                 stretch_ = s.left(s.length()-1).toInt();
       
  8213         }
       
  8214     }
       
  8215 
       
  8216     attributes = attr;
       
  8217 
       
  8218     parent->addCell(this);
       
  8219 }
       
  8220 
       
  8221 Q3TextTableCell::~Q3TextTableCell()
       
  8222 {
       
  8223     delete background;
       
  8224     background = 0;
       
  8225     delete richtext;
       
  8226     richtext = 0;
       
  8227 }
       
  8228 
       
  8229 QSize Q3TextTableCell::sizeHint() const
       
  8230 {
       
  8231     int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance);
       
  8232     int used = richtext->widthUsed() + extra;
       
  8233 
       
  8234     if  (stretch_) {
       
  8235         int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding;
       
  8236         return QSize(qMin(w, maxw), 0).expandedTo(minimumSize());
       
  8237     }
       
  8238 
       
  8239     return QSize(used, 0).expandedTo(minimumSize());
       
  8240 }
       
  8241 
       
  8242 QSize Q3TextTableCell::minimumSize() const
       
  8243 {
       
  8244     int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance);
       
  8245     return QSize(qMax(richtext->minimumWidth() + extra, minw), 0);
       
  8246 }
       
  8247 
       
  8248 QSize Q3TextTableCell::maximumSize() const
       
  8249 {
       
  8250     return QSize(maxw, QWIDGETSIZE_MAX);
       
  8251 }
       
  8252 
       
  8253 Qt::Orientations Q3TextTableCell::expandingDirections() const
       
  8254 {
       
  8255     return Qt::Horizontal | Qt::Vertical;
       
  8256 }
       
  8257 
       
  8258 bool Q3TextTableCell::isEmpty() const
       
  8259 {
       
  8260     return false;
       
  8261 }
       
  8262 void Q3TextTableCell::setGeometry(const QRect& r)
       
  8263 {
       
  8264     int extra = 2 * (parent->innerborder + parent->cellpadding);
       
  8265     if (r.width() != cached_width)
       
  8266         richtext->doLayout(Q3TextFormat::painter(), r.width() - extra);
       
  8267     cached_width = r.width();
       
  8268     geom = r;
       
  8269 }
       
  8270 
       
  8271 QRect Q3TextTableCell::geometry() const
       
  8272 {
       
  8273     return geom;
       
  8274 }
       
  8275 
       
  8276 bool Q3TextTableCell::hasHeightForWidth() const
       
  8277 {
       
  8278     return true;
       
  8279 }
       
  8280 
       
  8281 int Q3TextTableCell::heightForWidth(int w) const
       
  8282 {
       
  8283     int extra = 2 * (parent->innerborder + parent->cellpadding);
       
  8284     w = qMax(minw, w);
       
  8285 
       
  8286     if (cached_width != w) {
       
  8287         Q3TextTableCell* that = (Q3TextTableCell*) this;
       
  8288         that->richtext->doLayout(Q3TextFormat::painter(), w - extra);
       
  8289         that->cached_width = w;
       
  8290     }
       
  8291     return richtext->height() + extra;
       
  8292 }
       
  8293 
       
  8294 void Q3TextTableCell::adjustToPainter(QPainter* p)
       
  8295 {
       
  8296     Q3TextParagraph *parag = richtext->firstParagraph();
       
  8297     while (parag) {
       
  8298         parag->adjustToPainter(p);
       
  8299         parag = parag->next();
       
  8300     }
       
  8301 }
       
  8302 
       
  8303 int Q3TextTableCell::horizontalAlignmentOffset() const
       
  8304 {
       
  8305     return parent->cellpadding;
       
  8306 }
       
  8307 
       
  8308 int Q3TextTableCell::verticalAlignmentOffset() const
       
  8309 {
       
  8310     if ((align & Qt::AlignVCenter) == Qt::AlignVCenter)
       
  8311         return (geom.height() - richtext->height()) / 2;
       
  8312     else if ((align & Qt::AlignBottom) == Qt::AlignBottom)
       
  8313         return geom.height() - parent->cellpadding - richtext->height()  ;
       
  8314     return parent->cellpadding;
       
  8315 }
       
  8316 
       
  8317 void Q3TextTableCell::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
       
  8318                            const QPalette &pal, bool)
       
  8319 {
       
  8320     if (cached_width != geom.width()) {
       
  8321         int extra = 2 * (parent->innerborder + parent->cellpadding);
       
  8322         richtext->doLayout(p, geom.width() - extra);
       
  8323         cached_width = geom.width();
       
  8324     }
       
  8325     QPalette pal2(pal);
       
  8326     if (background)
       
  8327         pal2.setBrush(QPalette::Base, *background);
       
  8328     else if (richtext->paper())
       
  8329         pal2.setBrush(QPalette::Base, *richtext->paper());
       
  8330 
       
  8331     p->save();
       
  8332     p->translate(x + geom.x(), y + geom.y());
       
  8333     if (background)
       
  8334         p->fillRect(0, 0, geom.width(), geom.height(), *background);
       
  8335     else if (richtext->paper())
       
  8336         p->fillRect(0, 0, geom.width(), geom.height(), *richtext->paper());
       
  8337 
       
  8338     p->translate(horizontalAlignmentOffset(), verticalAlignmentOffset());
       
  8339 
       
  8340     QRegion r;
       
  8341     if (cx >= 0 && cy >= 0)
       
  8342         richtext->draw(p, cx - (x + horizontalAlignmentOffset() + geom.x()),
       
  8343                         cy - (y + geom.y() + verticalAlignmentOffset()),
       
  8344                         cw, ch, pal2, false, false, 0);
       
  8345     else
       
  8346         richtext->draw(p, -1, -1, -1, -1, pal2, false, false, 0);
       
  8347 
       
  8348     p->restore();
       
  8349 }
       
  8350 #endif
       
  8351 
       
  8352 QT_END_NAMESPACE
       
  8353 
       
  8354 #endif //QT_NO_RICHTEXT